Created
September 2, 2017 22:09
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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