From 0398b5eedffdcf87e23474b52c8b64a4aa097b76 Mon Sep 17 00:00:00 2001 From: Fred Chasen Date: Fri, 17 Jan 2014 18:39:38 -0800 Subject: [PATCH] split rendering up into renderer, render, layout --- demo/dev.html | 2 + hooks/default/endnotes.js | 14 +- hooks/default/mathml.js | 2 +- hooks/default/smartimages.js | 20 +- hooks/default/transculsions.js | 6 +- hooks/extensions/highlight.js | 2 +- reader/controllers/controls_controller.js | 1 - reader/controllers/reader_controller.js | 10 +- src/base.js | 2 + src/book.js | 279 ++++--- src/chapter.js | 12 +- src/content.js | 127 ---- src/core.js | 15 +- src/epubcfi.js | 2 +- src/hooks.js | 87 ++- src/layout.js | 135 ++-- src/navigation.js | 196 ----- src/parser.js | 17 +- src/render_iframe.js | 171 +++++ src/renderer.js | 851 ++++++++++++++-------- src/replace.js | 31 +- src/unarchiver.js | 7 +- 22 files changed, 1149 insertions(+), 840 deletions(-) delete mode 100644 src/content.js create mode 100644 src/render_iframe.js diff --git a/demo/dev.html b/demo/dev.html index b1077cb..f1cf7cf 100755 --- a/demo/dev.html +++ b/demo/dev.html @@ -56,6 +56,8 @@ + + diff --git a/hooks/default/endnotes.js b/hooks/default/endnotes.js index 12bee16..781e7c5 100644 --- a/hooks/default/endnotes.js +++ b/hooks/default/endnotes.js @@ -1,6 +1,6 @@ -EPUBJS.Hooks.register("beforeChapterDisplay").endnotes = function(callback, chapter){ +EPUBJS.Hooks.register("beforeChapterDisplay").endnotes = function(callback, renderer){ - var notes = chapter.doc.querySelectorAll('a[href]'), + var notes = renderer.contents.querySelectorAll('a[href]'), items = Array.prototype.slice.call(notes), //[].slice.call() attr = "epub:type", type = "noteref", @@ -8,7 +8,7 @@ EPUBJS.Hooks.register("beforeChapterDisplay").endnotes = function(callback, chap cssPath = folder + EPUBJS.cssPath || folder, popups = {}; - EPUBJS.core.addCss(cssPath + "popup.css", false, chapter.doc.head); + EPUBJS.core.addCss(cssPath + "popup.css", false, renderer.render.document.head); items.forEach(function(item){ @@ -26,7 +26,7 @@ EPUBJS.Hooks.register("beforeChapterDisplay").endnotes = function(callback, chap href = item.getAttribute("href"); id = href.replace("#", ''); - el = chapter.doc.getElementById(id); + el = renderer.render.document.getElementById(id); item.addEventListener("mouseover", showPop, false); @@ -34,8 +34,8 @@ EPUBJS.Hooks.register("beforeChapterDisplay").endnotes = function(callback, chap function showPop(){ var poppos, - iheight = chapter.iframe.height, - iwidth = chapter.iframe.width, + iheight = renderer.height, + iwidth = renderer.width, tip, pop, maxHeight = 225; @@ -45,7 +45,7 @@ EPUBJS.Hooks.register("beforeChapterDisplay").endnotes = function(callback, chap txt = pop.querySelector("p"); } - chapter.replaceLinks.bind(this) + // chapter.replaceLinks.bind(this) //TODO:Fred - update? //-- create a popup with endnote inside of it if(!popups[id]) { popups[id] = document.createElement("div"); diff --git a/hooks/default/mathml.js b/hooks/default/mathml.js index 5340042..6037cde 100644 --- a/hooks/default/mathml.js +++ b/hooks/default/mathml.js @@ -1,7 +1,7 @@ EPUBJS.Hooks.register("beforeChapterDisplay").mathml = function(callback, renderer){ // check of currentChapter properties contains 'mathml' - if(renderer.currentChapter.properties.indexOf("mathml") !== -1 ){ + if(renderer.currentChapter.manifestProperties.indexOf("mathml") !== -1 ){ // Assign callback to be inside iframe window renderer.iframe.contentWindow.mathmlCallback = callback; diff --git a/hooks/default/smartimages.js b/hooks/default/smartimages.js index ce4a45c..9e16682 100644 --- a/hooks/default/smartimages.js +++ b/hooks/default/smartimages.js @@ -1,9 +1,14 @@ -EPUBJS.Hooks.register("beforeChapterDisplay").smartimages = function(callback, chapter){ - var images = chapter.doc.querySelectorAll('img'), +EPUBJS.Hooks.register("beforeChapterDisplay").smartimages = function(callback, renderer){ + var images = renderer.contents.querySelectorAll('img'), items = Array.prototype.slice.call(images), - iheight = chapter.bodyEl.clientHeight,//chapter.doc.body.getBoundingClientRect().height, + iheight = renderer.height,//chapter.bodyEl.clientHeight,//chapter.doc.body.getBoundingClientRect().height, oheight; + if(renderer.settings.layout != "reflowable") { + callback(); + return; //-- Only adjust images for reflowable text + } + items.forEach(function(item){ function size() { @@ -14,7 +19,7 @@ EPUBJS.Hooks.register("beforeChapterDisplay").smartimages = function(callback, c height = oHeight || rectHeight, newHeight; - iheight = chapter.docEl.clientHeight; + iheight = renderer.contents.clientHeight; if(top < 0) top = 0; if(height + top >= iheight) { @@ -28,7 +33,6 @@ EPUBJS.Hooks.register("beforeChapterDisplay").smartimages = function(callback, c item.style.maxHeight = newHeight + "px"; item.style.marginTop = iheight - top + "px"; item.style.width= "auto"; - console.log(newHeight) } item.setAttribute('data-height', newHeight); @@ -41,11 +45,11 @@ EPUBJS.Hooks.register("beforeChapterDisplay").smartimages = function(callback, c item.addEventListener('load', size, false); - chapter.on("renderer:resized", size); + renderer.on("renderer:resized", size); - chapter.on("renderer:chapterUnloaded", function(){ + renderer.on("renderer:chapterUnloaded", function(){ item.removeEventListener('load', size); - chapter.off("renderer:resized", size); + renderer.off("renderer:resized", size); }); size(); diff --git a/hooks/default/transculsions.js b/hooks/default/transculsions.js index 7a5bac9..99d1a40 100644 --- a/hooks/default/transculsions.js +++ b/hooks/default/transculsions.js @@ -1,12 +1,12 @@ -EPUBJS.Hooks.register("beforeChapterDisplay").transculsions = function(callback, chapter){ +EPUBJS.Hooks.register("beforeChapterDisplay").transculsions = function(callback, renderer){ /* */ - var trans = chapter.doc.querySelectorAll('[transclusion]'), - items = Array.prototype.slice.call(trans); + var trans = renderer.contents.querySelectorAll('[transclusion]'), + items = Array.prototype.slice.call(trans); items.forEach(function(item){ var src = item.getAttribute("ref"), diff --git a/hooks/extensions/highlight.js b/hooks/extensions/highlight.js index cf4f885..1dd1c67 100644 --- a/hooks/extensions/highlight.js +++ b/hooks/extensions/highlight.js @@ -5,7 +5,7 @@ EPUBJS.Hooks.register("beforeChapterDisplay").highlight = function(callback, ren var s = document.createElement("style"); s.innerHTML =".highlight { background: yellow; font-weight: normal; }"; - renderer.doc.head.appendChild(s); + renderer.render.document.head.appendChild(s); if(callback) callback(); diff --git a/reader/controllers/controls_controller.js b/reader/controllers/controls_controller.js index 4533b05..3ec1b70 100644 --- a/reader/controllers/controls_controller.js +++ b/reader/controllers/controls_controller.js @@ -81,7 +81,6 @@ EPUBJS.reader.ControlsController = function(book) { book.on('renderer:pageChanged', function(cfi){ //-- Check if bookmarked var bookmarked = reader.isBookmarked(cfi); - if(bookmarked === -1) { //-- Not bookmarked $bookmark .removeClass("icon-bookmark") diff --git a/reader/controllers/reader_controller.js b/reader/controllers/reader_controller.js index 864fc98..7c7e580 100644 --- a/reader/controllers/reader_controller.js +++ b/reader/controllers/reader_controller.js @@ -30,9 +30,9 @@ EPUBJS.reader.ReaderController = function(book) { $loader.hide(); //-- If the book is using spreads, show the divider - if(book.settings.spreads) { - showDivider(); - } + // if(book.settings.spreads) { + // showDivider(); + // } }; var showDivider = function() { @@ -84,8 +84,8 @@ EPUBJS.reader.ReaderController = function(book) { e.preventDefault(); }); - book.on("book:spreads", function(){ - if(book.settings.spreads) { + book.on("renderer:spreads", function(bool){ + if(bool) { showDivider(); } else { hideDivider(); diff --git a/src/base.js b/src/base.js index b792935..bee353a 100644 --- a/src/base.js +++ b/src/base.js @@ -7,6 +7,8 @@ EPUBJS.plugins = EPUBJS.plugins || {}; EPUBJS.filePath = EPUBJS.filePath || "/epubjs/"; +EPUBJS.Render = {}; + (function(root) { var previousEpub = root.ePub || {}; diff --git a/src/book.js b/src/book.js index c05aae3..ed16aa5 100644 --- a/src/book.js +++ b/src/book.js @@ -13,9 +13,10 @@ EPUBJS.Book = function(options){ contained : false, width : false, height: false, - spreads: true, - fixedLayout : false, - responsive: true, + spread: null, + layout : null, + orientation : null, + minSpreadWidth: 800, //-- overridden by spread: none (never) / both (always) version: 1, restore: false, reload : false, @@ -39,13 +40,9 @@ EPUBJS.Book = function(options){ book:pageChanged */ - //-- All hooks to add functions (with a callback) to - this.hooks = { - "beforeChapterDisplay" : [] - }; - + EPUBJS.Hooks.mixin(this); //-- Get pre-registered hooks - this.getHooks(); + this.getHooks("beforeChapterDisplay"); this.online = this.settings.online || navigator.onLine; this.networkListeners(); @@ -57,8 +54,6 @@ EPUBJS.Book = function(options){ this.storage = new fileStorage.storage(this.settings.storage); } - - this.ready = { manifest: new RSVP.defer(), spine: new RSVP.defer(), @@ -83,6 +78,10 @@ EPUBJS.Book = function(options){ this.isRendered = false; this._rendering = false; this._displayQ = []; + + this.renderer = new EPUBJS.Renderer(this.settings.renderer, this.beforeDisplay.bind(this)); + this.renderer.setMinSpreadWidth(this.settings.minSpreadWidth); + this.listenToRenderer(this.renderer); this.defer_opened = new RSVP.defer(); this.opened = this.defer_opened.promise; @@ -154,6 +153,8 @@ EPUBJS.Book.prototype.open = function(bookPath, forceReload){ if(!this.settings.stored) opened.then(book.storeOffline()); } + this._registerReplacements(); + return opened.promise; }; @@ -201,7 +202,10 @@ EPUBJS.Book.prototype.unpack = function(packageXml){ book.spineIndexByURL = book.contents.spineIndexByURL; book.metadata = book.contents.metadata; book.setBookKey(book.metadata.identifier); - + + //-- Set Globbal Layout setting based on metadata + book.globalLayoutProperties = book.parseLayoutProperties(book.metadata); + book.cover = book.contents.cover = book.settings.contentsPath + book.contents.coverPath; book.spineNodeIndex = book.contents.spineNodeIndex; @@ -211,7 +215,6 @@ EPUBJS.Book.prototype.unpack = function(packageXml){ book.ready.metadata.resolve(book.contents.metadata); book.ready.cover.resolve(book.contents.cover); - //-- TODO: Adjust setting based on metadata //-- Load the TOC, optional; either the EPUB3 XHTML Navigation file or the EPUB2 NCX file if(book.contents.navPath) { @@ -271,6 +274,21 @@ EPUBJS.Book.prototype.networkListeners = function(){ }; +EPUBJS.Book.prototype.listenToRenderer = function(renderer){ + var book = this; + renderer.Events.forEach(function(eventName){ + renderer.on(eventName, function(e){ + book.trigger(eventName, e); + }); + }); +}; + +EPUBJS.Book.prototype.unlistenToRenderer = function(renderer){ + renderer.Events.forEach(function(eventName){ + renderer.off(eventName); + } ); +}; + //-- Choose between a request from store or a request from network EPUBJS.Book.prototype.loadXml = function(url){ if(this.settings.fromStorage) { @@ -335,11 +353,9 @@ EPUBJS.Book.prototype.unarchive = function(bookPath){ //-- Checks if url has a .epub or .zip extension EPUBJS.Book.prototype.isContained = function(bookUrl){ - var uri = EPUBJS.core.uri(bookUrl), - dot = uri.filename.lastIndexOf('.'), - ext = uri.filename.slice(dot+1); + var uri = EPUBJS.core.uri(bookUrl); - if(ext && (ext == "epub" || ext == "zip")){ + if(uri.extension && (uri.extension == "epub" || uri.extension == "zip")){ return true; } @@ -379,10 +395,6 @@ EPUBJS.Book.prototype.removeSavedContents = function() { -// EPUBJS.Book.prototype.chapterTitle = function(){ -// return this.spine[this.spinePos].id; //-- TODO: clarify that this is returning title -// } - //-- Takes a string or a element EPUBJS.Book.prototype.renderTo = function(elem){ var book = this, @@ -399,12 +411,13 @@ EPUBJS.Book.prototype.renderTo = function(elem){ rendered = this.opened. then(function(){ - book.render = new EPUBJS.Renderer(book); + // book.render = new EPUBJS.Renderer[this.settings.renderer](book); + book.renderer.initialize(book.element, book.settings.width, book.settings.height); book._rendered(); return book.startDisplay(); - }, function(error) { console.error(error); }); + }); - rendered.then(null, function(error) { console.error(error); }); + // rendered.then(null, function(error) { console.error(error); }); return rendered; }; @@ -413,17 +426,11 @@ EPUBJS.Book.prototype.startDisplay = function(){ var display; if(this.settings.goto) { - display = this.goto(this.settings.goto); - }else if(this.settings.previousLocationCfi) { - - display = this.displayChapter(this.settings.previousLocationCfi); - + display = this.gotoCfi(this.settings.previousLocationCfi); }else{ - display = this.displayChapter(this.spinePos); - } return display; @@ -469,12 +476,22 @@ EPUBJS.Book.prototype.restore = function(identifier){ }; +EPUBJS.Book.prototype.determineStore = function(){ + var store; + if(this.settings.stored){ + store = this.storage; + } else if(this.settings.contained){ + store = this.zip; + } + return store; +} EPUBJS.Book.prototype.displayChapter = function(chap, end){ var book = this, render, cfi, - pos; + pos, + store; if(!this.isRendered) return this._enqueue("displayChapter", arguments); @@ -501,16 +518,16 @@ EPUBJS.Book.prototype.displayChapter = function(chap, end){ this.spinePos = pos; //-- Create a new chapter - this.chapter = new EPUBJS.Chapter(this.spine[pos]); + this.currentChapter = new EPUBJS.Chapter(this.spine[pos], this.determineStore()); this._rendering = true; - render = book.render.chapter(this.chapter); + render = book.renderer.displayChapter(this.currentChapter, this.globalLayoutProperties); if(cfi) { render.then(function(chapter){ - chapter.currentLocationCfi = chap; - chapter.gotoCfiFragment(cfi); + // chapter.currentLocationCfi = chap; + chapter.gotoCfi(cfi); }); } else if(end) { render.then(function(chapter){ @@ -532,7 +549,7 @@ EPUBJS.Book.prototype.displayChapter = function(chap, end){ book._rendering = false; if(book._displayQ.length) { - inwait = book._displayQ.unshift(); + inwait = book._displayQ.shift(); book.displayChapter.apply(book, inwait); } @@ -545,7 +562,7 @@ EPUBJS.Book.prototype.nextPage = function(){ if(!this.isRendered) return this._enqueue("nextPage", arguments); - next = this.render.nextPage(); + next = this.renderer.nextPage(); if(!next){ return this.nextChapter(); @@ -557,7 +574,7 @@ EPUBJS.Book.prototype.prevPage = function() { if(!this.isRendered) return this._enqueue("prevPage", arguments); - prev = this.render.prevPage(); + prev = this.renderer.prevPage(); if(!prev){ return this.prevChapter(); @@ -565,30 +582,60 @@ EPUBJS.Book.prototype.prevPage = function() { }; EPUBJS.Book.prototype.nextChapter = function() { - if(this.spinePos > this.spine.length) return; - this.spinePos++; - return this.displayChapter(this.spinePos); + if(this.spinePos < this.spine.length - 1){ + this.spinePos += 1; + return this.displayChapter(this.spinePos); + } else { + this.trigger("book:atEnd"); + } + }; EPUBJS.Book.prototype.prevChapter = function() { - if(this.spinePos < 1) return; - this.spinePos--; - return this.displayChapter(this.spinePos, true); + if(this.spinePos > 0){ + this.spinePos -= 1; + return this.displayChapter(this.spinePos, true); + } else { + this.trigger("book:atStart"); + } }; EPUBJS.Book.prototype.getCurrentLocationCfi = function() { if(!this.isRendered) return false; - return this.render.currentLocationCfi; + return this.renderer.currentLocationCfi; }; -EPUBJS.Book.prototype.gotoCfi = function(cfi){ - //if(!this.isRendered) return this._enqueue("gotoCfi", arguments); +EPUBJS.Book.prototype.gotoCfi = function(cfiString){ + var cfi = new EPUBJS.EpubCFI(cfiString); + var spinePos = cfi.spinePos; + var rendered; + var deferred = new RSVP.defer(); + //if(!this.isRendered) return this._enqueue("gotoCfi", arguments); if(!this.isRendered) { - this.settings.previousLocationCfi = cfi; + this.settings.previousLocationCfi = cfiString; return; } - return this.displayChapter(cfi); + //-- If same chapter only stay on current chapter + if(this.currentChapter && this.spinePos === spinePos){ + this.renderer.gotoCfi(cfi); + deferred.resolve(this.currentChapter); + return deferred.promise; + } else { + this.currentChapter = new EPUBJS.Chapter(this.spine[spinePos], this.determineStore()); + + if(this.currentChapter) { + this.spinePos = spinePos; + render = this.renderer.displayChapter(this.currentChapter, this.globalLayoutProperties); + + render.then(function(rendered){ + rendered.gotoCfi(cfi); + }); + } + + return render; + } + }; EPUBJS.Book.prototype.goto = function(url){ @@ -608,13 +655,13 @@ EPUBJS.Book.prototype.goto = function(url){ //-- If link fragment only stay on current chapter if(!chapter){ - spinePos = this.chapter ? this.chapter.spinePos : 0; + spinePos = this.currentChapter ? this.currentChapter.spinePos : 0; } //-- Check that URL is present in the index, or stop if(typeof(spinePos) != "number") return false; - if(!this.chapter || spinePos != this.chapter.spinePos){ + if(!this.currentChapter || spinePos != this.currentChapter.spinePos){ //-- Load new chapter if different than current return this.displayChapter(spinePos).then(function(){ if(section) this.render.section(section); @@ -688,14 +735,14 @@ EPUBJS.Book.prototype.setStyle = function(style, val, prefixed) { this.settings.styles[style] = val; - this.render.setStyle(style, val, prefixed); - this.render.reformat(); + this.renderer.setStyle(style, val, prefixed); + this.renderer.reformat(); }; EPUBJS.Book.prototype.removeStyle = function(style) { if(!this.isRendered) return this._enqueue("removeStyle", arguments); - this.render.removeStyle(style); - this.render.reformat(); + this.renderer.removeStyle(style); + this.renderer.reformat(); delete this.settings.styles[style]; }; @@ -705,11 +752,19 @@ EPUBJS.Book.prototype.addHeadTag = function(tag, attrs) { }; EPUBJS.Book.prototype.useSpreads = function(use) { - if(!this.isRendered) return this._enqueue("useSpreads", arguments); - this.settings.spreads = use; - this.render.reformat(); - this.trigger("book:spreads", use); -}; + if(use) { + this.renderer.setMinSpreadWidth(this.settings.minSpreadWidth); + this.settings.spreads = true; + } else { + this.renderer.setMinSpreadWidth(0); + this.settings.spreads = false; + } + + if(this.isRendered) { + this.renderer.reformat(); + } + +}; EPUBJS.Book.prototype.unload = function(){ @@ -717,6 +772,8 @@ EPUBJS.Book.prototype.unload = function(){ this.saveContents(); } + this.unlistenToRenderer(this.renderer); + this.trigger("book:unload"); }; @@ -759,66 +816,64 @@ EPUBJS.Book.prototype._rendered = function(err) { }; -//-- Get pre-registered hooks -EPUBJS.Book.prototype.getHooks = function(){ - var book = this, - plugs; - - var plugTypes = _.values(this.hooks); //-- unused - for (var plugType in this.hooks) { - plugs = _.values(EPUBJS.Hooks[plugType]); - - plugs.forEach(function(hook){ - book.registerHook(plugType, hook); - }); - } +EPUBJS.Book.prototype.applyStyles = function(callback){ + if(!this.isRendered) return this._enqueue("applyStyles", arguments); + this.renderer.applyStyles(this.settings.styles); + callback(); }; -//-- Hooks allow for injecting async functions that must all complete before continuing -// Functions must have a callback as their first argument. -EPUBJS.Book.prototype.registerHook = function(type, toAdd, toFront){ - var book = this; - - if(typeof(this.hooks[type]) != "undefined"){ - - if(typeof(toAdd) === "function"){ - if(toFront) { - this.hooks[type].unshift(toAdd); - }else{ - this.hooks[type].push(toAdd); - } - }else if(Array.isArray(toAdd)){ - toAdd.forEach(function(hook){ - if(toFront) { - book.hooks[type].unshift(hook); - }else{ - book.hooks[type].push(hook); - } - }); +EPUBJS.Book.prototype._registerReplacements = function(){ + this.registerHook("beforeChapterDisplay", this.applyStyles.bind(this), true); + this.registerHook("beforeChapterDisplay", EPUBJS.replace.hrefs, true); + + if(this._needsAssetReplacement()) { + + this.registerHook("beforeChapterDisplay", [ + EPUBJS.replace.head, + EPUBJS.replace.resources, + EPUBJS.replace.svg + ], true); + + } + +}; + +EPUBJS.Book.prototype._needsAssetReplacement = function(){ + if(this.settings.fromStorage) { + + //-- Filesystem api links are relative, so no need to replace them + if(this.storage.getStorageType() == "filesystem") { + return false; } - }else{ - //-- Allows for undefined hooks, but maybe this should error? - this.hooks[type] = [func]; + + return true; + + } else if(this.settings.contained) { + + return true; + + } else { + + return false; + } }; -EPUBJS.Book.prototype.triggerHooks = function(type, callback, passed){ - var hooks, count; +EPUBJS.Book.prototype.beforeDisplay = function(callback, renderer){ + this.triggerHooks("beforeChapterDisplay", callback.bind(this), this.renderer); +}; - if(typeof(this.hooks[type]) == "undefined") return false; - - hooks = this.hooks[type]; - - count = hooks.length; - function countdown(){ - count--; - if(count <= 0 && callback) callback(); +//-- http://www.idpf.org/epub/fxl/ +EPUBJS.Book.prototype.parseLayoutProperties = function(metadata){ + var layout = this.settings.layout || metadata.layout || "reflowable"; + var spread = this.settings.spread || metadata.spread || "auto"; + var orientation = this.settings.orientations || metadata.orientation || "auto"; + return { + layout : layout, + spread : spread, + orientation : orientation } - - hooks.forEach(function(hook){ - hook(countdown, passed); - }); }; //-- Enable binding events to book diff --git a/src/chapter.js b/src/chapter.js index 4fe7419..1e94a4e 100644 --- a/src/chapter.js +++ b/src/chapter.js @@ -1,15 +1,18 @@ -EPUBJS.Chapter = function(spineObject){ +EPUBJS.Chapter = function(spineObject, store){ this.href = spineObject.href; this.absolute = spineObject.url; this.id = spineObject.id; this.spinePos = spineObject.index; + this.cfiBase = spineObject.cfiBase; this.properties = spineObject.properties; this.linear = spineObject.linear; this.pages = 1; + this.store = store; }; -EPUBJS.Chapter.prototype.contents = function(store){ +EPUBJS.Chapter.prototype.contents = function(_store){ + var store = _store || this.store; // if(this.store && (!this.book.online || this.book.contained)) if(store){ return store.get(href); @@ -19,9 +22,10 @@ EPUBJS.Chapter.prototype.contents = function(store){ }; -EPUBJS.Chapter.prototype.url = function(store){ +EPUBJS.Chapter.prototype.url = function(_store){ var deferred = new RSVP.defer(); - + var store = _store || this.store; + if(store){ if(!this.tempUrl) { this.tempUrl = store.getUrl(this.absolute); diff --git a/src/content.js b/src/content.js deleted file mode 100644 index 940d257..0000000 --- a/src/content.js +++ /dev/null @@ -1,127 +0,0 @@ -EPUBJS.Renderer.prototype.replace = function(query, func, finished, progress){ - var items = this.doc.querySelectorAll(query), - resources = Array.prototype.slice.call(items), - count = resources.length, - after = function(result, full){ - count--; - if(progress) progress(result, full, count); - if(count <= 0 && finished) finished(true); - }; - - if(count === 0) { - finished(false); - return; - } - - resources.forEach(function(item){ - - func(item, after); - - }.bind(this)); - -}; - -EPUBJS.Renderer.prototype.replaceWithStored = function(query, attr, func, callback) { - var _oldUrls, - _newUrls = {}, - _store = this.determineStore(), - _cache = this.caches[query], - _uri = EPUBJS.core.uri(this.book.chapter.absolute), - _chapterBase = _uri.base, - _attr = attr, - _wait = 2000, - progress = function(url, full, count) { - _newUrls[full] = url; - }, - finished = function(notempty) { - if(callback) callback(); - - _.each(_oldUrls, function(url){ - _store.revokeUrl(url); - }); - - _cache = _newUrls; - }; - - if(!_store) return; - - if(!_cache) _cache = {}; - _oldUrls = _.clone(_cache); - - this.replace(query, function(link, done){ - var src = link.getAttribute(_attr), - full = EPUBJS.core.resolveUrl(_chapterBase, src); - - var replaceUrl = function(url) { - var timeout; - - link.onload = function(){ - clearTimeout(timeout); - done(url, full); - }; - - link.onerror = function(e){ - clearTimeout(timeout); - done(url, full); - console.error(e); - }; - - if(query == "image") { - //-- SVG needs this to trigger a load event - link.setAttribute("externalResourcesRequired", "true"); - } - - if(query == "link[href]") { - //-- Only Stylesheet links seem to have a load events, just continue others - done(url, full); - } - - link.setAttribute(_attr, url); - - //-- If elements never fire Load Event, should continue anyways - timeout = setTimeout(function(){ - done(url, full); - }, _wait); - - }; - - if(full in _oldUrls){ - replaceUrl(_oldUrls[full]); - _newUrls[full] = _oldUrls[full]; - delete _oldUrls[full]; - }else{ - func(_store, full, replaceUrl, link); - } - - }, finished, progress); -}; - -//-- Replaces the relative links within the book to use our internal page changer -EPUBJS.Renderer.prototype.replaceLinks = function(callback){ - - var renderer = this; - - this.replace("a[href]", function(link, done){ - - var href = link.getAttribute("href"), - relative = href.search("://"), - fragment = href[0] == "#"; - - if(relative != -1){ - - link.setAttribute("target", "_blank"); - - }else{ - - link.onclick = function(){ - renderer.book.goto(href); - return false; - }; - } - - done(); - - }, callback); - -}; - diff --git a/src/core.js b/src/core.js index 172ee25..23cf9cf 100644 --- a/src/core.js +++ b/src/core.js @@ -111,13 +111,21 @@ EPUBJS.core.uri = function(url){ directory : '', base : '', filename : '', + extension : '', href : url }, doubleSlash = url.indexOf('://'), search = url.indexOf('?'), + fragment = url.indexOf("#"), withoutProtocol, + dot, firstSlash; - + + if(fragment != -1) { + uri.fragment = url.slice(fragment + 1); + url = url.slice(0, fragment); + } + if(search != -1) { uri.search = url.slice(search + 1); url = url.slice(0, search); @@ -151,7 +159,10 @@ EPUBJS.core.uri = function(url){ //-- Filename uri.filename = url.replace(uri.base, ''); - + dot = uri.filename.lastIndexOf('.'); + if(dot != -1) { + uri.extension = uri.filename.slice(dot+1); + } return uri; }; diff --git a/src/epubcfi.js b/src/epubcfi.js index cc92d5c..1031603 100644 --- a/src/epubcfi.js +++ b/src/epubcfi.js @@ -168,7 +168,7 @@ EPUBJS.EpubCFI.prototype.getElement = function(cfi, _doc) { } - if(!element) console.error("No Element For", part); + if(!element) console.error("No Element For", part, cfi); children = Array.prototype.slice.call(element.children); } diff --git a/src/hooks.js b/src/hooks.js index 0368dad..2239380 100644 --- a/src/hooks.js +++ b/src/hooks.js @@ -1,11 +1,80 @@ +EPUBJS.hooks = {}; EPUBJS.Hooks = (function(){ - - "use strict"; - return { - register: function(name) { - if(this[name] === undefined) { this[name] = {}; } - if(typeof this[name] !== 'object') { throw "Already registered: "+name; } - return this[name]; - } + function hooks(){}; + + //-- Get pre-registered hooks + hooks.prototype.getHooks = function(){ + var plugs; + this.hooks = {}; + Array.prototype.slice.call(arguments).forEach(function(arg){ + this.hooks[arg] = []; + }, this); + + for (var plugType in this.hooks) { + plugs = _.values(EPUBJS.hooks[plugType]); + + plugs.forEach(function(hook){ + this.registerHook(plugType, hook); + }, this); + } }; -})(); \ No newline at end of file + + //-- Hooks allow for injecting async functions that must all complete before continuing + // Functions must have a callback as their first argument. + hooks.prototype.registerHook = function(type, toAdd, toFront){ + + if(typeof(this.hooks[type]) != "undefined"){ + + if(typeof(toAdd) === "function"){ + if(toFront) { + this.hooks[type].unshift(toAdd); + }else{ + this.hooks[type].push(toAdd); + } + }else if(Array.isArray(toAdd)){ + toAdd.forEach(function(hook){ + if(toFront) { + this.hooks[type].unshift(hook); + }else{ + this.hooks[type].push(hook); + } + }, this); + } + }else{ + //-- Allows for undefined hooks, but maybe this should error? + this.hooks[type] = [func]; + } + }; + + hooks.prototype.triggerHooks = function(type, callback, passed){ + 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, passed); + }); + }; + + return { + register: function(name) { + if(EPUBJS.hooks[name] === undefined) { EPUBJS.hooks[name] = {}; } + if(typeof EPUBJS.hooks[name] !== 'object') { throw "Already registered: "+name; } + return EPUBJS.hooks[name]; + }, + mixin: function(object) { + for (var prop in hooks.prototype) { + object[prop] = hooks.prototype[prop]; + } + } + }; +})(); + diff --git a/src/layout.js b/src/layout.js index e6e9594..f0106c7 100644 --- a/src/layout.js +++ b/src/layout.js @@ -1,72 +1,115 @@ -EPUBJS.Renderer.prototype.formatSpread = function(){ - - var divisor = 2, - cutoff = 800; +EPUBJS.Layout = EPUBJS.Layout || {}; +EPUBJS.Layout.Reflowable = function(documentElement, _width, _height){ + var columnAxis = EPUBJS.core.prefixed('columnAxis'); + var columnGap = EPUBJS.core.prefixed('columnGap'); + var columnWidth = EPUBJS.core.prefixed('columnWidth'); + //-- Check the width and decied on columns - //-- Todo: a better place for this? - this.elWidth = this.iframe.clientWidth; - if(this.elWidth % 2 !== 0){ - this.elWidth -= 1; - } + var width = (_width % 2 == 0) ? _width : Math.floor(_width) - 1; + var section = Math.ceil(width / 8); + var gap = (section % 2 == 0) ? section : section - 1; - // this.gap = this.gap || Math.ceil(this.elWidth / 8); - this.gap = Math.ceil(this.elWidth / 8); + //-- Single Page + var spreadWidth = (width + gap); + var totalWidth, displayedPages; - if(this.gap % 2 !== 0){ - this.gap += 1; - } + documentElement.style.width = "auto"; //-- reset width for calculations - if(this.elWidth < cutoff || !this.book.settings.spreads) { - this.spread = false; //-- Single Page - divisor = 1; - this.colWidth = Math.floor(this.elWidth / divisor); - }else{ - this.spread = true; //-- Double Page - this.colWidth = Math.floor((this.elWidth - this.gap) / divisor); - } + documentElement.style.overflow = "hidden"; - this.spreadWidth = (this.colWidth + this.gap) * divisor; - // if(this.bodyEl) this.bodyEl.style.margin = 0; - // this.bodyEl.style.fontSize = localStorage.getItem("fontSize") || "medium"; - - //-- Clear Margins - if(this.bodyEl) this.bodyEl.style.margin = "0"; - - this.docEl.style.overflow = "hidden"; - - this.docEl.style.width = this.elWidth + "px"; + documentElement.style.width = width + "px"; //-- Adjust height - this.docEl.style.height = this.iframe.clientHeight + "px"; + documentElement.style.height = _height + "px"; //-- Add columns - this.docEl.style[EPUBJS.Renderer.columnAxis] = "horizontal"; - this.docEl.style[EPUBJS.Renderer.columnGap] = this.gap+"px"; - this.docEl.style[EPUBJS.Renderer.columnWidth] = this.colWidth+"px"; + documentElement.style[columnAxis] = "horizontal"; + documentElement.style[columnGap] = gap+"px"; + documentElement.style[columnWidth] = width+"px"; + totalWidth = documentElement.scrollWidth; + displayedPages = Math.ceil(totalWidth / spreadWidth); + + // documentElement.style.width = totalWidth + spreadWidth + "px"; + + return { + pageWidth : spreadWidth, + pageHeight : _height, + displayedPages : displayedPages + }; }; -EPUBJS.Renderer.prototype.fixedLayout = function(){ - this.paginated = false; +EPUBJS.Layout.ReflowableSpreads = function(documentElement, _width, _height){ + var columnAxis = EPUBJS.core.prefixed('columnAxis'); + var columnGap = EPUBJS.core.prefixed('columnGap'); + var columnWidth = EPUBJS.core.prefixed('columnWidth'); + + var divisor = 2, + cutoff = 800; - this.elWidth = this.iframe.width; - this.docEl.style.width = this.elWidth; - // this.setLeft(0); + //-- Check the width and decied on columns + var width = (_width % 2 == 0) ? _width : Math.floor(_width) - 1; + var section = Math.ceil(width / 8); + var gap = (section % 2 == 0) ? section : section - 1; - this.docEl.style.width = this.elWidth; + //-- Double Page + var colWidth = Math.floor((width - gap) / divisor); + var spreadWidth = (colWidth + gap) * divisor; + + var totalWidth, displayedPages; + + documentElement.style.width = "auto"; //-- reset width for calculations + + documentElement.style.overflow = "hidden"; + + documentElement.style.width = width + "px"; //-- Adjust height - this.docEl.style.height = "auto"; + documentElement.style.height = _height + "px"; + + //-- Add columns + documentElement.style[columnAxis] = "horizontal"; + documentElement.style[columnGap] = gap+"px"; + documentElement.style[columnWidth] = colWidth+"px"; + + totalWidth = documentElement.scrollWidth; + displayedPages = Math.ceil(totalWidth / spreadWidth); + + documentElement.style.width = totalWidth + spreadWidth + "px"; + + return { + pageWidth : spreadWidth, + pageHeight : _height, + displayedPages : displayedPages + }; +}; + +EPUBJS.Layout.Fixed = function(documentElement, _width, _height){ + var columnWidth = EPUBJS.core.prefixed('columnWidth'); + + var totalWidth = documentElement.scrollWidth; + var totalHeight = documentElement.scrollHeight; + + documentElement.style.width = _width; + + //-- Adjust height + documentElement.style.height = "auto"; //-- Remove columns - // this.docEl.style[EPUBJS.core.columnWidth] = "auto"; + documentElement.style[columnWidth] = "auto"; //-- Scroll - this.docEl.style.overflow = "auto"; - this.iframe.scrolling = "yes"; + documentElement.style.overflow = "auto"; + // this.iframe.scrolling = "yes"; // this.displayedPages = 1; + return { + pageWidth : totalWidth, + pageHeight : totalHeight, + displayedPages : 1 + }; + }; \ No newline at end of file diff --git a/src/navigation.js b/src/navigation.js index 293354c..e69de29 100644 --- a/src/navigation.js +++ b/src/navigation.js @@ -1,196 +0,0 @@ -EPUBJS.Renderer.prototype.page = function(pg){ - if(pg >= 1 && pg <= this.displayedPages){ - this.chapterPos = pg; - this.leftPos = this.spreadWidth * (pg-1); //-- pages start at 1 - this.setLeft(this.leftPos); - - this.currentLocationCfi = this.getPageCfi(); - - this.book.trigger("renderer:pageChanged", this.currentLocationCfi); - - // localStorage.setItem("chapterPos", pg); - return true; - } - //-- Return false if page is greater than the total - return false; -}; - -EPUBJS.Renderer.prototype.nextPage = function(){ - if(this.chapterPos < this.displayedPages){ - this.chapterPos++; - - this.leftPos += this.spreadWidth; - - this.setLeft(this.leftPos); - - this.currentLocationCfi = this.getPageCfi(); - - this.book.trigger("renderer:pageChanged", this.currentLocationCfi); - - - return this.chapterPos; - }else{ - return false; - } -}; - -EPUBJS.Renderer.prototype.prevPage = function(){ - if(this.chapterPos > 1){ - this.chapterPos--; - - this.leftPos -= this.spreadWidth; - - this.setLeft(this.leftPos); - - this.currentLocationCfi = this.getPageCfi(); - - this.book.trigger("renderer:pageChanged", this.currentLocationCfi); - - return this.chapterPos; - }else{ - return false; - } -}; - -EPUBJS.Renderer.prototype.gotoChapterEnd = function(){ - this.chapterEnd(); -}; - -EPUBJS.Renderer.prototype.chapterEnd = function(){ - this.page(this.displayedPages); -}; - -//-- Find a section by fragement id -EPUBJS.Renderer.prototype.section = function(fragment){ - var el = this.doc.getElementById(fragment), - left, pg; - - if(el){ - this.pageByElement(el); - } - -}; - -//-- Show the page containing an Element -EPUBJS.Renderer.prototype.pageByElement = function(el){ - var left, pg; - if(!el) return; - - left = this.leftPos + el.getBoundingClientRect().left; //-- Calculate left offset compaired to scrolled position - pg = Math.floor(left / this.spreadWidth) + 1; //-- pages start at 1 - this.page(pg); - -}; - - - -EPUBJS.Renderer.prototype.walk = function(node) { - var r, children, leng, - startNode = node, - prevNode, - stack = [startNode]; - - var STOP = 10000, ITER=0; - - while(!r && stack.length) { - - node = stack.shift(); - - if( this.isElementVisible(node) ) { - - r = node; - - } - - if(!r && node && node.childElementCount > 0){ - - children = node.children; - if (children && children.length) { - leng = children.length ? children.length : 0; - } else { - return r; - } - - for (var i = 0; i < leng; i++) { - if(children[i] != prevNode) stack.push(children[i]); - } - - } - - - if(!r && stack.length === 0 && startNode && startNode.parentNode !== null){ - - stack.push(startNode.parentNode); - prevNode = startNode; - startNode = startNode.parentNode; - } - - - ITER++; - if(ITER > STOP) { - console.error("ENDLESS LOOP"); - break; - } - - } - - return r; -}; - - -EPUBJS.Renderer.prototype.getPageCfi = function(){ - var prevEl = this.visibileEl; - this.visibileEl = this.findFirstVisible(prevEl); - - if(!this.visibileEl.id) { - this.visibileEl.id = "EPUBJS-PAGE-" + this.chapterPos; - } - - this.pageIds[this.chapterPos] = this.visibileEl.id; - - - return this.epubcfi.generateFragment(this.visibileEl, this.currentChapterCfi); - -}; - -EPUBJS.Renderer.prototype.gotoCfiFragment = function(cfi){ - var element; - - if(_.isString(cfi)){ - cfi = this.epubcfi.parse(cfi); - } - - element = this.epubcfi.getElement(cfi, this.doc); - this.pageByElement(element); -}; - -EPUBJS.Renderer.prototype.findFirstVisible = function(startEl){ - var el = startEl || this.bodyEl, - found; - - found = this.walk(el); - - if(found) { - return found; - }else{ - return startEl; - } - -}; - -EPUBJS.Renderer.prototype.isElementVisible = function(el){ - var rect; - - if(el && typeof el.getBoundingClientRect === 'function'){ - rect = el.getBoundingClientRect(); - - if( rect.width != 0 && - rect.height != 0 && - rect.left >= 0 && - rect.left < this.spreadWidth ) { - return true; - } - } - - return false; -}; diff --git a/src/parser.js b/src/parser.js index 4cdb130..f24da60 100644 --- a/src/parser.js +++ b/src/parser.js @@ -143,7 +143,7 @@ EPUBJS.Parser.prototype.metadata = function(xml){ metadata.rights = p.getElementText(xml, "rights"); metadata.modified_date = p.querySelectorText(xml, "meta[property='dcterms:modified']"); - metadata.layout = p.querySelectorText(xml, "meta[property='rendition:orientation']"); + metadata.layout = p.querySelectorText(xml, "meta[property='rendition:layout']"); metadata.orientation = p.querySelectorText(xml, "meta[property='rendition:orientation']"); metadata.spread = p.querySelectorText(xml, "meta[property='rendition:spread']"); // metadata.page_prog_dir = packageXml.querySelector("spine").getAttribute("page-progression-direction"); @@ -210,18 +210,25 @@ EPUBJS.Parser.prototype.spine = function(spineXml, manifest){ var selected = spineXml.getElementsByTagName("itemref"), items = Array.prototype.slice.call(selected); - + + var spineNodeIndex = Array.prototype.indexOf.call(spineXml.parentNode.childNodes, spineXml); + + var epubcfi = new EPUBJS.EpubCFI(); + //-- Add to array to mantain ordering and cross reference with manifest items.forEach(function(item, index){ var Id = item.getAttribute('idref'); + var cfiBase = epubcfi.generateChapter(spineNodeIndex, index, Id); var vert = { 'id' : Id, 'linear' : item.getAttribute('linear') || '', - 'properties' : manifest[Id].properties || '', + 'properties' : item.getAttribute('properties') || '', + 'manifestProperties' : manifest[Id].properties || '', 'href' : manifest[Id].href, 'url' : manifest[Id].url, - 'index' : index - }; + 'index' : index, + 'cfiBase' : cfiBase + }; spine.push(vert); }); diff --git a/src/render_iframe.js b/src/render_iframe.js new file mode 100644 index 0000000..1678c46 --- /dev/null +++ b/src/render_iframe.js @@ -0,0 +1,171 @@ +EPUBJS.Render.Iframe = function() { + this.iframe; + this.document; + this.window; + this.docEl; + this.bodyEl; + + this.leftPos = 0; + this.pageWidth = 0; +}; + +//-- Build up any html needed +EPUBJS.Render.Iframe.prototype.create = function(){ + this.iframe = document.createElement('iframe'); + this.iframe.id = "epubjs-iframe"; + this.iframe.scrolling = "no"; + + return this.iframe; +}; + +EPUBJS.Render.Iframe.prototype.load = function(url){ + var render = this, + deferred = new RSVP.defer(); + + this.leftPos = 0; + this.iframe.src = url; + + if(this.window) { + this.unload(); + } + + this.iframe.onload = function() { + render.document = render.iframe.contentDocument; + + render.docEl = render.document.documentElement; + render.bodyEl = render.document.body; + render.window = render.iframe.contentWindow; + + render.window.addEventListener("resize", render.resized.bind(render), false); + + //-- Clear Margins + if(render.bodyEl) render.bodyEl.style.margin = "0"; + + deferred.resolve(render.docEl); + }; + + this.iframe.onerror = function(e) { + console.error("Error Loading Contents", e); + } + return deferred.promise; +}; + +EPUBJS.Render.Iframe.prototype.resize = function(width, height){ + var iframeBox; + + if(!this.iframe) return; + + this.iframe.height = height; + + if(!isNaN(width) && width % 2 !== 0){ + width += 1; //-- Prevent cutting off edges of text in columns + } + + this.iframe.width = width; + + this.width = this.iframe.getBoundingClientRect().width; + this.height = this.iframe.getBoundingClientRect().height; +}; + + +EPUBJS.Render.Iframe.prototype.resized = function(e){ + this.width = this.iframe.getBoundingClientRect().width; + this.height = this.iframe.getBoundingClientRect().height; +}; + +EPUBJS.Render.Iframe.prototype.totalWidth = function(){ + return this.docEl.scrollWidth; +}; + +EPUBJS.Render.Iframe.prototype.totalHeight = function(){ + return this.docEl.scrollHeight; +}; + +EPUBJS.Render.Iframe.prototype.setPageDimensions = function(pageWidth, pageHeight){ + this.pageWidth = pageWidth; + this.pageHeight = pageHeight; + //-- Add a page to the width of the document to account an for odd number of pages + // this.docEl.style.width = this.docEl.scrollWidth + pageWidth + "px"; +}; + +EPUBJS.Render.Iframe.prototype.setLeft = function(leftPos){ + // this.bodyEl.style.marginLeft = -leftPos + "px"; + // this.docEl.style.marginLeft = -leftPos + "px"; + // this.docEl.style[EPUBJS.Render.Iframe.transform] = 'translate('+ (-leftPos) + 'px, 0)'; + this.document.defaultView.scrollTo(leftPos, 0); +}; + +// EPUBJS.Render.Iframe.prototype.nextPage = function(){ +// this.leftPos += this.pageWidth; +// this.setLeft(this.leftPos); +// }; +// +// EPUBJS.Render.Iframe.prototype.prevPage = function(){ +// this.leftPos -= this.pageWidth; +// this.setLeft(this.leftPos); +// }; + +EPUBJS.Render.Iframe.prototype.setStyle = function(style, val, prefixed){ + if(prefixed) { + style = EPUBJS.core.prefixed(style); + } + + if(this.bodyEl) this.bodyEl.style[style] = val; +}; + +EPUBJS.Render.Iframe.prototype.removeStyle = function(style){ + + if(this.bodyEl) this.bodyEl.style[style] = ''; + +}; + +EPUBJS.Render.Iframe.prototype.page = function(pg){ + this.leftPos = this.pageWidth * (pg-1); //-- pages start at 1 + this.setLeft(this.leftPos); +}; + +//-- Show the page containing an Element +EPUBJS.Render.Iframe.prototype.getPageNumberByElement = function(el){ + var left, pg; + if(!el) return; + + left = this.leftPos + el.getBoundingClientRect().left; //-- Calculate left offset compaired to scrolled position + + pg = Math.floor(left / this.pageWidth) + 1; //-- pages start at 1 + + return pg; +}; + +//-- +EPUBJS.Render.Iframe.prototype.getBaseElement = function(){ + return this.bodyEl; +}; + +EPUBJS.Render.Iframe.prototype.isElementVisible = function(el){ + var rect; + + if(el && typeof el.getBoundingClientRect === 'function'){ + rect = el.getBoundingClientRect(); + if( rect.width != 0 && + rect.height != 0 && + rect.left >= 0 && + rect.left < this.pageWidth ) { + return true; + } + } + + return false; +}; + + +EPUBJS.Render.Iframe.prototype.scroll = function(bool){ + if(bool) { + this.iframe.scrolling = "yes"; + } else { + this.iframe.scrolling = "no"; + } +}; + +EPUBJS.Render.Iframe.prototype.unload = function(){ + this.window.removeEventListener("resize", this.resized); +}; \ No newline at end of file diff --git a/src/renderer.js b/src/renderer.js index b3e6dd4..9434b6b 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -1,94 +1,420 @@ -EPUBJS.Renderer = function(book) { - this.el = book.element; - this.book = book; - - // this.settings = book.settings; - this.caches = {}; - - this.crossBrowserColumnCss(); - - this.epubcfi = new EPUBJS.EpubCFI(); - - this.initialize(); - +EPUBJS.Renderer = function(type, beforeDisplay) { + this.beforeDisplay = beforeDisplay; + this.listenedEvents = ["keydown", "keyup", "keypressed", "mouseup", "mousedown", "click"]; - this.listeners(); + + if(type && typeof(EPUBJS.Render[type]) != "undefined"){ + this.render = new EPUBJS.Render[type]; + } else { + console.error("Not a Valid Rendering Method"); + } + + this.caches = {}; + + this.epubcfi = new EPUBJS.EpubCFI(); + + this.spreads = true; + this.resized = _.throttle(this.onResized.bind(this), 10); +}; //-- Renderer events for listening - /* - renderer:resized - renderer:chapterDisplayed - renderer:chapterUnloaded - */ -}; +EPUBJS.Renderer.prototype.Events = [ + "renderer:keydown", + "renderer:keyup", + "renderer:keypressed", + "renderer:mouseup", + "renderer:mousedown", + "renderer:click", + "renderer:selected", + "renderer:chapterUnloaded", + "renderer:chapterDisplayed", + "renderer:pageChanged", + "renderer:resized", + "renderer:spreads" +]; -//-- Build up any html needed -EPUBJS.Renderer.prototype.initialize = function(){ - this.iframe = document.createElement('iframe'); - //this.iframe.id = "epubjs-iframe"; - this.iframe.scrolling = "no"; +EPUBJS.Renderer.prototype.initialize = function(element, width, height){ + this.container = element; + this.element = this.render.create(); - if(this.book.settings.width || this.book.settings.height){ - this.resizeIframe(this.book.settings.width || this.el.clientWidth, this.book.settings.height || this.el.clientHeight); + this.initWidth = width; + this.initHeight = height; + + this.width = width || this.container.clientWidth; + this.height = height || this.container.clientHeight; + + this.container.appendChild(this.element); + + if(width && height){ + this.render.resize(this.width, this.height); } else { - // this.resizeIframe(false, this.el.clientWidth, this.el.clientHeight); - this.resizeIframe('100%', '100%'); - - // this.on("renderer:resized", this.resizeIframe, this); - } - - - this.el.appendChild(this.iframe); -}; - -//-- Listeners for browser events -EPUBJS.Renderer.prototype.listeners = function(){ - - this.resized = _.throttle(this.onResized.bind(this), 10); - - // window.addEventListener("hashchange", book.route.bind(this), false); - - this.book.registerHook("beforeChapterDisplay", this.replaceLinks.bind(this), true); - - if(this.determineStore()) { - - this.book.registerHook("beforeChapterDisplay", [ - EPUBJS.replace.head, - EPUBJS.replace.resources, - EPUBJS.replace.svg - ], true); - + this.render.resize('100%', '100%'); } }; -EPUBJS.Renderer.prototype.chapter = function(chapter){ + +EPUBJS.Renderer.prototype.displayChapter = function(chapter, globalLayout){ var renderer = this, - store = false; - - if(this.book.settings.contained) store = this.book.zip; - // if(this.settings.stored) store = this.storage; - + store = false; + + if(this.currentChapter) { this.currentChapter.unload(); - + this.render.window.removeEventListener("resize", this.resized); + this.removeEventListeners(); + this.removeSelectionListeners(); this.trigger("renderer:chapterUnloaded"); - this.book.trigger("renderer:chapterUnloaded"); } - + this.currentChapter = chapter; this.chapterPos = 1; - this.pageIds = {}; - this.leftPos = 0; + + this.currentChapterCfiBase = chapter.cfiBase; + + this.settings = this.reconcileLayoutSettings(globalLayout, chapter.properties); - this.currentChapterCfi = this.epubcfi.generateChapter(this.book.spineNodeIndex, chapter.spinePos, chapter.id); - this.visibileEl = false; - return chapter.url(store). + + return chapter.url(). then(function(url) { - return renderer.setIframeSrc(url); + return renderer.load(url); }); + +}; + +EPUBJS.Renderer.prototype.reconcileLayoutSettings = function(global, chapter){ + var layoutMethod = "ReflowableSpreads"; + var properties = chapter.split(' '); + var settings = {}; + var spreads = true; + + //-- Get the global defaults + for (var attr in global) { + if (global.hasOwnProperty(attr)){ + settings[attr] = global[attr]; + } + } + //-- Get the chapter's display type + properties.forEach(function(prop){ + var rendition = prop.replace("rendition:", ''); + var split = rendition.indexOf("-"); + var property, value; + + if(split != -1){ + property = rendition.slice(0, split); + value = rendition.slice(split+1); + + settings[property] = value; + } + }); + + return settings; +}; + +EPUBJS.Renderer.prototype.determineLayout = function(settings){ + var layoutMethod = "ReflowableSpreads"; + + if(settings.layout === "pre-paginated") { + layoutMethod = "Fixed"; + this.render.scroll(true); + return EPUBJS.Layout[layoutMethod]; + } + + if(settings.layout === "reflowable" && settings.spread === "none") { + layoutMethod = "Reflowable"; + this.render.scroll(false); + this.trigger("renderer:spreads", false); + return EPUBJS.Layout[layoutMethod]; + } + if(settings.layout === "reflowable" && settings.spread === "both") { + layoutMethod = "ReflowableSpreads"; + this.render.scroll(false); + this.trigger("renderer:spreads", true); + return EPUBJS.Layout[layoutMethod]; + } + + // Reflowable Auto adjustments for width + if(settings.layout === "reflowable" && settings.spread === "auto"){ + spreads = this.determineSpreads(this.minSpreadWidth); + if(spreads){ + layoutMethod = "ReflowableSpreads"; + this.trigger("renderer:spreads", true); + } else { + layoutMethod = "Reflowable"; + this.trigger("renderer:spreads", false); + } + this.render.scroll(false); + return EPUBJS.Layout[layoutMethod]; + } + + +}; + +EPUBJS.Renderer.prototype.load = function(url){ + var deferred = new RSVP.defer(); + var loaded; + + this.layoutMethod = this.determineLayout(this.settings); + + this.visible(false); + + loaded = this.render.load(url); + + loaded.then(function(contents) { + this.contents = contents; + this.doc = this.render.document; + + + + if(!this.initWidth && !this.initHeight){ + this.render.window.addEventListener("resize", this.resized, false); + } + + this.addEventListeners(); + this.addSelectionListeners(); + + //-- Trigger registered hooks before displaying + this.beforeDisplay(function(){ + var msg = this.currentChapter; + + msg.cfi = this.currentLocationCfi = this.getPageCfi(); + + this.trigger("renderer:chapterDisplayed", msg); + this.trigger("renderer:pageChanged", this.currentLocationCfi); + + this.layout = this.layoutMethod(contents, this.render.width, this.render.height); + this.updatePages(this.layout); + + this.visible(true); + + deferred.resolve(this); //-- why does this return the renderer? + }.bind(this)); + + }.bind(this)); + + return deferred.promise; +}; + +EPUBJS.Renderer.prototype.updatePages = function(layout){ + this.displayedPages = layout.displayedPages; + this.currentChapter.pages = layout.displayedPages; + this.render.setPageDimensions(layout.pageWidth, layout.pageHeight); +}; + +EPUBJS.Renderer.prototype.reformat = function(){ + var renderer = this; + if(!this.contents) return; + + this.layout = this.layoutMethod(this.contents, this.render.width, this.render.height); + this.updatePages(this.layout); + + setTimeout(function(){ + + //-- Go to current page after formating + if(renderer.currentLocationCfi){ + renderer.gotoCfi(renderer.currentLocationCfi); + } + + }, 10); + +}; + +EPUBJS.Renderer.prototype.visible = function(bool){ + if(typeof(bool) === "undefined") { + return this.container.style.visibility; + } + + if(bool === true){ + this.container.style.visibility = "visible"; + }else if(bool === false){ + this.container.style.visibility = "hidden"; + } +}; + +EPUBJS.Renderer.prototype.remove = function() { + if(this.renderer.window) { + this.render.unload(); + this.render.window.removeEventListener("resize", this.resized); + this.removeEventListeners(); + this.removeSelectionListeners(); + } + + this.container.removeChild(this.element); +}; + +//-- STYLES + +EPUBJS.Renderer.prototype.applyStyles = function(styles) { + for (var style in styles) { + this.render.setStyle(style, styles[style]); + } +}; + +EPUBJS.Renderer.prototype.setStyle = function(style, val, prefixed){ + this.render.setStyle(style, val, prefixed); +}; + +EPUBJS.Renderer.prototype.removeStyle = function(style){ + this.render.removeStyle(style); +}; + +//-- NAVIGATION + +//-- Show the page containing an Element +EPUBJS.Renderer.prototype.pageByElement = function(el){ + var pg; + if(!el) return; + + pg = this.render.getPageNumberByElement(el); + this.page(pg); +}; + +EPUBJS.Renderer.prototype.page = function(pg){ + if(pg >= 1 && pg <= this.displayedPages){ + this.chapterPos = pg; + + this.render.page(pg); + + this.currentLocationCfi = this.getPageCfi(); + this.trigger("renderer:pageChanged", this.currentLocationCfi); + + return true; + } + //-- Return false if page is greater than the total + return false; +}; + +EPUBJS.Renderer.prototype.nextPage = function(){ + var pg = this.chapterPos + 1; + if(pg <= this.displayedPages){ + this.chapterPos = pg; + + this.render.page(pg); + + this.currentLocationCfi = this.getNextPageCfi(); + this.trigger("renderer:pageChanged", this.currentLocationCfi); + + return true; + } + //-- Return false if page is greater than the total + return false; +}; + +EPUBJS.Renderer.prototype.prevPage = function(){ + return this.page(this.chapterPos - 1); +}; + +EPUBJS.Renderer.prototype.gotoChapterEnd = function(){ + this.chapterEnd(); +}; + +EPUBJS.Renderer.prototype.chapterEnd = function(){ + this.page(this.displayedPages); +}; + +//-- Find a section by fragement id +EPUBJS.Renderer.prototype.section = function(fragment){ + var el = this.doc.getElementById(fragment), + left, pg; + + if(el){ + this.pageByElement(el); + } + +}; + + +EPUBJS.Renderer.prototype.walk = function(node) { + var r, children, leng, + startNode = node, + prevNode, + stack = [startNode]; + + var STOP = 10000, ITER=0; + + while(!r && stack.length) { + + node = stack.shift(); + if( this.render.isElementVisible(node) ) { + r = node; + } + + if(!r && node && node.childElementCount > 0){ + children = node.children; + if (children && children.length) { + leng = children.length ? children.length : 0; + } else { + return r; + } + for (var i = 0; i < leng; i++) { + if(children[i] != prevNode) stack.push(children[i]); + } + } + + if(!r && stack.length === 0 && startNode && startNode.parentNode !== null){ + stack.push(startNode.parentNode); + prevNode = startNode; + startNode = startNode.parentNode; + } + + + ITER++; + if(ITER > STOP) { + console.error("ENDLESS LOOP"); + break; + } + + } + + return r; +}; + + +EPUBJS.Renderer.prototype.getPageCfi = function(){ + // var prevEl = this.visibileEl; + this.visibileEl = this.findFirstVisible(); + + // if(!this.visibileEl.id) { + // this.visibileEl.id = "EPUBJS-PAGE-" + this.chapterPos; + // } + // + // this.pageIds[this.chapterPos] = this.visibileEl.id; + + return this.epubcfi.generateFragment(this.visibileEl, this.currentChapter.cfiBase); + +}; + +EPUBJS.Renderer.prototype.getNextPageCfi = function(){ + var prevEl = this.visibileEl; + this.visibileEl = this.findFirstVisible(prevEl); + + return this.epubcfi.generateFragment(this.visibileEl, this.currentChapter.cfiBase); +}; + +EPUBJS.Renderer.prototype.gotoCfi = function(cfi){ + var element; + + if(_.isString(cfi)){ + cfi = this.epubcfi.parse(cfi); + } + + element = this.epubcfi.getElement(cfi, this.doc); + this.pageByElement(element); +}; + +EPUBJS.Renderer.prototype.findFirstVisible = function(startEl){ + var el = startEl || this.render.getBaseElement(); + var found; + found = this.walk(el); + + if(found) { + return found; + }else{ + return startEl; + } + }; /* @@ -110,34 +436,58 @@ EPUBJS.Renderer.prototype.hideHashChanges = function(){ */ //-- Listeners for events in the frame -EPUBJS.Renderer.prototype.addIframeListeners = function(){ + +EPUBJS.Renderer.prototype.onResized = function(e){ + var spreads; + + this.width = this.container.clientWidth; + this.height = this.container.clientHeight; + + spreads = this.determineSpreads(this.minSpreadWidth); + if(spreads != this.spreads){ + this.spreads = spreads; + this.layoutMethod = this.determineLayout(this.settings); + } + + if(this.contents){ + this.reformat(); + } + + this.trigger("renderer:resized", { + width: this.width, + height: this.height + }); + +}; + +EPUBJS.Renderer.prototype.addEventListeners = function(){ this.listenedEvents.forEach(function(eventName){ - this.doc.addEventListener(eventName, this.triggerEvent.bind(this), false); + this.render.document.addEventListener(eventName, this.triggerEvent.bind(this), false); }, this); }; -EPUBJS.Renderer.prototype.removeIframeListeners = function(){ +EPUBJS.Renderer.prototype.removeEventListeners = function(){ this.listenedEvents.forEach(function(eventName){ - this.doc.removeEventListener(eventName, this.triggerEvent, false); + this.render.document.removeEventListener(eventName, this.triggerEvent, false); }, this); }; EPUBJS.Renderer.prototype.triggerEvent = function(e){ - this.book.trigger("renderer:"+e.type, e); + this.trigger("renderer:"+e.type, e); }; EPUBJS.Renderer.prototype.addSelectionListeners = function(){ - this.doc.addEventListener("selectionchange", this.onSelectionChange.bind(this), false); - this.contentWindow.addEventListener("mouseup", this.onMouseUp.bind(this), false); + this.render.document.addEventListener("selectionchange", this.onSelectionChange.bind(this), false); + this.render.window.addEventListener("mouseup", this.onMouseUp.bind(this), false); }; EPUBJS.Renderer.prototype.removeSelectionListeners = function(){ this.doc.removeEventListener("selectionchange", this.onSelectionChange, false); - this.contentWindow.removeEventListener("mouseup", this.onMouseUp, false); + this.render.window.removeEventListener("mouseup", this.onMouseUp, false); }; EPUBJS.Renderer.prototype.onSelectionChange = function(e){ @@ -147,246 +497,133 @@ EPUBJS.Renderer.prototype.onSelectionChange = function(e){ EPUBJS.Renderer.prototype.onMouseUp = function(e){ var selection; if(this.highlighted) { - selection = this.contentWindow.getSelection(); - this.book.trigger("renderer:selected", selection); + selection = this.render.window.getSelection(); + this.trigger("renderer:selected", selection); this.highlighted = false; } }; -EPUBJS.Renderer.prototype.onResized = function(e){ - - var msg = { - width: this.iframe.clientWidth, - height: this.iframe.clientHeight - }; - - if(this.doc){ - this.reformat(); - } - this.trigger("renderer:resized", msg); - this.book.trigger("book:resized", msg); - - - +//-- Spreads +// EPUBJS.Renderer.prototype.useSpreads = function(spreads){ +// var prevSpreads = this.spreads; +// this.spreads = spreads; +// +// if(this.contents && (this.spreads != prevSpreads)) { +// this.layoutMethod = this.determineLayout(this.currentChapter.properties); +// } +// }; + +EPUBJS.Renderer.prototype.setMinSpreadWidth = function(width){ + this.minSpreadWidth = width; }; -EPUBJS.Renderer.prototype.reformat = function(){ - var renderer = this; - - //-- reformat - if(renderer.book.settings.fixedLayout) { - renderer.fixedLayout(); - } else { - renderer.formatSpread(); +EPUBJS.Renderer.prototype.determineSpreads = function(cutoff){ + if(this.width < cutoff || !cutoff) { + return false; //-- Single Page + }else{ + return true; //-- Double Page } - - setTimeout(function(){ - - //-- re-calc number of pages - renderer.calcPages(); - - - //-- Go to current page after resize - if(renderer.currentLocationCfi){ - renderer.gotoCfiFragment(renderer.currentLocationCfi); +}; + +//-- Content Replacements + +EPUBJS.Renderer.prototype.replace = function(query, func, finished, progress){ + var items = this.contents.querySelectorAll(query), + resources = Array.prototype.slice.call(items), + count = resources.length, + after = function(result, full){ + count--; + if(progress) progress(result, full, count); + if(count <= 0 && finished) finished(true); + }; + + if(count === 0) { + finished(false); + return; + } + resources.forEach(function(item){ + + func(item, after); + + }.bind(this)); + +}; + +EPUBJS.Renderer.prototype.replaceWithStored = function(query, attr, func, callback) { + var _oldUrls, + _newUrls = {}, + _store = this.currentChapter.store, + _cache = this.caches[query], + _uri = EPUBJS.core.uri(this.currentChapter.absolute), + _chapterBase = _uri.base, + _attr = attr, + _wait = 2000, + progress = function(url, full, count) { + _newUrls[full] = url; + }, + finished = function(notempty) { + if(callback) callback(); + + _.each(_oldUrls, function(url){ + _store.revokeUrl(url); + }); + + _cache = _newUrls; + }; + + if(!_store) return; + + if(!_cache) _cache = {}; + _oldUrls = _.clone(_cache); + + this.replace(query, function(link, done){ + var src = link.getAttribute(_attr), + full = EPUBJS.core.resolveUrl(_chapterBase, src); + + var replaceUrl = function(url) { + var timeout; + + link.onload = function(){ + clearTimeout(timeout); + done(url, full); + }; + + link.onerror = function(e){ + clearTimeout(timeout); + done(url, full); + console.error(e); + }; + + if(query == "image") { + //-- SVG needs this to trigger a load event + link.setAttribute("externalResourcesRequired", "true"); + } + + if(query == "link[href]") { + //-- Only Stylesheet links seem to have a load events, just continue others + done(url, full); + } + + link.setAttribute(_attr, url); + + //-- If elements never fire Load Event, should continue anyways + timeout = setTimeout(function(){ + done(url, full); + }, _wait); + + }; + + if(full in _oldUrls){ + replaceUrl(_oldUrls[full]); + _newUrls[full] = _oldUrls[full]; + delete _oldUrls[full]; + }else{ + func(_store, full, replaceUrl, link); } - - }, 10); - - + + }, finished, progress); }; -EPUBJS.Renderer.prototype.resizeIframe = function(width, height){ - - this.iframe.height = height; - - if(!isNaN(width) && width % 2 !== 0){ - width += 1; //-- Prevent cutting off edges of text in columns - } - - this.iframe.width = width; - - this.onResized(); - -}; - - -EPUBJS.Renderer.prototype.crossBrowserColumnCss = function(){ - - EPUBJS.Renderer.columnAxis = EPUBJS.core.prefixed('columnAxis'); - EPUBJS.Renderer.columnGap = EPUBJS.core.prefixed('columnGap'); - EPUBJS.Renderer.columnWidth = EPUBJS.core.prefixed('columnWidth'); - EPUBJS.Renderer.transform = EPUBJS.core.prefixed('transform'); - -}; - -EPUBJS.Renderer.prototype.setIframeSrc = function(url){ - var renderer = this, - deferred = new RSVP.defer(); - - this.visible(false); - - this.iframe.src = url; - - this.iframe.onload = function() { - renderer.doc = renderer.iframe.contentDocument; - renderer.docEl = renderer.doc.documentElement; - renderer.headEl = renderer.doc.head; - renderer.bodyEl = renderer.doc.body; - renderer.contentWindow = renderer.iframe.contentWindow; - - renderer.applyStyles(); - renderer.applyHeadTags(); - - if(renderer.book.settings.fixedLayout) { - renderer.fixedLayout(); - } else { - renderer.formatSpread(); - } - - //-- Trigger registered hooks before displaying - renderer.beforeDisplay(function(){ - var msg = renderer.currentChapter; - - renderer.calcPages(); - - deferred.resolve(renderer); - - msg.cfi = renderer.currentLocationCfi = renderer.getPageCfi(); - - renderer.trigger("renderer:chapterDisplayed", msg); - renderer.book.trigger("renderer:chapterDisplayed", msg); - renderer.book.trigger("renderer:pageChanged", renderer.currentLocationCfi); - - renderer.visible(true); - - }); - - renderer.contentWindow.addEventListener("resize", renderer.onResized.bind(renderer), false); - renderer.addIframeListeners(); - renderer.addSelectionListeners(); - }; - - return deferred.promise; -}; - - - - -EPUBJS.Renderer.prototype.setStyle = function(style, val, prefixed){ - if(prefixed) { - style = EPUBJS.core.prefixed(style); - } - - if(this.bodyEl) this.bodyEl.style[style] = val; - -}; - -EPUBJS.Renderer.prototype.removeStyle = function(style){ - - if(this.bodyEl) this.bodyEl.style[style] = ''; - -}; - -EPUBJS.Renderer.prototype.applyStyles = function() { - var styles = this.book.settings.styles; - - for (var style in styles) { - this.setStyle(style, styles[style]); - } -}; - -EPUBJS.Renderer.prototype.addHeadTag = function(tag, attrs) { - var s = document.createElement(tag); - - for(attr in attrs) { - s[attr] = attrs[attr]; - } - this.headEl.appendChild(s); -} - -EPUBJS.Renderer.prototype.applyHeadTags = function() { - - var headTags = this.book.settings.headTags; - - for ( var headTag in headTags ) { - this.addHeadTag(headTag, headTags[headTag]) - } -}; - -EPUBJS.Renderer.prototype.gotoChapterEnd = function(){ - this.chapterEnd(); -}; - -EPUBJS.Renderer.prototype.visible = function(bool){ - if(typeof(bool) === "undefined") { - return this.iframe.style.visibility; - } - - if(bool === true){ - this.iframe.style.visibility = "visible"; - }else if(bool === false){ - this.iframe.style.visibility = "hidden"; - } -}; - -EPUBJS.Renderer.prototype.calcPages = function() { - - if(this.docEl) this.totalWidth = this.docEl.scrollWidth; - - this.displayedPages = Math.ceil(this.totalWidth / this.spreadWidth); - - this.currentChapter.pages = this.displayedPages; -}; - -EPUBJS.Renderer.prototype.setLeft = function(leftPos){ - // this.bodyEl.style.marginLeft = -leftPos + "px"; - // this.docEl.style.marginLeft = -leftPos + "px"; - // this.docEl.style[EPUBJS.Renderer.transform] = 'translate('+ (-leftPos) + 'px, 0)'; - this.doc.defaultView.scrollTo(leftPos, 0); -}; - -EPUBJS.Renderer.prototype.determineStore = function(callback){ - if(this.book.fromStorage) { - - //-- Filesystem api links are relative, so no need to replace them - if(this.book.storage.getStorageType() == "filesystem") { - return false; - } - - return this.book.store; - - } else if(this.book.contained) { - - return this.book.zip; - - } else { - - return false; - - } -}; - - -EPUBJS.Renderer.prototype.beforeDisplay = function(callback){ - this.book.triggerHooks("beforeChapterDisplay", callback.bind(this), this); -}; - - - -EPUBJS.Renderer.prototype.height = function(el){ - return this.docEl.offsetHeight; -}; - -EPUBJS.Renderer.prototype.remove = function() { - this.contentWindow.removeEventListener("resize", this.resize); - this.removeIframeListeners(); - this.removeSelectionListeners(); - this.el.removeChild(this.iframe); -}; - - - //-- Enable binding events to Renderer RSVP.EventTarget.mixin(EPUBJS.Renderer.prototype); diff --git a/src/replace.js b/src/replace.js index d3b61dc..2e87c29 100644 --- a/src/replace.js +++ b/src/replace.js @@ -1,6 +1,33 @@ var EPUBJS = EPUBJS || {}; EPUBJS.replace = {}; +//-- Replaces the relative links within the book to use our internal page changer +EPUBJS.replace.hrefs = function(callback, renderer){ + var replacments = function(link, done){ + var href = link.getAttribute("href"), + relative = href.search("://"), + fragment = href[0] == "#"; + + if(relative != -1){ + + link.setAttribute("target", "_blank"); + + }else{ + + link.onclick = function(){ + renderer.book.goto(href); + return false; + }; + + } + done(); + + }; + + renderer.replace("a[href]", replacments, callback); + +}; + EPUBJS.replace.head = function(callback, renderer) { renderer.replaceWithStored("link[href]", "href", EPUBJS.replace.links, callback); @@ -73,7 +100,7 @@ EPUBJS.replace.stylesheets = function(_store, full) { EPUBJS.replace.cssUrls = function(_store, base, text){ var deferred = new RSVP.defer(), promises = [], - matches = text.match(/url\(\'?\"?([^\'|^\"]*)\'?\"?\)/g); + matches = text.match(/url\(\'?\"?([^\'|^\"|^\)]*)\'?\"?\)/g); if(!_store) return; @@ -86,8 +113,6 @@ EPUBJS.replace.cssUrls = function(_store, base, text){ var full = EPUBJS.core.resolveUrl(base, str.replace(/url\(|[|\)|\'|\"]/g, '')); var replaced = _store.getUrl(full).then(function(url){ text = text.replace(str, 'url("'+url+'")'); - }, function(e) { - console.error(e); }); promises.push(replaced); diff --git a/src/unarchiver.js b/src/unarchiver.js index 146c1ae..7a6341f 100644 --- a/src/unarchiver.js +++ b/src/unarchiver.js @@ -55,8 +55,11 @@ EPUBJS.Unarchiver.prototype.getUrl = function(url, mime){ var _URL = window.URL || window.webkitURL || window.mozURL; if(!entry) { - console.error("File not found in the epub:", url); - return; + deferred.reject({ + message : "File not found in the epub: " + url, + stack : new Error().stack + }); + return deferred.promise; } if(url in this.urlCache) {