mirror of
https://github.com/futurepress/epub.js.git
synced 2025-10-04 15:09:16 +02:00
commented code and cleaned up
This commit is contained in:
parent
3c151efdb5
commit
2ddae3a024
7 changed files with 223 additions and 315 deletions
|
@ -46,6 +46,10 @@ body {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#title-seperator {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
#area {
|
#area {
|
||||||
width: 80%;
|
width: 80%;
|
||||||
height: 80%;
|
height: 80%;
|
||||||
|
|
|
@ -23,17 +23,23 @@ FPR.app.init = (function($){
|
||||||
}else{
|
}else{
|
||||||
$("#main").width(windowWidth);
|
$("#main").width(windowWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-- Create a new book object,
|
||||||
|
// this will create an iframe in the el with the ID provided
|
||||||
Book = new FP.Book("area");
|
Book = new FP.Book("area");
|
||||||
|
|
||||||
|
//-- Add listeners to handle book events
|
||||||
|
//-- Full list of event are at start of book.js
|
||||||
Book.listen("book:metadataReady", meta);
|
Book.listen("book:metadataReady", meta);
|
||||||
Book.listen("book:tocReady", toc);
|
Book.listen("book:tocReady", toc);
|
||||||
Book.listen("book:chapterReady", chapterChange);
|
Book.listen("book:chapterReady", chapterChange);
|
||||||
Book.listen("book:online", goOnline);
|
Book.listen("book:online", goOnline);
|
||||||
Book.listen("book:offline", goOffline);
|
Book.listen("book:offline", goOffline);
|
||||||
|
|
||||||
|
//-- Start loading / parsing of the book.
|
||||||
Book.start(bookURL + "/");
|
// This must be done AFTER adding listeners or hooks
|
||||||
|
Book.start(bookURL);
|
||||||
|
|
||||||
//-- Wait for Dom ready to handle jquery
|
//-- Wait for Dom ready to handle jquery
|
||||||
$(function() {
|
$(function() {
|
||||||
|
@ -47,24 +53,44 @@ FPR.app.init = (function($){
|
||||||
var title = Book.getTitle(),
|
var title = Book.getTitle(),
|
||||||
author = Book.getCreator(),
|
author = Book.getCreator(),
|
||||||
$title = $("#book-title"),
|
$title = $("#book-title"),
|
||||||
$author = $("#chapter-title");
|
$author = $("#chapter-title"),
|
||||||
|
$dash = $("#title-seperator");
|
||||||
|
|
||||||
document.title = title+" – "+author;
|
document.title = title+" – "+author;
|
||||||
$title.html(title);
|
$title.html(title);
|
||||||
$author.html(author);
|
$author.html(author);
|
||||||
|
$dash.show();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toc(){
|
function toc(){
|
||||||
var contents = Book.getTOC(),
|
var contents = Book.getTOC(),
|
||||||
$toc = $("#toc"),
|
$toc = $("#toc"),
|
||||||
|
$links,
|
||||||
$items;
|
$items;
|
||||||
|
|
||||||
$toc.empty();
|
$toc.empty();
|
||||||
|
|
||||||
|
//-- Recursively generate TOC levels
|
||||||
$items = generateTocItems(contents);
|
$items = generateTocItems(contents);
|
||||||
|
|
||||||
$toc.append($items);
|
$toc.append($items);
|
||||||
|
|
||||||
|
$links = $(".toc_link");
|
||||||
|
|
||||||
|
$links.on("click", function(e){
|
||||||
|
var $this = $(this),
|
||||||
|
url = $this.data("url");
|
||||||
|
|
||||||
|
$(".openChapter").removeClass("openChapter");
|
||||||
|
$this.parent().addClass("openChapter");
|
||||||
|
|
||||||
|
//-- Provide the Book with the url to show
|
||||||
|
// The Url must be found in the books manifest
|
||||||
|
Book.show(url);
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,22 +101,8 @@ FPR.app.init = (function($){
|
||||||
contents.forEach(function(item){
|
contents.forEach(function(item){
|
||||||
var $subitems,
|
var $subitems,
|
||||||
$wrapper = $("<li id='toc-"+item.id+"'>"),
|
$wrapper = $("<li id='toc-"+item.id+"'>"),
|
||||||
$item = $("<a href='#"+item.href+"' data-url='"+item.href+"'>"+item.label+"</a>");
|
$item = $("<a class='toc_link' href='#"+item.href+"' data-url='"+item.href+"'>"+item.label+"</a>");
|
||||||
|
|
||||||
$item.on("click", function(e){
|
|
||||||
var $this = $(this),
|
|
||||||
url = $this.data("url");
|
|
||||||
//spinepos = $this.data("spinepos"),
|
|
||||||
//section = $this.data("section") || false;
|
|
||||||
|
|
||||||
$(".openChapter").removeClass("openChapter");
|
|
||||||
$this.parent().addClass("openChapter");
|
|
||||||
|
|
||||||
Book.show(url);
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
});
|
|
||||||
|
|
||||||
$wrapper.append($item);
|
$wrapper.append($item);
|
||||||
|
|
||||||
if(item.subitems && item.subitems.length){
|
if(item.subitems && item.subitems.length){
|
||||||
|
@ -150,15 +162,15 @@ FPR.app.init = (function($){
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$next.on("click swipeleft", function(){
|
$next.on("click", function(){
|
||||||
Book.nextPage();
|
Book.nextPage();
|
||||||
});
|
});
|
||||||
|
|
||||||
$prev.on("click swiperight", function(){
|
$prev.on("click", function(){
|
||||||
Book.prevPage();
|
Book.prevPage();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//-- TODO: This doesn't seem to work
|
||||||
$window.bind("touchy-swipe", function(event, phase, $target, data){
|
$window.bind("touchy-swipe", function(event, phase, $target, data){
|
||||||
|
|
||||||
if(data.direction = "left"){
|
if(data.direction = "left"){
|
||||||
|
@ -221,8 +233,7 @@ FPR.app.init = (function($){
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$network.on("click", function(){
|
$network.on("click", function(){
|
||||||
offline = !offline;
|
offline = !offline;
|
||||||
|
|
|
@ -9,6 +9,7 @@ FP.Book = function(elem, bookUrl){
|
||||||
|
|
||||||
this.events = new FP.Events(this, this.el);
|
this.events = new FP.Events(this, this.el);
|
||||||
|
|
||||||
|
//-- All Book events for listening
|
||||||
this.createEvent("book:tocReady");
|
this.createEvent("book:tocReady");
|
||||||
this.createEvent("book:metadataReady");
|
this.createEvent("book:metadataReady");
|
||||||
this.createEvent("book:spineReady");
|
this.createEvent("book:spineReady");
|
||||||
|
@ -20,6 +21,7 @@ FP.Book = function(elem, bookUrl){
|
||||||
this.createEvent("book:online");
|
this.createEvent("book:online");
|
||||||
this.createEvent("book:offline");
|
this.createEvent("book:offline");
|
||||||
|
|
||||||
|
//-- All hooks to add functions (with a callback) to
|
||||||
this.hooks = {
|
this.hooks = {
|
||||||
"beforeChapterDisplay" : []
|
"beforeChapterDisplay" : []
|
||||||
};
|
};
|
||||||
|
@ -53,6 +55,7 @@ FP.Book.prototype.initialize = function(el){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-- Listeners for browser events
|
||||||
FP.Book.prototype.listeners = function(){
|
FP.Book.prototype.listeners = function(){
|
||||||
var that = this;
|
var that = this;
|
||||||
window.addEventListener("resize", that.onResized.bind(this), false);
|
window.addEventListener("resize", that.onResized.bind(this), false);
|
||||||
|
@ -67,22 +70,20 @@ FP.Book.prototype.listeners = function(){
|
||||||
that.tell("book:online");
|
that.tell("book:online");
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
//-- TODO: listener for offline
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-- Check bookUrl and start parsing book Assets or load them from storage
|
||||||
FP.Book.prototype.start = function(bookUrl){
|
FP.Book.prototype.start = function(bookUrl){
|
||||||
var pathname = window.location.pathname,
|
var pathname = window.location.pathname,
|
||||||
folder = (pathname[pathname.length - 1] == "/") ? pathname : "/";
|
folder = (pathname[pathname.length - 1] == "/") ? pathname : "/";
|
||||||
|
|
||||||
this.bookUrl = bookUrl;
|
this.bookUrl = (bookUrl[bookUrl.length - 1] == "/") ? bookUrl : bookUrl + "/";
|
||||||
|
|
||||||
if(this.bookUrl.search("://") == -1){
|
if(this.bookUrl.search("://") == -1){
|
||||||
//-- get full path
|
//-- get full path
|
||||||
this.bookUrl = window.location.origin + folder + this.bookUrl;
|
this.bookUrl = window.location.origin + folder + this.bookUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-- TODO: Check what storage types are available
|
|
||||||
//-- TODO: Checks if the url is a zip file and unpack
|
//-- TODO: Checks if the url is a zip file and unpack
|
||||||
if(this.isContained(bookUrl)){
|
if(this.isContained(bookUrl)){
|
||||||
console.error("Zipped!");
|
console.error("Zipped!");
|
||||||
|
@ -93,10 +94,11 @@ FP.Book.prototype.start = function(bookUrl){
|
||||||
this.parseContainer(function(){
|
this.parseContainer(function(){
|
||||||
//-- Gets all setup of the book from xml file
|
//-- Gets all setup of the book from xml file
|
||||||
//-- TODO: add promise for this instead of callback?
|
//-- TODO: add promise for this instead of callback?
|
||||||
this.parseContents();
|
//this.parseContents();
|
||||||
});
|
});
|
||||||
|
|
||||||
}else{
|
}else{
|
||||||
|
//-- Events for elements loaded from storage
|
||||||
this.tell("book:tocReady");
|
this.tell("book:tocReady");
|
||||||
this.tell("book:metadataReady");
|
this.tell("book:metadataReady");
|
||||||
this.tell("book:spineReady");
|
this.tell("book:spineReady");
|
||||||
|
@ -108,7 +110,7 @@ FP.Book.prototype.start = function(bookUrl){
|
||||||
}
|
}
|
||||||
|
|
||||||
FP.Book.prototype.isSaved = function(force) {
|
FP.Book.prototype.isSaved = function(force) {
|
||||||
|
//-- If url or version has changed invalidate stored data and reset
|
||||||
if (localStorage.getItem("bookUrl") != this.bookUrl ||
|
if (localStorage.getItem("bookUrl") != this.bookUrl ||
|
||||||
localStorage.getItem("fpjs-version") != FP.VERSION ||
|
localStorage.getItem("fpjs-version") != FP.VERSION ||
|
||||||
force == true) {
|
force == true) {
|
||||||
|
@ -138,7 +140,8 @@ FP.Book.prototype.isSaved = function(force) {
|
||||||
this.spine = JSON.parse(localStorage.getItem("spine"));
|
this.spine = JSON.parse(localStorage.getItem("spine"));
|
||||||
this.spineIndexByURL = JSON.parse(localStorage.getItem("spineIndexByURL"));
|
this.spineIndexByURL = JSON.parse(localStorage.getItem("spineIndexByURL"));
|
||||||
this.toc = JSON.parse(localStorage.getItem("toc"));
|
this.toc = JSON.parse(localStorage.getItem("toc"));
|
||||||
|
|
||||||
|
//-- Check that retrieved object aren't null
|
||||||
if(!this.assets || !this.spine || !this.spineIndexByURL || !this.toc){
|
if(!this.assets || !this.spine || !this.spineIndexByURL || !this.toc){
|
||||||
this.stored = 0;
|
this.stored = 0;
|
||||||
return false;
|
return false;
|
||||||
|
@ -184,7 +187,7 @@ FP.Book.prototype.resizeIframe = function(e, cWidth, cHeight){
|
||||||
this.iframe.height = height;
|
this.iframe.height = height;
|
||||||
|
|
||||||
if(width % 2 != 0){
|
if(width % 2 != 0){
|
||||||
width += 1;
|
width += 1; //-- Prevent cutting off edges of text in columns
|
||||||
}
|
}
|
||||||
|
|
||||||
this.iframe.width = width;
|
this.iframe.width = width;
|
||||||
|
@ -199,9 +202,6 @@ FP.Book.prototype.parseContainer = function(callback){
|
||||||
//-- <rootfile full-path="OPS/package.opf" media-type="application/oebps-package+xml"/>
|
//-- <rootfile full-path="OPS/package.opf" media-type="application/oebps-package+xml"/>
|
||||||
rootfile = container.querySelector("rootfile");
|
rootfile = container.querySelector("rootfile");
|
||||||
|
|
||||||
//-- Should only be one
|
|
||||||
//rootfile = rootfiles[0];
|
|
||||||
|
|
||||||
fullpath = rootfile.getAttribute('full-path').split("/");
|
fullpath = rootfile.getAttribute('full-path').split("/");
|
||||||
|
|
||||||
that.basePath = that.bookUrl + fullpath[0] + "/";
|
that.basePath = that.bookUrl + fullpath[0] + "/";
|
||||||
|
@ -211,7 +211,7 @@ FP.Book.prototype.parseContainer = function(callback){
|
||||||
localStorage.setItem("contentsPath", that.contentsPath);
|
localStorage.setItem("contentsPath", that.contentsPath);
|
||||||
|
|
||||||
//-- Now that we have the path we can parse the contents
|
//-- Now that we have the path we can parse the contents
|
||||||
//-- TODO: move this
|
//-- TODO: move this and handle errors
|
||||||
that.parseContents(that.contentsPath);
|
that.parseContents(that.contentsPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -244,6 +244,8 @@ FP.Book.prototype.parseMetadata = function(metadata){
|
||||||
this.metadata["bookTitle"] = title ? title.childNodes[0].nodeValue : "";
|
this.metadata["bookTitle"] = title ? title.childNodes[0].nodeValue : "";
|
||||||
this.metadata["creator"] = creator ? creator.childNodes[0].nodeValue : "";
|
this.metadata["creator"] = creator ? creator.childNodes[0].nodeValue : "";
|
||||||
|
|
||||||
|
//-- TODO: add more meta data items, such as ISBN
|
||||||
|
|
||||||
localStorage.setItem("metadata", JSON.stringify(this.metadata));
|
localStorage.setItem("metadata", JSON.stringify(this.metadata));
|
||||||
|
|
||||||
this.tell("book:metadataReady");
|
this.tell("book:metadataReady");
|
||||||
|
@ -259,7 +261,7 @@ FP.Book.prototype.parseManifest = function(manifest){
|
||||||
items.forEach(function(item){
|
items.forEach(function(item){
|
||||||
var id = item.getAttribute('id'),
|
var id = item.getAttribute('id'),
|
||||||
href = item.getAttribute('href');
|
href = item.getAttribute('href');
|
||||||
that.assets[id] = that.basePath + href;
|
that.assets[id] = that.basePath + href; //-- Absolute URL for loading with a web worker
|
||||||
|
|
||||||
//-- Find NCX: media-type="application/x-dtbncx+xml" href="toc.ncx"
|
//-- Find NCX: media-type="application/x-dtbncx+xml" href="toc.ncx"
|
||||||
if(item.getAttribute('media-type') == "application/x-dtbncx+xml"){
|
if(item.getAttribute('media-type') == "application/x-dtbncx+xml"){
|
||||||
|
@ -328,11 +330,8 @@ FP.Book.prototype.parseTOC = function(path){
|
||||||
items.forEach(function(item){
|
items.forEach(function(item){
|
||||||
var id = item.getAttribute('id'),
|
var id = item.getAttribute('id'),
|
||||||
content = item.querySelector("content"),
|
content = item.querySelector("content"),
|
||||||
src = content.getAttribute('src'), //that.assets[id],
|
src = content.getAttribute('src'),
|
||||||
split = src.split("#"),
|
split = src.split("#"),
|
||||||
//href = that.basePath + split[0],
|
|
||||||
//hash = split[1] || false,
|
|
||||||
//spinePos = parseInt(that.spineIndexByID[id] || that.spineIndexByURL[href]),
|
|
||||||
navLabel = item.querySelector("navLabel"),
|
navLabel = item.querySelector("navLabel"),
|
||||||
text = navLabel.textContent ? navLabel.textContent : "",
|
text = navLabel.textContent ? navLabel.textContent : "",
|
||||||
subitems = item.querySelectorAll("navPoint") || false,
|
subitems = item.querySelectorAll("navPoint") || false,
|
||||||
|
@ -349,8 +348,6 @@ FP.Book.prototype.parseTOC = function(path){
|
||||||
"id": id,
|
"id": id,
|
||||||
"href": src,
|
"href": src,
|
||||||
"label": text,
|
"label": text,
|
||||||
//"spinePos": spinePos,
|
|
||||||
//"section" : hash || false,
|
|
||||||
"subitems" : subs || false
|
"subitems" : subs || false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -395,10 +392,10 @@ FP.Book.prototype.chapterTitle = function(){
|
||||||
|
|
||||||
FP.Book.prototype.startDisplay = function(){
|
FP.Book.prototype.startDisplay = function(){
|
||||||
|
|
||||||
|
|
||||||
this.tell("book:bookReady");
|
this.tell("book:bookReady");
|
||||||
this.displayChapter(this.spinePos, function(chapter){
|
this.displayChapter(this.spinePos, function(chapter){
|
||||||
|
|
||||||
|
//-- If there is network connection, store the books contents
|
||||||
if(this.online){
|
if(this.online){
|
||||||
this.storeOffline();
|
this.storeOffline();
|
||||||
}
|
}
|
||||||
|
@ -414,18 +411,22 @@ FP.Book.prototype.show = function(url){
|
||||||
section = split[1] || false,
|
section = split[1] || false,
|
||||||
absoluteURL = (chapter.search("://") == -1) ? this.basePath + chapter : chapter,
|
absoluteURL = (chapter.search("://") == -1) ? this.basePath + chapter : chapter,
|
||||||
spinePos = this.spineIndexByURL[absoluteURL];
|
spinePos = this.spineIndexByURL[absoluteURL];
|
||||||
|
|
||||||
|
//-- If link fragment only stay on current chapter
|
||||||
if(!chapter){
|
if(!chapter){
|
||||||
spinePos = this.spinePos;
|
spinePos = this.spinePos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-- Check that URL is present in the index, or stop
|
||||||
if(typeof(spinePos) != "number") return false;
|
if(typeof(spinePos) != "number") return false;
|
||||||
|
|
||||||
if(spinePos != this.spinePos){
|
if(spinePos != this.spinePos){
|
||||||
|
//-- Load new chapter if different than current
|
||||||
this.displayChapter(spinePos, function(chap){
|
this.displayChapter(spinePos, function(chap){
|
||||||
if(section) chap.section(section);
|
if(section) chap.section(section);
|
||||||
});
|
});
|
||||||
}else{
|
}else{
|
||||||
|
//-- Only goto section
|
||||||
if(section) this.currentChapter.section(section);
|
if(section) this.currentChapter.section(section);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -452,16 +453,12 @@ FP.Book.prototype.displayChapter = function(pos, callback){
|
||||||
|
|
||||||
|
|
||||||
this.currentChapter.afterLoaded = function(chapter) {
|
this.currentChapter.afterLoaded = function(chapter) {
|
||||||
//-- TODO: Choose between single and spread
|
|
||||||
//that.formatSpread();
|
|
||||||
|
|
||||||
that.tell("book:chapterReady", chapter.getID());
|
that.tell("book:chapterReady", chapter.getID());
|
||||||
|
|
||||||
if(callback){
|
if(callback){
|
||||||
callback(chapter);
|
callback(chapter);
|
||||||
}
|
}
|
||||||
//that.preloadNextChapter();
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -501,16 +498,19 @@ FP.Book.prototype.getTOC = function() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: Remove, replace by batch queue
|
||||||
FP.Book.prototype.preloadNextChapter = function() {
|
FP.Book.prototype.preloadNextChapter = function() {
|
||||||
var next = this.spinePos + 1,
|
var next = this.spinePos + 1,
|
||||||
path = this.spine[next].href;
|
path = this.spine[next].href;
|
||||||
|
|
||||||
file = FP.storage.preload(path);
|
file = FP.storage.preload(path);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
FP.Book.prototype.storeOffline = function(callback) {
|
FP.Book.prototype.storeOffline = function(callback) {
|
||||||
var assets = FP.core.toArray(this.assets);
|
var assets = FP.core.toArray(this.assets);
|
||||||
|
|
||||||
|
//-- Creates a queue of all items to load
|
||||||
FP.storage.batch(assets, function(){
|
FP.storage.batch(assets, function(){
|
||||||
this.stored = 1;
|
this.stored = 1;
|
||||||
localStorage.setItem("stored", 1);
|
localStorage.setItem("stored", 1);
|
||||||
|
@ -530,13 +530,14 @@ FP.Book.prototype.fromStorage = function(stored) {
|
||||||
this.tell("book:online");
|
this.tell("book:online");
|
||||||
}else{
|
}else{
|
||||||
if(!this.availableOffline){
|
if(!this.availableOffline){
|
||||||
|
//-- If book hasn't been cached yet, store offline
|
||||||
this.storeOffline(function(){
|
this.storeOffline(function(){
|
||||||
this.online = false;
|
this.online = false;
|
||||||
this.tell("book:offline");
|
this.tell("book:offline");
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
}else{
|
}else{
|
||||||
this.online = false;
|
this.online = false;
|
||||||
console.log("gone offline")
|
|
||||||
this.tell("book:offline");
|
this.tell("book:offline");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -545,7 +546,7 @@ FP.Book.prototype.fromStorage = function(stored) {
|
||||||
|
|
||||||
FP.Book.prototype.determineStorageMethod = function(override) {
|
FP.Book.prototype.determineStorageMethod = function(override) {
|
||||||
var method = 'ram';
|
var method = 'ram';
|
||||||
// options: none | ram | websql | indexedDB | filesystem
|
|
||||||
if(override){
|
if(override){
|
||||||
method = override;
|
method = override;
|
||||||
}else{
|
}else{
|
||||||
|
@ -557,24 +558,8 @@ FP.Book.prototype.determineStorageMethod = function(override) {
|
||||||
FP.storage.storageMethod(method);
|
FP.storage.storageMethod(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
FP.Book.prototype.triggerHooks = function(type, callback){
|
//-- Hooks allow for injecting async functions that must all complete before continuing
|
||||||
var hooks, count;
|
// Functions must have a callback as their first argument.
|
||||||
|
|
||||||
if(typeof(this.hooks[type]) == "undefined") return false;
|
|
||||||
|
|
||||||
hooks = this.hooks[type];
|
|
||||||
count = hooks.length;
|
|
||||||
|
|
||||||
function countdown(){
|
|
||||||
count--;
|
|
||||||
if(count <= 0 && callback) callback();
|
|
||||||
};
|
|
||||||
|
|
||||||
hooks.forEach(function(hook){
|
|
||||||
hook(countdown);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
FP.Book.prototype.registerHook = function(type, toAdd){
|
FP.Book.prototype.registerHook = function(type, toAdd){
|
||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
|
@ -590,6 +575,25 @@ FP.Book.prototype.registerHook = function(type, toAdd){
|
||||||
|
|
||||||
|
|
||||||
}else{
|
}else{
|
||||||
this.hooks[type] = [func]; //or maybe this should error?
|
//-- Allows for undefined hooks, but maybe this should error?
|
||||||
|
this.hooks[type] = [func];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FP.Book.prototype.triggerHooks = function(type, callback){
|
||||||
|
var hooks, count;
|
||||||
|
|
||||||
|
if(typeof(this.hooks[type]) == "undefined") return false;
|
||||||
|
|
||||||
|
hooks = this.hooks[type];
|
||||||
|
count = hooks.length;
|
||||||
|
|
||||||
|
function countdown(){
|
||||||
|
count--;
|
||||||
|
if(count <= 0 && callback) callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
hooks.forEach(function(hook){
|
||||||
|
hook(countdown);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -39,19 +39,15 @@ FP.Chapter.prototype.loadFromStorage = function(path){
|
||||||
FP.Chapter.prototype.setIframeSrc = function(url){
|
FP.Chapter.prototype.setIframeSrc = function(url){
|
||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
//-- Not sure if this is the best time to do this, but hide current text
|
//-- Not sure if this is the best time to do this, but hides current text
|
||||||
//if(this.bodyEl) this.bodyEl.style.visibility = "hidden";
|
|
||||||
this.visible(false);
|
this.visible(false);
|
||||||
|
|
||||||
this.iframe.src = url;
|
this.iframe.src = url;
|
||||||
|
|
||||||
this.iframe.onload = function() {
|
this.iframe.onload = function() {
|
||||||
//that.bodyEl = that.iframe.contentDocument.documentElement.getElementsByTagName('body')[0];
|
|
||||||
//that.bodyEl = that.iframe.contentDocument.querySelector('body, html');
|
|
||||||
that.doc = that.iframe.contentDocument;
|
that.doc = that.iframe.contentDocument;
|
||||||
that.bodyEl = that.doc.body;
|
that.bodyEl = that.doc.body;
|
||||||
|
|
||||||
//-- TODO: Choose between single and spread
|
|
||||||
that.formatSpread();
|
that.formatSpread();
|
||||||
|
|
||||||
that.afterLoaded(that);
|
that.afterLoaded(that);
|
||||||
|
@ -62,7 +58,7 @@ FP.Chapter.prototype.setIframeSrc = function(url){
|
||||||
}
|
}
|
||||||
|
|
||||||
FP.Chapter.prototype.afterLoaded = function(chapter){
|
FP.Chapter.prototype.afterLoaded = function(chapter){
|
||||||
console.log("afterLoaded")
|
//-- This is overwritten by the book object
|
||||||
}
|
}
|
||||||
|
|
||||||
FP.Chapter.prototype.error = function(err){
|
FP.Chapter.prototype.error = function(err){
|
||||||
|
@ -77,7 +73,6 @@ FP.Chapter.prototype.formatSpread = function(){
|
||||||
this.OldcolWidth = this.colWidth;
|
this.OldcolWidth = this.colWidth;
|
||||||
this.OldspreadWidth = this.spreadWidth;
|
this.OldspreadWidth = this.spreadWidth;
|
||||||
}
|
}
|
||||||
//this.bodyEl.setAttribute("style", "background: #777");
|
|
||||||
|
|
||||||
//-- Check the width and decied on columns
|
//-- Check the width and decied on columns
|
||||||
//-- Todo: a better place for this?
|
//-- Todo: a better place for this?
|
||||||
|
@ -86,11 +81,14 @@ FP.Chapter.prototype.formatSpread = function(){
|
||||||
this.gap = this.gap || Math.ceil(this.elWidth / 8);
|
this.gap = this.gap || Math.ceil(this.elWidth / 8);
|
||||||
|
|
||||||
if(this.elWidth < cutoff) {
|
if(this.elWidth < cutoff) {
|
||||||
|
this.spread = false; //-- Single Page
|
||||||
|
|
||||||
divisor = 1;
|
divisor = 1;
|
||||||
this.colWidth = Math.floor(this.elWidth / divisor);
|
this.colWidth = Math.floor(this.elWidth / divisor);
|
||||||
}else{
|
}else{
|
||||||
this.colWidth = Math.floor((this.elWidth - this.gap) / divisor);
|
this.spread = true; //-- Double Page
|
||||||
|
|
||||||
|
this.colWidth = Math.floor((this.elWidth - this.gap) / divisor);
|
||||||
//-- Must be even for firefox
|
//-- Must be even for firefox
|
||||||
if(this.colWidth % 2 != 0){
|
if(this.colWidth % 2 != 0){
|
||||||
this.colWidth -= 1;
|
this.colWidth -= 1;
|
||||||
|
@ -109,25 +107,14 @@ FP.Chapter.prototype.formatSpread = function(){
|
||||||
//-- Adjust height
|
//-- Adjust height
|
||||||
this.bodyEl.style.height = this.book.el.clientHeight + "px";
|
this.bodyEl.style.height = this.book.el.clientHeight + "px";
|
||||||
|
|
||||||
|
//-- Add columns
|
||||||
|
|
||||||
this.bodyEl.style[FP.core.columnAxis] = "horizontal";
|
this.bodyEl.style[FP.core.columnAxis] = "horizontal";
|
||||||
this.bodyEl.style[FP.core.columnGap] = this.gap+"px";
|
this.bodyEl.style[FP.core.columnGap] = this.gap+"px";
|
||||||
this.bodyEl.style[FP.core.columnWidth] = this.colWidth+"px";
|
this.bodyEl.style[FP.core.columnWidth] = this.colWidth+"px";
|
||||||
|
|
||||||
this.calcPages();
|
this.calcPages();
|
||||||
|
|
||||||
|
//-- Trigger registered hooks before displaying
|
||||||
|
|
||||||
// if(!this.book.online){
|
|
||||||
// //-- Temp place to parse Links
|
|
||||||
// this.replaceLinks(function(){
|
|
||||||
// this.visible(true);
|
|
||||||
// }.bind(this));
|
|
||||||
// }else{
|
|
||||||
// this.visible(true);
|
|
||||||
// }
|
|
||||||
|
|
||||||
this.beforeDisplay(function(){
|
this.beforeDisplay(function(){
|
||||||
this.book.tell("book:chapterDisplayed");
|
this.book.tell("book:chapterDisplayed");
|
||||||
this.visible(true);
|
this.visible(true);
|
||||||
|
@ -160,12 +147,6 @@ FP.Chapter.prototype.calcPages = function(){
|
||||||
this.displayedPages = Math.ceil(this.totalWidth / this.spreadWidth);
|
this.displayedPages = Math.ceil(this.totalWidth / this.spreadWidth);
|
||||||
|
|
||||||
//console.log("Pages:", this.displayedPages)
|
//console.log("Pages:", this.displayedPages)
|
||||||
//-- I work for Chrome
|
|
||||||
//this.iframe.contentDocument.body.scrollLeft = 200;
|
|
||||||
|
|
||||||
//-- I work for Firefox
|
|
||||||
//this.iframe.contentDocument.documentElement.scrollLeft = 200;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -174,13 +155,11 @@ FP.Chapter.prototype.nextPage = function(){
|
||||||
this.chapterPos++;
|
this.chapterPos++;
|
||||||
|
|
||||||
this.leftPos += this.spreadWidth;
|
this.leftPos += this.spreadWidth;
|
||||||
//this.bodyEl.scrollLeft = this.leftPos;
|
|
||||||
//this.bodyEl.style.marginLeft = -this.leftPos + "px";
|
|
||||||
this.setLeft(this.leftPos);
|
this.setLeft(this.leftPos);
|
||||||
|
|
||||||
return this.chapterPos;
|
return this.chapterPos;
|
||||||
}else{
|
}else{
|
||||||
//this.nextChapter();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,27 +169,24 @@ FP.Chapter.prototype.prevPage = function(){
|
||||||
this.chapterPos--;
|
this.chapterPos--;
|
||||||
|
|
||||||
this.leftPos -= this.spreadWidth;
|
this.leftPos -= this.spreadWidth;
|
||||||
//this.bodyEl.scrollLeft = this.leftPos;
|
|
||||||
//this.bodyEl.style.marginLeft = -this.leftPos + "px";
|
|
||||||
this.setLeft(this.leftPos);
|
this.setLeft(this.leftPos);
|
||||||
|
|
||||||
return this.chapterPos;
|
return this.chapterPos;
|
||||||
}else{
|
}else{
|
||||||
//this.prevChapter();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FP.Chapter.prototype.chapterEnd = function(){
|
FP.Chapter.prototype.chapterEnd = function(){
|
||||||
this.page(this.displayedPages);
|
this.page(this.displayedPages);
|
||||||
//this.leftPos = this.spreadWidth * (this.displayedPages - 1);//this.totalWidth - this.colWidth;
|
|
||||||
//this.setLeft(this.leftPos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FP.Chapter.prototype.setLeft = function(leftPos){
|
FP.Chapter.prototype.setLeft = function(leftPos){
|
||||||
this.bodyEl.style.marginLeft = -leftPos + "px";
|
this.bodyEl.style.marginLeft = -leftPos + "px";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-- Replaces the relative links within the book to use our internal page changer
|
||||||
FP.Chapter.prototype.replaceLinks = function(callback){
|
FP.Chapter.prototype.replaceLinks = function(callback){
|
||||||
var hrefs = this.doc.querySelectorAll('[href]'),
|
var hrefs = this.doc.querySelectorAll('[href]'),
|
||||||
links = Array.prototype.slice.call(hrefs),
|
links = Array.prototype.slice.call(hrefs),
|
||||||
|
@ -218,7 +194,10 @@ FP.Chapter.prototype.replaceLinks = function(callback){
|
||||||
|
|
||||||
links.forEach(function(link){
|
links.forEach(function(link){
|
||||||
var path,
|
var path,
|
||||||
href = link.getAttribute("href");
|
href = link.getAttribute("href"),
|
||||||
|
relative = href.search("://");
|
||||||
|
|
||||||
|
if(relative != -1) return; //-- Only replace relative links
|
||||||
|
|
||||||
link.onclick = function(){
|
link.onclick = function(){
|
||||||
that.book.show(href);
|
that.book.show(href);
|
||||||
|
@ -229,11 +208,13 @@ FP.Chapter.prototype.replaceLinks = function(callback){
|
||||||
if(callback) callback();
|
if(callback) callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-- Replaces assets src's to point to stored version if browser is offline
|
||||||
FP.Chapter.prototype.replaceResources = function(callback){
|
FP.Chapter.prototype.replaceResources = function(callback){
|
||||||
var srcs, resources, count;
|
var srcs, resources, count;
|
||||||
|
|
||||||
|
//-- No need to replace if there is network connectivity
|
||||||
|
//-- also Filesystem api links are relative, so no need to replace them
|
||||||
if(this.book.online || FP.storage.getStorageType() == "filesystem") {
|
if(this.book.online || FP.storage.getStorageType() == "filesystem") {
|
||||||
//-- filesystem api links are relative, so no need to fix
|
|
||||||
if(callback) callback();
|
if(callback) callback();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -263,19 +244,21 @@ FP.Chapter.prototype.getID = function(){
|
||||||
FP.Chapter.prototype.page = function(pg){
|
FP.Chapter.prototype.page = function(pg){
|
||||||
if(pg >= 1 && pg <= this.displayedPages){
|
if(pg >= 1 && pg <= this.displayedPages){
|
||||||
this.chapterPos = pg;
|
this.chapterPos = pg;
|
||||||
this.leftPos = this.spreadWidth * (pg-1);
|
this.leftPos = this.spreadWidth * (pg-1); //-- pages start at 1
|
||||||
this.setLeft(this.leftPos);
|
this.setLeft(this.leftPos);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-- Return false if page is greater than the total
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-- Find a section by fragement id
|
||||||
FP.Chapter.prototype.section = function(fragment){
|
FP.Chapter.prototype.section = function(fragment){
|
||||||
var el = this.doc.getElementById(fragment),
|
var el = this.doc.getElementById(fragment),
|
||||||
left = this.leftPos + el.offsetLeft,
|
left = this.leftPos + el.offsetLeft, //-- Calculate left offset compaired to scrolled position
|
||||||
pg = Math.floor(left / this.spreadWidth) + 1;
|
pg = Math.floor(left / this.spreadWidth) + 1; //-- pages start at 1
|
||||||
|
|
||||||
this.page(pg);
|
this.page(pg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,201 +53,4 @@ FP.storage = function(){
|
||||||
"getStorageType" : getStorageType
|
"getStorageType" : getStorageType
|
||||||
}
|
}
|
||||||
|
|
||||||
}();
|
}();
|
||||||
|
|
||||||
/*
|
|
||||||
FP.storage.ram = function() {
|
|
||||||
var _store = {},
|
|
||||||
_blobs = {},
|
|
||||||
_queue = new FP.Queue("loader_ram.js", 3);
|
|
||||||
|
|
||||||
|
|
||||||
//-- TODO: this should be prototypes?
|
|
||||||
|
|
||||||
//-- Used for preloading
|
|
||||||
function preload(path) {
|
|
||||||
var fromCache = check(path);
|
|
||||||
|
|
||||||
if(!fromCache){
|
|
||||||
request(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function batch(group, callback){
|
|
||||||
_queue.addGroup(group)
|
|
||||||
}
|
|
||||||
|
|
||||||
//-- Fetches url
|
|
||||||
function get(path, callback) {
|
|
||||||
var fromCache = check(path),
|
|
||||||
url;
|
|
||||||
|
|
||||||
if(fromCache){
|
|
||||||
url = getURL(path, fromCache);
|
|
||||||
if(typeof(callback) != "undefined"){
|
|
||||||
callback(url);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
request(path, function(file){
|
|
||||||
url = getURL(path, file);
|
|
||||||
if(typeof(callback) != "undefined"){
|
|
||||||
callback(url);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function check(path) {
|
|
||||||
var file = _store[file];
|
|
||||||
|
|
||||||
if(typeof(file) != "undefined"){
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function request(path, callback) {
|
|
||||||
var xhr = new FP.core.loadFile(path);
|
|
||||||
|
|
||||||
xhr.succeeded = function(file) {
|
|
||||||
//console.log("file", file)
|
|
||||||
cache(path, file);
|
|
||||||
if(typeof(callback) != "undefined"){
|
|
||||||
callback(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xhr.failed = _error;
|
|
||||||
|
|
||||||
xhr.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
function cache(path, file) {
|
|
||||||
if(_store[path]) return;
|
|
||||||
|
|
||||||
_store[path] = file;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function getURL(path, file){
|
|
||||||
var url;
|
|
||||||
|
|
||||||
if(typeof(_blobs[path]) != "undefined"){
|
|
||||||
return _blobs[path];
|
|
||||||
}
|
|
||||||
|
|
||||||
url = this._URL.createObjectURL(file);
|
|
||||||
|
|
||||||
//-- need to revokeObjectURL previous urls, but only when cleaning cache
|
|
||||||
// this.createdURLs.forEach(function(url){
|
|
||||||
// this._URL.revokeObjectURL(url);
|
|
||||||
// });
|
|
||||||
|
|
||||||
_blobs[path] = url;
|
|
||||||
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this.succeeded = function(){
|
|
||||||
// console.log("loaded");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// this.failed = function(){
|
|
||||||
// console.log("loaded");
|
|
||||||
// }
|
|
||||||
|
|
||||||
function _error(err){
|
|
||||||
if(typeof(this.failed) == "undefined"){
|
|
||||||
console.log("Error: ", err);
|
|
||||||
}else{
|
|
||||||
this.failed(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
"get" : get,
|
|
||||||
"preload" : preload
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
FP.store = FP.store || {};
|
|
||||||
|
|
||||||
FP.store.none = function() {
|
|
||||||
var _store = {};
|
|
||||||
|
|
||||||
//-- Used for preloading
|
|
||||||
function preload(path) {
|
|
||||||
var fromCache = check(path);
|
|
||||||
|
|
||||||
if(!fromCache){
|
|
||||||
request(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//-- need to add batch!
|
|
||||||
|
|
||||||
//-- Fetches url
|
|
||||||
function get(path, callback) {
|
|
||||||
var fromCache = check(path),
|
|
||||||
url;
|
|
||||||
|
|
||||||
if(fromCache){
|
|
||||||
callback(path);
|
|
||||||
}else{
|
|
||||||
request(path, function(file){
|
|
||||||
callback(path);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function check(path) {
|
|
||||||
var file = _store[path];
|
|
||||||
|
|
||||||
if(typeof(file) != "undefined"){
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function request(path, callback) {
|
|
||||||
var xhr = new FP.core.loadFile(path);
|
|
||||||
|
|
||||||
xhr.succeeded = function(file) {
|
|
||||||
cache(path, file);
|
|
||||||
if(typeof(callback) != "undefined"){
|
|
||||||
callback(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xhr.failed = _error;
|
|
||||||
|
|
||||||
xhr.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
function cache(path, file) {
|
|
||||||
if(_store[path]) return;
|
|
||||||
|
|
||||||
_store[path] = file;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function _error(err){
|
|
||||||
if(typeof(this.failed) == "undefined"){
|
|
||||||
console.log("Error: ", err);
|
|
||||||
}else{
|
|
||||||
this.failed(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
"get" : get,
|
|
||||||
"preload" : preload
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
103
fpjs/render/storage_none.js
Normal file
103
fpjs/render/storage_none.js
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
FP.store = FP.store || {};
|
||||||
|
|
||||||
|
FP.store.none = function() {
|
||||||
|
var _store = {},
|
||||||
|
_blobs = {},
|
||||||
|
_queue = new FP.Queue(loader, 6);
|
||||||
|
//-- max of 6 concurrent requests: http://www.browserscope.org/?category=network
|
||||||
|
|
||||||
|
|
||||||
|
function loader(msg, callback){
|
||||||
|
var e = {"data":null},
|
||||||
|
fromCache = check(msg);
|
||||||
|
|
||||||
|
if(fromCache){
|
||||||
|
e.data = fromCache;
|
||||||
|
callback(e);
|
||||||
|
}else{
|
||||||
|
request(msg, function(url){
|
||||||
|
e.data = url;
|
||||||
|
callback(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function preload(path) {
|
||||||
|
var fromCache = check(path);
|
||||||
|
|
||||||
|
if(!fromCache){
|
||||||
|
_queue.add(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function batch(group, callback){
|
||||||
|
_queue.addGroup(group, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-- Fetches url
|
||||||
|
function get(path, callback) {
|
||||||
|
var fromCache = check(path),
|
||||||
|
url;
|
||||||
|
|
||||||
|
if(fromCache){
|
||||||
|
if(typeof(callback) != "undefined"){
|
||||||
|
callback(fromCache);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
_queue.add(path, function(file){
|
||||||
|
url = getURL(path, file);
|
||||||
|
if(typeof(callback) != "undefined"){
|
||||||
|
callback(url);
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function check(path) {
|
||||||
|
var url = _store[path];
|
||||||
|
|
||||||
|
if(typeof(url) != "undefined"){
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function request(path, callback) {
|
||||||
|
var xhr = new FP.core.loadFile(path);
|
||||||
|
|
||||||
|
xhr.succeeded = function(file) {
|
||||||
|
//console.log("file", file)
|
||||||
|
cache(path, file);
|
||||||
|
if(typeof(callback) != "undefined"){
|
||||||
|
callback(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xhr.failed = _error;
|
||||||
|
|
||||||
|
xhr.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
function cache(path, file) {
|
||||||
|
if(_store[path]) return;
|
||||||
|
|
||||||
|
_store[path] = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function _error(err){
|
||||||
|
if(typeof(this.failed) == "undefined"){
|
||||||
|
console.log("Error: ", err);
|
||||||
|
}else{
|
||||||
|
this.failed(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"get" : get,
|
||||||
|
"preload" : preload,
|
||||||
|
"batch" : batch
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,7 +60,7 @@
|
||||||
<div id="main">
|
<div id="main">
|
||||||
<div id="titlebar">
|
<div id="titlebar">
|
||||||
<span id="book-title"></span>
|
<span id="book-title"></span>
|
||||||
–
|
<span id="title-seperator"> – </span>
|
||||||
<span id="chapter-title"> </span>
|
<span id="chapter-title"> </span>
|
||||||
</div>
|
</div>
|
||||||
<div id="prev" class="arrow">‹</div>
|
<div id="prev" class="arrow">‹</div>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue