From 3fb0b5de30b95f8ed13b606a242090984942f77b Mon Sep 17 00:00:00 2001 From: Fred Chasen Date: Tue, 21 Jan 2014 20:34:29 -0800 Subject: [PATCH] comments and js linted --- Gruntfile.js | 33 ++++- hooks/default/endnotes.js | 4 +- hooks/default/transculsions.js | 2 +- package.json | 3 +- src/book.js | 132 +++++++++--------- src/core.js | 6 +- src/hooks.js | 4 +- src/layout.js | 58 ++++---- src/parser.js | 11 +- src/render_iframe.js | 44 +++--- src/renderer.js | 242 ++++++++++++++++++--------------- tests/render.js | 2 +- 12 files changed, 307 insertions(+), 234 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index d5305bd..578d492 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -36,12 +36,43 @@ module.exports = function(grunt) { 'build/libs/screenfull.min.js': ['libs/screenfull.min.js'] } } + }, + jshint: { + all: ['src/**/*.js'],//, 'reader/**/*.js'] + options : { + // Environments + "browser": true, + "devel": true, + "worker": true, + + // Enforcing + //"maxlen": 80, + //"quotmark": "single", + "trailing": true, + "strict": false, + + // Relaxing + "boss": true, + "funcscope": true, + "globalstrict": true, + "loopfunc": true, + "maxerr": 1000, + "nonstandard": true, + "sub": true, + "validthis": true, + + "globals": { + "_": false, + "define" : false, + "module" : false + } + } } }); grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-uglify'); - + grunt.loadNpmTasks('grunt-contrib-jshint'); // Default task(s). grunt.registerTask('default', ['concat', 'uglify']); }; diff --git a/hooks/default/endnotes.js b/hooks/default/endnotes.js index 781e7c5..2146ffe 100644 --- a/hooks/default/endnotes.js +++ b/hooks/default/endnotes.js @@ -67,8 +67,8 @@ EPUBJS.Hooks.register("beforeChapterDisplay").endnotes = function(callback, rend //-- Add hide on page change // chapter.book.listenUntil("book:pageChanged", "book:chapterDestroy", hidePop); // chapter.book.listenUntil("book:pageChanged", "book:chapterDestroy", offPop); - chapter.book.on("renderer:pageChanged", hidePop, this); - chapter.book.on("renderer:pageChanged", offPop, this); + renderer.on("renderer:pageChanged", hidePop, this); + renderer.on("renderer:pageChanged", offPop, this); // chapter.book.on("renderer:chapterDestroy", hidePop, this); } diff --git a/hooks/default/transculsions.js b/hooks/default/transculsions.js index 99d1a40..2c2a569 100644 --- a/hooks/default/transculsions.js +++ b/hooks/default/transculsions.js @@ -40,7 +40,7 @@ EPUBJS.Hooks.register("beforeChapterDisplay").transculsions = function(callback, //-- resize event - chapter.book.listenUntil("book:resized", "book:chapterDestroy", size); + renderer.listenUntil("renderer:resized", "renderer:chapterUnloaded", size); iframe.src = src; diff --git a/package.json b/package.json index afb4771..79e35a4 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "optimist": "~0.6.0", "portfinder": "~0.2.1", "grunt-contrib-concat": "~0.3.0", - "grunt-contrib-uglify": "~0.2.2" + "grunt-contrib-uglify": "~0.2.2", + "grunt-contrib-jshint": "~0.8.0" } } diff --git a/src/book.js b/src/book.js index ed16aa5..5f3c2bb 100644 --- a/src/book.js +++ b/src/book.js @@ -1,7 +1,7 @@ EPUBJS.Book = function(options){ var book = this; - + this.settings = _.defaults(options || {}, { bookPath : null, bookKey : null, @@ -11,11 +11,11 @@ EPUBJS.Book = function(options){ saved : false, online : true, contained : false, - width : false, - height: false, + width : null, + height: null, spread: null, layout : null, - orientation : null, + orientation : null, minSpreadWidth: 800, //-- overridden by spread: none (never) / both (always) version: 1, restore: false, @@ -25,7 +25,7 @@ EPUBJS.Book = function(options){ headTags : {}, withCredentials: false, }); - + this.settings.EPUBJSVERSION = EPUBJS.VERSION; this.spinePos = 0; @@ -40,13 +40,17 @@ EPUBJS.Book = function(options){ book:pageChanged */ - EPUBJS.Hooks.mixin(this); - //-- Get pre-registered hooks - this.getHooks("beforeChapterDisplay"); + //-- Adds Hook methods to the Book prototype + // Hooks will all return before triggering the callback. + // EPUBJS.Hooks.mixin(this); + //-- Get pre-registered hooks for events + // this.getHooks("beforeChapterDisplay"); this.online = this.settings.online || navigator.onLine; this.networkListeners(); - + + this.store = false; //-- False if not using storage; + //-- Determine storage method //-- Override options: none | ram | websqldatabase | indexeddb | filesystem @@ -79,8 +83,14 @@ EPUBJS.Book = function(options){ this._rendering = false; this._displayQ = []; - this.renderer = new EPUBJS.Renderer(this.settings.renderer, this.beforeDisplay.bind(this)); + /** + * Creates a new renderer. + * The renderer will handle displaying the content using the method provided in the settings + */ + this.renderer = new EPUBJS.Renderer(this.settings.renderer); + //-- Set the width at which to switch from spreads to single pages this.renderer.setMinSpreadWidth(this.settings.minSpreadWidth); + //-- Pass through the renderer events this.listenToRenderer(this.renderer); this.defer_opened = new RSVP.defer(); @@ -89,7 +99,6 @@ EPUBJS.Book = function(options){ if(typeof this.settings.bookPath === 'string') { this.open(this.settings.bookPath, this.settings.reload); } - window.addEventListener("beforeunload", this.unload.bind(this), false); @@ -110,21 +119,20 @@ EPUBJS.Book.prototype.open = function(bookPath, forceReload){ this.bookUrl = this.urlFrom(bookPath); if(this.settings.contained || this.isContained(bookPath)){ - + this.settings.contained = this.contained = true; - + this.bookUrl = ''; - - // return; //-- TODO: this need to be fixed and tested before enabling + epubpackage = this.unarchive(bookPath). then(function(){ return book.loadPackage(); }); - + } else { epubpackage = this.loadPackage(); } - + if(this.settings.restore && !forceReload){ //-- Will load previous package json, or re-unpack if error epubpackage.then(function(packageXml) { @@ -201,7 +209,9 @@ EPUBJS.Book.prototype.unpack = function(packageXml){ book.spine = book.contents.spine; book.spineIndexByURL = book.contents.spineIndexByURL; book.metadata = book.contents.metadata; - book.setBookKey(book.metadata.identifier); + if(!book.settings.bookKey) { + book.settings.bookKey = book.generateBookKey(book.metadata.identifier); + } //-- Set Globbal Layout setting based on metadata book.globalLayoutProperties = book.parseLayoutProperties(book.metadata); @@ -274,6 +284,7 @@ EPUBJS.Book.prototype.networkListeners = function(){ }; +// Listen to all events the renderer triggers and pass them as book events EPUBJS.Book.prototype.listenToRenderer = function(renderer){ var book = this; renderer.Events.forEach(function(eventName){ @@ -347,7 +358,7 @@ EPUBJS.Book.prototype.unarchive = function(bookPath){ // } this.zip = new EPUBJS.Unarchiver(); - + this.store = this.zip; // Use zip storaged in ram return this.zip.openZip(bookPath); }; @@ -374,13 +385,7 @@ EPUBJS.Book.prototype.isSaved = function(bookKey) { } }; -EPUBJS.Book.prototype.setBookKey = function(identifier){ - if(!this.settings.bookKey) { - this.settings.bookKey = this.generateBookKey(identifier); - } - return this.settings.bookKey; -}; - +// Generates the Book Key using the identifer in the manifest or other string provided EPUBJS.Book.prototype.generateBookKey = function(identifier){ return "epubjs:" + EPUBJS.VERSION + ":" + window.location.host + ":" + identifier; }; @@ -432,28 +437,28 @@ EPUBJS.Book.prototype.startDisplay = function(){ }else{ display = this.displayChapter(this.spinePos); } - + return display; }; EPUBJS.Book.prototype.restore = function(identifier){ - + var book = this, fetch = ['manifest', 'spine', 'metadata', 'cover', 'toc', 'spineNodeIndex', 'spineIndexByURL'], reject = false, - bookKey = this.setBookKey(identifier), + bookKey = this.generateBookKey(identifier), fromStore = localStorage.getItem(bookKey), len = fetch.length, i; - + if(this.settings.clearSaved) reject = true; - if(!reject && fromStore != 'undefined' && fromStore != null){ + if(!reject && fromStore != 'undefined' && fromStore !== null){ book.contents = JSON.parse(fromStore); - + for(i = 0; i < len; i++) { var item = fetch[i]; - + if(!book.contents[item]) { reject = true; break; @@ -461,10 +466,11 @@ EPUBJS.Book.prototype.restore = function(identifier){ book[item] = book.contents[item]; } } - + if(reject || !fromStore || !this.contents || !this.settings.contentsPath){ return false; }else{ + this.settings.bookKey = bookKey; this.ready.manifest.resolve(this.manifest); this.ready.spine.resolve(this.spine); this.ready.metadata.resolve(this.metadata); @@ -475,17 +481,6 @@ 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, @@ -518,7 +513,7 @@ EPUBJS.Book.prototype.displayChapter = function(chap, end){ this.spinePos = pos; //-- Create a new chapter - this.currentChapter = new EPUBJS.Chapter(this.spine[pos], this.determineStore()); + this.currentChapter = new EPUBJS.Chapter(this.spine[pos], this.store); this._rendering = true; @@ -531,7 +526,7 @@ EPUBJS.Book.prototype.displayChapter = function(chap, end){ }); } else if(end) { render.then(function(chapter){ - chapter.gotoChapterEnd(); + chapter.lastPage(); }); } @@ -606,23 +601,35 @@ EPUBJS.Book.prototype.getCurrentLocationCfi = function() { }; 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); + var cfi, + spinePos, + spineItem, + rendered, + deferred; + if(!this.isRendered) { this.settings.previousLocationCfi = cfiString; - return; + return false; } + cfi = new EPUBJS.EpubCFI(cfiString); + spinePos = cfi.spinePos; + spineItem = this.spine[spinePos]; + deferred = new RSVP.defer(); + //-- 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(!spineItem || spinePos == -1) { + spinePos = 0; + spineItem = this.spine[spinePos]; + } + + this.currentChapter = new EPUBJS.Chapter(spineItem, this.store); if(this.currentChapter) { this.spinePos = spinePos; @@ -762,9 +769,9 @@ EPUBJS.Book.prototype.useSpreads = function(use) { if(this.isRendered) { this.renderer.reformat(); - } - -}; + } + +}; EPUBJS.Book.prototype.unload = function(){ @@ -824,12 +831,12 @@ EPUBJS.Book.prototype.applyStyles = function(callback){ }; EPUBJS.Book.prototype._registerReplacements = function(){ - this.registerHook("beforeChapterDisplay", this.applyStyles.bind(this), true); - this.registerHook("beforeChapterDisplay", EPUBJS.replace.hrefs, true); + this.renderer.registerHook("beforeChapterDisplay", this.applyStyles.bind(this), true); + this.renderer.registerHook("beforeChapterDisplay", EPUBJS.replace.hrefs, true); if(this._needsAssetReplacement()) { - this.registerHook("beforeChapterDisplay", [ + this.renderer.registerHook("beforeChapterDisplay", [ EPUBJS.replace.head, EPUBJS.replace.resources, EPUBJS.replace.svg @@ -860,9 +867,6 @@ EPUBJS.Book.prototype._needsAssetReplacement = function(){ } }; -EPUBJS.Book.prototype.beforeDisplay = function(callback, renderer){ - this.triggerHooks("beforeChapterDisplay", callback.bind(this), this.renderer); -}; //-- http://www.idpf.org/epub/fxl/ EPUBJS.Book.prototype.parseLayoutProperties = function(metadata){ @@ -873,7 +877,7 @@ EPUBJS.Book.prototype.parseLayoutProperties = function(metadata){ layout : layout, spread : spread, orientation : orientation - } + }; }; //-- Enable binding events to book diff --git a/src/core.js b/src/core.js index 23cf9cf..c880999 100644 --- a/src/core.js +++ b/src/core.js @@ -101,7 +101,7 @@ EPUBJS.core.toArray = function(obj) { return arr; }; -//-- Parse out the origin +//-- Parse the different parts of a url, returning a object EPUBJS.core.uri = function(url){ var uri = { protocol : '', @@ -112,6 +112,7 @@ EPUBJS.core.uri = function(url){ base : '', filename : '', extension : '', + fragment : '', href : url }, doubleSlash = url.indexOf('://'), @@ -166,7 +167,8 @@ EPUBJS.core.uri = function(url){ return uri; }; -//-- Parse out the folder +//-- Parse out the folder, will return everything before the last slash + EPUBJS.core.folder = function(url){ var lastSlash = url.lastIndexOf('/'); diff --git a/src/hooks.js b/src/hooks.js index 2239380..c97c85c 100644 --- a/src/hooks.js +++ b/src/hooks.js @@ -1,13 +1,13 @@ EPUBJS.hooks = {}; EPUBJS.Hooks = (function(){ - function hooks(){}; + 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.hooks[arg] = []; }, this); for (var plugType in this.hooks) { diff --git a/src/layout.js b/src/layout.js index f0106c7..4cbc7f4 100644 --- a/src/layout.js +++ b/src/layout.js @@ -1,14 +1,15 @@ EPUBJS.Layout = EPUBJS.Layout || {}; EPUBJS.Layout.Reflowable = function(documentElement, _width, _height){ + // Get the prefixed CSS commands 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 - var width = (_width % 2 == 0) ? _width : Math.floor(_width) - 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; + var gap = (section % 2 === 0) ? section : section - 1; //-- Single Page var spreadWidth = (width + gap); @@ -16,12 +17,8 @@ EPUBJS.Layout.Reflowable = function(documentElement, _width, _height){ documentElement.style.width = "auto"; //-- reset width for calculations - - documentElement.style.overflow = "hidden"; - documentElement.style.width = width + "px"; - //-- Adjust height documentElement.style.height = _height + "px"; @@ -31,9 +28,9 @@ EPUBJS.Layout.Reflowable = function(documentElement, _width, _height){ documentElement.style[columnWidth] = width+"px"; totalWidth = documentElement.scrollWidth; - displayedPages = Math.ceil(totalWidth / spreadWidth); - - // documentElement.style.width = totalWidth + spreadWidth + "px"; + displayedPages = Math.ceil(totalWidth / spreadWidth); + + documentElement.style.width = width + "px"; return { pageWidth : spreadWidth, @@ -50,10 +47,10 @@ EPUBJS.Layout.ReflowableSpreads = function(documentElement, _width, _height){ var divisor = 2, cutoff = 800; - //-- Check the width and decied on columns - var width = (_width % 2 == 0) ? _width : Math.floor(_width) - 1; + //-- Check the width and create even width 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; + var gap = (section % 2 === 0) ? section : section - 1; //-- Double Page var colWidth = Math.floor((width - gap) / divisor); @@ -78,6 +75,7 @@ EPUBJS.Layout.ReflowableSpreads = function(documentElement, _width, _height){ totalWidth = documentElement.scrollWidth; displayedPages = Math.ceil(totalWidth / spreadWidth); + //-- Add a page to the width of the document to account an for odd number of pages documentElement.style.width = totalWidth + spreadWidth + "px"; return { @@ -89,26 +87,40 @@ EPUBJS.Layout.ReflowableSpreads = function(documentElement, _width, _height){ 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; + var viewport = documentElement.querySelector("[name=viewport"); + var content; + var contents; + var width, height; - //-- Adjust height - documentElement.style.height = "auto"; + /** + * check for the viewport size + * + */ + if(viewport && viewport.hasAttribute("content")) { + content = viewport.getAttribute("content"); + contents = content.split(','); + if(contents[0]){ + width = contents[0].replace("width=", ''); + } + if(contents[1]){ + height = contents[1].replace("height=", ''); + } + } + + //-- Adjust width and height + documentElement.style.width = width + "px" || "auto"; + documentElement.style.height = height + "px" || "auto"; //-- Remove columns documentElement.style[columnWidth] = "auto"; //-- Scroll documentElement.style.overflow = "auto"; - // this.iframe.scrolling = "yes"; - // this.displayedPages = 1; + return { - pageWidth : totalWidth, - pageHeight : totalHeight, + pageWidth : width, + pageHeight : height, displayedPages : 1 }; diff --git a/src/parser.js b/src/parser.js index f24da60..5b5fde6 100644 --- a/src/parser.js +++ b/src/parser.js @@ -213,8 +213,8 @@ EPUBJS.Parser.prototype.spine = function(spineXml, manifest){ var spineNodeIndex = Array.prototype.indexOf.call(spineXml.parentNode.childNodes, spineXml); - var epubcfi = new EPUBJS.EpubCFI(); - + 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'); @@ -295,8 +295,8 @@ EPUBJS.Parser.prototype.nav = function(navHtml, spineIndexByURL, bookSpine){ if(!id) { if(spinePos) { - spineItem = bookSpine[spinePos]; - id = spineItem.id + spineItem = bookSpine[spinePos]; + id = spineItem.id; } else { id = 'epubjs-autogen-toc-id-' + (idCounter++); } @@ -325,7 +325,6 @@ EPUBJS.Parser.prototype.toc = function(tocXml, spineIndexByURL, bookSpine){ function getTOC(parent){ var list = [], - items = [], nodes = parent.querySelectorAll("navPoint"), items = Array.prototype.slice.call(nodes).reverse(), length = items.length, @@ -349,7 +348,7 @@ EPUBJS.Parser.prototype.toc = function(tocXml, spineIndexByURL, bookSpine){ if(!id) { if(spinePos) { spineItem = bookSpine[spinePos]; - id = spineItem.id + id = spineItem.id; } else { id = 'epubjs-autogen-toc-id-' + (idCounter++); } diff --git a/src/render_iframe.js b/src/render_iframe.js index 1678c46..3652b63 100644 --- a/src/render_iframe.js +++ b/src/render_iframe.js @@ -1,9 +1,9 @@ EPUBJS.Render.Iframe = function() { - this.iframe; - this.document; - this.window; - this.docEl; - this.bodyEl; + this.iframe = null; + this.document = null; + this.window = null; + this.docEl = null; + this.bodyEl = null; this.leftPos = 0; this.pageWidth = 0; @@ -18,6 +18,11 @@ EPUBJS.Render.Iframe.prototype.create = function(){ return this.iframe; }; +/** +* Sets the source of the iframe with the given URL string +* Takes: URL string +* Returns: promise with document element +*/ EPUBJS.Render.Iframe.prototype.load = function(url){ var render = this, deferred = new RSVP.defer(); @@ -41,15 +46,21 @@ EPUBJS.Render.Iframe.prototype.load = function(url){ //-- 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); - } + deferred.reject({ + message : "Error Loading Contents: " + e, + stack : new Error().stack + }); + }; return deferred.promise; }; +// Resize the iframe to the given width and height EPUBJS.Render.Iframe.prototype.resize = function(width, height){ var iframeBox; @@ -62,13 +73,14 @@ EPUBJS.Render.Iframe.prototype.resize = function(width, height){ } this.iframe.width = width; - + // Get the fractional height and width of the iframe this.width = this.iframe.getBoundingClientRect().width; this.height = this.iframe.getBoundingClientRect().height; }; EPUBJS.Render.Iframe.prototype.resized = function(e){ + // Get the fractional height and width of the iframe this.width = this.iframe.getBoundingClientRect().width; this.height = this.iframe.getBoundingClientRect().height; }; @@ -95,16 +107,6 @@ EPUBJS.Render.Iframe.prototype.setLeft = function(leftPos){ 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); @@ -136,18 +138,19 @@ EPUBJS.Render.Iframe.prototype.getPageNumberByElement = function(el){ return pg; }; -//-- +// Return the root element of the content EPUBJS.Render.Iframe.prototype.getBaseElement = function(){ return this.bodyEl; }; +// Checks if an element is on the screen 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 && + if( rect.width !== 0 && + rect.height !== 0 && // Element not visible rect.left >= 0 && rect.left < this.pageWidth ) { return true; @@ -166,6 +169,7 @@ EPUBJS.Render.Iframe.prototype.scroll = function(bool){ } }; +// Cleanup event listeners 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 9434b6b..c75a820 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -1,38 +1,53 @@ -EPUBJS.Renderer = function(type, beforeDisplay) { - this.beforeDisplay = beforeDisplay; - +EPUBJS.Renderer = function(type) { + // Dom events to listen for this.listenedEvents = ["keydown", "keyup", "keypressed", "mouseup", "mousedown", "click"]; + /** + * Setup a render method. + * Options are: Iframe + */ if(type && typeof(EPUBJS.Render[type]) != "undefined"){ - this.render = new EPUBJS.Render[type]; + this.render = new EPUBJS.Render[type]() ; } else { console.error("Not a Valid Rendering Method"); } - - this.caches = {}; + // Cached for replacement urls from storage + this.caches = {}; + + // Blank Cfi for Parsing this.epubcfi = new EPUBJS.EpubCFI(); this.spreads = true; this.resized = _.throttle(this.onResized.bind(this), 10); + + //-- Adds Hook methods to the Book prototype + // Hooks will all return before triggering the callback. + EPUBJS.Hooks.mixin(this); + //-- Get pre-registered hooks for events + this.getHooks("beforeChapterDisplay"); }; - //-- Renderer events for listening +//-- Renderer events for listening 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" + "renderer:keydown", + "renderer:keyup", + "renderer:keypressed", + "renderer:mouseup", + "renderer:mousedown", + "renderer:click", + "renderer:selected", + "renderer:chapterUnloaded", + "renderer:chapterDisplayed", + "renderer:pageChanged", + "renderer:resized", + "renderer:spreads" ]; +/** +* Creates an element to render to. +* Resizes to passed width and height or to the elements size +*/ EPUBJS.Renderer.prototype.initialize = function(element, width, height){ this.container = element; this.element = this.render.create(); @@ -53,14 +68,18 @@ EPUBJS.Renderer.prototype.initialize = function(element, width, height){ }; - +/** +* Display a chapter +* Takes: chapter object, global layout settings +* Returns: Promise with passed Renderer after pages has loaded +*/ EPUBJS.Renderer.prototype.displayChapter = function(chapter, globalLayout){ var renderer = this, store = false; - + // Unload the previous chapter listener if(this.currentChapter) { - this.currentChapter.unload(); + this.currentChapter.unload(); // Remove stored blobs this.render.window.removeEventListener("resize", this.resized); this.removeEventListeners(); this.removeSelectionListeners(); @@ -73,9 +92,8 @@ EPUBJS.Renderer.prototype.displayChapter = function(chapter, globalLayout){ this.currentChapterCfiBase = chapter.cfiBase; this.settings = this.reconcileLayoutSettings(globalLayout, chapter.properties); - - - + + // Get the url string from the chapter (may be from storage) return chapter.url(). then(function(url) { return renderer.load(url); @@ -83,8 +101,63 @@ EPUBJS.Renderer.prototype.displayChapter = function(chapter, globalLayout){ }; +/** +* Loads a url (string) and renders it, +* attaching event listeners and triggering hooks. +* Returns: Promise with the rendered contents. +*/ + +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; +}; + +/** +* Reconciles the current chapters layout properies with +* the global layout properities. +* Takes: global layout settings object, chapter properties string +* Returns: Object with layout properties +*/ EPUBJS.Renderer.prototype.reconcileLayoutSettings = function(global, chapter){ - var layoutMethod = "ReflowableSpreads"; + var layoutMethod = "ReflowableSpreads"; // Default to Spreads var properties = chapter.split(' '); var settings = {}; var spreads = true; @@ -112,6 +185,11 @@ EPUBJS.Renderer.prototype.reconcileLayoutSettings = function(global, chapter){ return settings; }; +/** +* Uses the settings to determine which Layout Method is needed +* Takes: Layout settings object +* Returns: EPUBJS.Layout function +*/ EPUBJS.Renderer.prototype.determineLayout = function(settings){ var layoutMethod = "ReflowableSpreads"; @@ -152,57 +230,19 @@ EPUBJS.Renderer.prototype.determineLayout = function(settings){ }; -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; +// Shortcut to trigger the hook before displaying the chapter +EPUBJS.Renderer.prototype.beforeDisplay = function(callback, renderer){ + this.triggerHooks("beforeChapterDisplay", callback, this); }; +// Update the renderer with the information passed by the layout EPUBJS.Renderer.prototype.updatePages = function(layout){ this.displayedPages = layout.displayedPages; this.currentChapter.pages = layout.displayedPages; this.render.setPageDimensions(layout.pageWidth, layout.pageHeight); }; +// Apply the layout again and jump back to the previous cfi position EPUBJS.Renderer.prototype.reformat = function(){ var renderer = this; if(!this.contents) return; @@ -221,6 +261,7 @@ EPUBJS.Renderer.prototype.reformat = function(){ }; +// Hide and show the render's container element. EPUBJS.Renderer.prototype.visible = function(bool){ if(typeof(bool) === "undefined") { return this.container.style.visibility; @@ -233,6 +274,7 @@ EPUBJS.Renderer.prototype.visible = function(bool){ } }; +// Remove the render element and clean up listeners EPUBJS.Renderer.prototype.remove = function() { if(this.renderer.window) { this.render.unload(); @@ -262,15 +304,6 @@ EPUBJS.Renderer.prototype.removeStyle = function(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; @@ -286,6 +319,7 @@ EPUBJS.Renderer.prototype.page = function(pg){ return false; }; +// Short cut to find next page's cfi starting at the last visible element EPUBJS.Renderer.prototype.nextPage = function(){ var pg = this.chapterPos + 1; if(pg <= this.displayedPages){ @@ -293,7 +327,7 @@ EPUBJS.Renderer.prototype.nextPage = function(){ this.render.page(pg); - this.currentLocationCfi = this.getNextPageCfi(); + this.currentLocationCfi = this.getPageCfi(this.visibileEl); this.trigger("renderer:pageChanged", this.currentLocationCfi); return true; @@ -306,11 +340,17 @@ EPUBJS.Renderer.prototype.prevPage = function(){ return this.page(this.chapterPos - 1); }; -EPUBJS.Renderer.prototype.gotoChapterEnd = function(){ - this.chapterEnd(); +//-- 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.chapterEnd = function(){ +// Jump to the last page of the chapter +EPUBJS.Renderer.prototype.lastPage = function(){ this.page(this.displayedPages); }; @@ -325,7 +365,7 @@ EPUBJS.Renderer.prototype.section = function(fragment){ }; - +// Walk the node tree from an element to the root EPUBJS.Renderer.prototype.walk = function(node) { var r, children, leng, startNode = node, @@ -371,28 +411,14 @@ EPUBJS.Renderer.prototype.walk = function(node) { 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; +// Get the cfi of the current page +EPUBJS.Renderer.prototype.getPageCfi = function(prevEl){ this.visibileEl = this.findFirstVisible(prevEl); return this.epubcfi.generateFragment(this.visibileEl, this.currentChapter.cfiBase); }; +// Goto a cfi position in the current chapter EPUBJS.Renderer.prototype.gotoCfi = function(cfi){ var element; @@ -404,6 +430,7 @@ EPUBJS.Renderer.prototype.gotoCfi = function(cfi){ this.pageByElement(element); }; +// Walk nodes until a visible element is found EPUBJS.Renderer.prototype.findFirstVisible = function(startEl){ var el = startEl || this.render.getBaseElement(); var found; @@ -418,7 +445,6 @@ EPUBJS.Renderer.prototype.findFirstVisible = function(startEl){ }; /* - EPUBJS.Renderer.prototype.route = function(hash, callback){ var location = window.location.hash.replace('#/', ''); if(this.useHash && location.length && location != this.prevLocation){ @@ -449,7 +475,7 @@ EPUBJS.Renderer.prototype.onResized = function(e){ this.layoutMethod = this.determineLayout(this.settings); } - if(this.contents){ + if(this.contents){ this.reformat(); } @@ -476,6 +502,7 @@ EPUBJS.Renderer.prototype.removeEventListeners = function(){ }; +// Pass browser events EPUBJS.Renderer.prototype.triggerEvent = function(e){ this.trigger("renderer:"+e.type, e); }; @@ -494,6 +521,7 @@ EPUBJS.Renderer.prototype.onSelectionChange = function(e){ this.highlighted = true; }; +// only pass selection on mouse up EPUBJS.Renderer.prototype.onMouseUp = function(e){ var selection; if(this.highlighted) { @@ -505,14 +533,6 @@ EPUBJS.Renderer.prototype.onMouseUp = function(e){ //-- 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; @@ -520,7 +540,7 @@ EPUBJS.Renderer.prototype.setMinSpreadWidth = function(width){ EPUBJS.Renderer.prototype.determineSpreads = function(cutoff){ if(this.width < cutoff || !cutoff) { - return false; //-- Single Page + return false; //-- Single Page }else{ return true; //-- Double Page } diff --git a/tests/render.js b/tests/render.js index e356507..16fc4a3 100644 --- a/tests/render.js +++ b/tests/render.js @@ -29,7 +29,7 @@ asyncTest("Fit to given width and height", 3, function() { equal( $iframe.width(), 400, "iframe had correct width" ); equal( $iframe.height(), 600, "iframe has correct height" ); - start(); + start(); };