Skip to content

Instantly share code, notes, and snippets.

@cab404
Created September 2, 2017 22:09
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 cab404/d787bafb844a457d75fc5e7e6a639b79 to your computer and use it in GitHub Desktop.
Save cab404/d787bafb844a457d75fc5e7e6a639b79 to your computer and use it in GitHub Desktop.
Opens FB2 books from src url parameter. Modified version of https://gist.github.com/Kup3a/1ae3b114b3f07ee35d37
<!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...");
}
}
}
}
function getURLParameter(name) {
return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [null, ''])[1].replace(/\+/g, '%20')) || null;
}
function openUrl(url) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", url, true);
xmlHttp.onload = function () {
var parser = new DOMParser();
var booksrc = parser.parseFromString(
xmlHttp.response, "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";
};
xmlHttp.send( null )
}
window.addEventListener("load", function () {
var toc = [];
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">
<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">
</div>
<script> openUrl(getURLParameter("src")) </script>
</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