Skip to content

Instantly share code, notes, and snippets.

@Kup3a
Last active August 29, 2015 14:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Kup3a/1ae3b114b3f07ee35d37 to your computer and use it in GitHub Desktop.
Save Kup3a/1ae3b114b3f07ee35d37 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html manifest="fiction-reader.appcache">
<head>
<meta charset="utf-8">
<title>FictionReader JS</title>
<meta name="viewport" content="width=device-width">
<!--<link rel="shortcut icon" href="bookonscreen.png">-->
<!--<link rel="apple-touch-icon-precomposed" href="fiction-reader.png">-->
<script type="text/javascript" src="fiction-reader.js"></script>
<script type="text/javascript">
function setup_meta(book) {
document.getElementById("book-title").innerHTML =
book.getBookTitle();
document.getElementById("book-authors").innerHTML =
render_authors(book.getBookAuthors());
document.getElementById("book-annotation").innerHTML = "";
document.getElementById("book-annotation")
.appendChild(book.getBookAnnotation());
var doc_authors = book.getDocumentAuthors();
if (doc_authors.length > 0)
document.getElementById("prepared-by").innerHTML =
"Prepared by<br>\n"
+ render_authors(book.getDocumentAuthors());
var program = book.getProgramUsed();
if (program)
document.getElementById("prepared-with").innerHTML =
"Prepared with<br>\n" + program;
var date = book.getDocumentDate();
if (date)
document.getElementById("prepared-on").innerHTML =
"Prepared on<br>\n" + date;
}
function render_authors(authors) {
var authors_field = "";
for (var i = 0; i < authors.length; i++) {
if (authors[i].nickname.length > 0) {
authors_field += authors[i].nickname + "<br>\n";
} else {
authors_field +=
authors[i].first_name
+ " " + authors[i].middle_name
+ " " + authors[i].last_name + "<br>\n";
}
}
return authors_field;
}
function setup_toc(toc, book) {
document.getElementById("toc").innerHTML = "";
for (var i = 0; i < toc.length; i++) {
var li = document.createElement("li");
document.getElementById("toc").appendChild(li);
var a = document.createElement("a");
a.href = "#content";
a.onclick = toc_handler(toc, i, book);
li.appendChild(a);
var title = FicR.getSectionTitle(toc[i]);
if (!title) title = "(unnamed)";
a.innerHTML = title;
}
}
function setup_notes(notes) {
var notes_content = document.getElementById("notes-content");
for (var i = 0; i < notes.length; i++) {
notes_content.appendChild(FicR.render_section(notes[i]));
}
}
function setup_nav_links(toc, current_section) {
var prev_link = document.getElementById("prev-link");
var next_link = document.getElementById("next-link");
if (current_section <= 0) {
prev_link.style.display = "none";
} else {
prev_link.style.display = "inline";
}
if (current_section >= (toc.length - 1)) {
next_link.style.display = "none";
} else {
next_link.style.display = "inline";
}
}
var current_section = 0;
function toc_handler(toc, section_num, book) {
return function () {
/*history.pushState(
{section: current_section},
FicR.getSectionTitle(toc[current_section]));*/
load_section(toc[section_num], book);
setup_nav_links(toc, section_num);
current_section = section_num;
//return false; // Let the anchor work.
}
}
function load_section(section, book) {
var content = document.getElementById("content");
content.innerHTML = "";
content.appendChild(FicR.render_section(section, book));
var note_links = content.getElementsByTagName("a");
for (var i = 0; i < note_links.length; i++) {
if (note_links[i].className == "note") {
note_links[i].onclick = function () {
document.getElementById("notes-page")
.style.display = "block";
console.log("showing notes...");
}
}
}
}
window.addEventListener("load", function () {
var toc = [];
var input = document.getElementById("local-file");
input.addEventListener("change", function () {
var file = this.files[0];
var reader = new FileReader();
reader.onload = function () {
var parser = new DOMParser();
var booksrc = parser.parseFromString(
reader.result, "application/xml");
var book = new FictionReader(booksrc);
toc = book.getTableOfContents();
setup_meta(book);
setup_toc(toc, book);
setup_notes(book.getNotes());
load_section(toc[0], book);
setup_nav_links(toc, current_section);
document.getElementById("blurb")
.style.display = "none";
document.getElementById("reader")
.style.display = "block";
};
reader.readAsText(file);
}, false);
var prev_link = document.getElementById("prev-link");
prev_link.addEventListener("click", function () {
if (current_section <= 0) {
alert("You're at the beginning of the document.");
} else {
/*history.pushState(
{section: current_section},
FicR.getSectionTitle(toc[current_section]));*/
current_section--;
load_section(toc[current_section]);
setup_nav_links(toc, current_section);
}
}, false);
var next_link = document.getElementById("next-link");
next_link.addEventListener("click", function () {
if (current_section >= (toc.length - 1)) {
alert("You're at the end of the document.");
} else {
/*history.pushState(
{section: current_section},
FicR.getSectionTitle(toc[current_section]));*/
current_section++;
load_section(toc[current_section]);
setup_nav_links(toc, current_section);
}
}, false);
/*window.addEventListener("popstate", function (event) {
if (event.state) {
current_section = event.state.section;
load_section(toc[current_section]);
setup_nav_links(toc, current_section);
}
}, false);*/
var close_notes_link = document.getElementById("close-notes");
close_notes_link.addEventListener("click", function (event) {
document.getElementById("notes-page").style.display = "none";
event.preventDefault();
}, false);
}, false);
</script>
<style type="text/css">
body {
Margin: 0;
Padding: 0;
Color: black;
Background-color: #eeddcc;
Font-family: serif;
}
#file-browser, #notes-page {
Padding: 1ex;
Margin: 1ex;
Background-color: #f0f0f0;
Border: 1px solid black;
Box-shadow: 3px 3px 3px;
}
#title-page {
Text-align: center;
Font-weight: bold;
Width: 70ex;
Max-width: 80%;
Margin: 0 auto;
}
.annotation {
Text-align: left;
Margin: 1em 5ex;
}
.epigraph {
Text-align: left;
Margin: 1em 0;
Margin-left: 40%;
Font-style: italic;
}
#toc-page, #nav-links, #content, #blurb {
Width: 70ex;
Max-width: 80%;
Padding: 1em 5ex;
Margin: 1em auto;
Background-color: #f0f0f0;
Border: 1px solid black;
Box-shadow: 3px 3px 3px;
}
#notes-page {
Overflow: auto;
Position: fixed;
Bottom: 0;
Left: 0;
Right: 0;
Top: 60%;
Display: none;
}
#close-notes {
Position: absolute;
Top: 1ex;
Right: 1ex;
}
#nav-links { Text-align: center; }
#prev-link { Float: left; }
#next-link { Float: right; }
h1, h2, h3 { Font-family: sans-serif; }
#content { Text-align: justify; }
#content table { Text-align: left; }
#reader { Display: none; }
</style>
</head>
<body role="application">
<form id="file-browser">
<label>
Open a file from your computer:
<input type="file" id="local-file">
</label>
Only FictionBook (.FB2) files are supported.
</form>
<div id="reader">
<div id="title-page" role="complementary">
<h1 id="book-title"></h1>
<p id="book-authors"></p>
<div id="book-annotation" class="annotation"></div>
<p id="prepared-by"></p>
<p id="prepared-with"></p>
<p id="prepared-on"></p>
</div>
<div id="toc-page" role="navigation">
<h2>Table of Contents</h2>
<ol id="toc"></ol>
</div>
<div id="content" role="main" aria-live="polite"></div>
<div id="nav-links" role="navigation">
<a href="#content" id="prev-link">Previous</a>
<a href="#content" id="next-link">Next</a>
<a href="#toc-page" title="Table of Contents">ToC</a>
</div>
<div id="notes-page" role="complementary">
<h2>Notes</h2>
<a id="close-notes" href="#">Close notes</a>
<div id="notes-content">
</div>
</div>
</div>
<div id="blurb" role="contentinfo">
<h2>What is this?</h2>
<p>FictionReader JS is an incomplete reader for the FictionBook
format, also known as FB2. FictionBook is an open e-book format
created in Russia, where it's the most popular.</p>
<p>You can find out more about the format at:</p>
<ul>
<li><a href="http://fictionbook.org/index.php/English">the official FictionBook website</a>;</li>
<li><a href="https://en.wikipedia.org/wiki/FictionBook">Wikipedia</a>;</li>
<li><a href="http://wiki.mobileread.com/wiki/FB2">the MobileReads Wiki</a>.</li>
</ul>
<p>Books in this format can be found at:</p>
<ul>
<li><a href="http://manybooks.net/">ManyBooks.net</a> and</li>
<li><a href="http://fictionbook-lib.org/">fictionbook-lib.org</a>.</li>
</ul>
<h2>Status</h2>
<p>As of 2013-04-01, FictionReader JS can load FB2 files off your
computer and display the text in a navigable format. I couldn't get
images to work.</p>
<p>FictionReader JS has been developed with Firefox 17 and tested
in Opera 12. Any compatibility reports would be appreciated.</p>
<h2>License</h2>
<p>FictionReader JS was written by
<a href="http://felix.plesoianu.ro/">Felix Pleșoianu</a>.</p>
<p>Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:</p>
<p>The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.</p>
<p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.</p>
</div>
</body>
</html>
// FictionBook (a.k.a. FB2) reading library
// 2013-03-28 Felix Pleșoianu <felixp7@yahoo.com>
// MIT license -- see the main file for full text.
var FictionReader = function (xml) {
this.xml = xml;
this.toc = null;
this.getBookTitle = function () {
return this.xml.documentElement
.getElementsByTagName("book-title")[0]
.textContent;
}
this.getBookAuthors = function () {
return this.getAuthorsFromElementId("title-info");
}
this.getDocumentAuthors = function () {
return this.getAuthorsFromElementId("document-info");
}
this.getAuthorsFromElementId = function (id) {
var author_data = [];
try {
var authors = this.xml.documentElement
.getElementsByTagName(id)[0]
.getElementsByTagName("author");
for (var i = 0; i < authors.length; i++) {
author_data.push(
FicR.getAuthorData(
authors[i]));
}
} catch (e) {
console.log(e);
}
return author_data;
}
this.getBookAnnotation = function () {
try {
var annotation = this.xml.documentElement
.getElementsByTagName("title-info")[0]
.getElementsByTagName("annotation")[0];
return FicR.render_section(annotation);
} catch (e) {
console.log(e);
return document.createElement("div");
}
}
this.getProgramUsed = function () {
try {
var program = this.xml.documentElement
.getElementsByTagName("document-info")[0]
.getElementsByTagName("program-used")[0];
return program.textContent;
} catch (e) {
console.log(e);
return "";
}
}
this.getDocumentDate = function () {
try {
var date = this.xml.documentElement
.getElementsByTagName("document-info")[0]
.getElementsByTagName("date")[0];
return date.textContent;
} catch (e) {
console.log(e);
return "";
}
}
this.getTableOfContents = function () {
if (this.toc != null) return this.toc;
this.toc = [];
var body = this.xml.documentElement
.getElementsByTagName("body")[0];
var nodes = body.childNodes;
for (var i = 0; i < nodes.length; i++) {
if (!nodes[i].tagName) continue;
if (nodes[i].tagName != "section") continue;
this.toc.push(nodes[i]);
}
return this.toc;
}
this.getNotes = function () {
var bodies = this.xml.documentElement
.getElementsByTagName("body");
var note_blocks = [];
for (var i = 1; i < bodies.length; i++) {
var name = bodies[i].getAttribute("name");
if (name.search("notes") >= 0) {
note_blocks.push(bodies[i]);
}
}
return note_blocks;
}
/*this.getImage = function (id) {
var URL = window.URL = window.URL || window.webkitURL;
var img = document.createElement("img");
try {
var binary = this.getBinaryById(id);
var base64 = binary.textContent.replace(/\s+/gm, "");
img.src = URL.createObjectURL(window.atob(base64));
img.onload = function() {
URL.revokeObjectURL(this.src);
}
} catch (e) {
if (!binary)
console.log("Failed to retrieve element " + id);
else
console.log(e);
}
return img;
}
// Idiotic workaround for the fact that browsers apparently
// don't handle getElementById() for XML.
this.getBinaryById = function (id) {
var binaries = this.xml.documentElement
.getElementsByTagName("binary");
for (var i = 0; i < binaries.length; i++) {
if (binaries[i].getAttribute("id") == id)
return binaries[i];
}
return null;
}*/
};
var FicR = {};
FicR.getSectionTitle = function (section) {
var tmp = section.getElementsByTagName("title")[0];
if (!tmp) return null;
return tmp.textContent;
};
FicR.getAuthorData = function (author) {
var first_name = author.getElementsByTagName("first-name")[0];
var middle_name = author.getElementsByTagName("middle-name")[0];
var last_name = author.getElementsByTagName("last-name")[0];
var nickname = author.getElementsByTagName("nickname")[0];
first_name = first_name ? first_name.textContent : "";
middle_name = middle_name ? middle_name.textContent : "";
last_name = last_name ? last_name.textContent : "";
nickname = nickname ? nickname.textContent : "";
return {
first_name: first_name,
middle_name: middle_name,
last_name: last_name,
nickname: nickname
};
}
FicR.render_section = function (section, book) {
var s;
if (section.tagName == "section")
s = document.createElement("section");
else if (section.tagName == "cite")
s = document.createElement("blockquote");
else if (section.tagName == "td")
s = document.createElement("td");
else
s = document.createElement("div");
if (section.tagName == "annotation")
s.className = "annotation";
else if (section.tagName == "epigraph")
s.className = "epigraph";
var nodes = section.childNodes;
for (var i = 0; i < nodes.length; i++) {
if (!nodes[i].tagName) continue;
if (nodes[i].tagName == "p") {
s.appendChild(FicR.render_para(nodes[i], book));
} else if (nodes[i].tagName == "empty-line") {
s.appendChild(document.createElement("hr"));
} else if (nodes[i].tagName == "subtitle") {
var h3 = document.createElement("h3");
h3.innerHTML = nodes[i].textContent;
s.appendChild(h3);
if (nodes[i].hasAttribute("id"))
h3.id = nodes[i].getAttribute("id");
} else if (nodes[i].tagName == "section") {
s.appendChild(FicR.render_section(nodes[i]));
} else if (nodes[i].tagName == "cite") {
s.appendChild(FicR.render_section(nodes[i]));
} else if (nodes[i].tagName == "poem") {
s.appendChild(FicR.render_poem(nodes[i]));
} else if (nodes[i].tagName == "title") {
var title = document.createElement("h2");
s.appendChild(title);
title.innerHTML = nodes[i].textContent;
} else if (nodes[i].tagName == "annotation") {
s.appendChild(FicR.render_section(nodes[i]));
} else if (nodes[i].tagName == "epigraph") {
s.appendChild(FicR.render_section(nodes[i]));
} else if (nodes[i].tagName == "table") {
s.appendChild(FicR.render_table(nodes[i]));
} else {
s.appendChild(
document.createTextNode(
nodes[i].textContent));
}
}
return s;
};
FicR.render_para = function (para, book) {
var p = document.createElement("p");
var nodes = para.childNodes;
for (var i = 0; i < nodes.length; i++) {
if (nodes[i].nodeType == 3) {
p.appendChild(nodes[i].cloneNode());
} else if (nodes[i].nodeType != 1) {
continue;
} else if (nodes[i].tagName == "emphasis") {
var em = document.createElement("em");
em.innerHTML = nodes[i].textContent;
p.appendChild(em);
} else if (nodes[i].tagName == "strong") {
var strong = document.createElement("strong");
strong.innerHTML = nodes[i].textContent;
p.appendChild(strong);
/*} else if (nodes[i].tagName == "image") {
var imgid =
nodes[i].getAttribute("xlink:href").slice(1);
p.appendChild(book.getImage(imgid));*/
} else if (nodes[i].tagName == "a") {
var a = document.createElement("a");
a.innerHTML = nodes[i].textContent;
a.href = nodes[i].getAttribute("xlink:href");
a.className = "note";
p.appendChild(a);
} else {
p.appendChild(
document.createTextNode(
nodes[i].textContent));
}
}
return p;
};
FicR.render_poem = function (section) {
var poem = document.createElement("div");
poem.className = "poem";
var stanzas = section.childNodes;
for (var i = 0; i < stanzas.length; i++) {
if (!stanzas[i].tagName) continue;
if (stanzas[i].tagName != "stanza") continue;
var stanza = document.createElement("p");
var verses = stanzas[i].childNodes;
for (var j = 0; j < verses.length; j++) {
if (!verses[j].tagName) continue;
if (verses[j].tagName != "v") continue;
stanza.appendChild(
document.createTextNode(
verses[j].textContent));
stanza.appendChild(document.createElement("br"));
}
poem.appendChild(stanza);
}
return poem;
};
FicR.render_table = function (section) {
var table = document.createElement("table");
var rows = section.childNodes;
for (var i = 0; i < rows.length; i++) {
if (!rows[i].tagName) continue;
if (rows[i].tagName != "tr") continue;
var row = document.createElement("tr");
var cells = rows[i].childNodes;
for (var j = 0; j < cells.length; j++) {
if (!cells[j].tagName) continue;
if (cells[j].tagName != "td") continue;
row.appendChild(FicR.render_section(cells[j]));
}
table.appendChild(row);
}
return table;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment