diff --git a/examples/locations.html b/examples/locations.html index 826d0f9..c23d1ae 100644 --- a/examples/locations.html +++ b/examples/locations.html @@ -25,16 +25,16 @@ var currentPage = document.getElementById("current-percent"); var slider = document.createElement("input"); var slide = function(){ - var cfi = book.locations.cfiFromPercentage(slider.value); + var cfi = book.locations.cfiFromPercentage(slider.value / 100); rendition.display(cfi); }; var mouseDown = false; // Load the opf - var book = ePub("../test/fixtures/alice/OPS/package.opf"); + var book = ePub("https://s3.amazonaws.com/moby-dick/moby-dick.epub"); var rendition = book.renderTo("viewer", { width: "100%", - height: 500 + height: 600 }); var displayed = rendition.display(); @@ -109,7 +109,7 @@ // Get the current CFI var currentLocation = rendition.currentLocation(); // Get the Percentage (or location) from that CFI - var currentPage = book.locations.percentageFromCfi(currentLocation); + var currentPage = book.locations.percentageFromCfi(currentLocation.start); slider.value = currentPage; currentPage.value = currentPage; }); @@ -123,7 +123,7 @@ // Listen for location changed event, get percentage from CFI rendition.on('locationChanged', function(location){ - var percent = book.locations.percentageFromCfi(location); + var percent = book.locations.percentageFromCfi(location.start); var percentage = Math.floor(percent * 100); if(!mouseDown) { slider.value = percentage; diff --git a/package.json b/package.json index 6b8df10..2e3e98f 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "dependencies": { "event-emitter": "^0.3.4", "jszip": "^3.1.3", - "marks-pane": "^1.0.0", + "marks-pane": "^1.0.1", "path-webpack": "^0.0.3", "stream-browserify": "^2.0.1", "xmldom": "0.1.27" diff --git a/src/epubcfi.js b/src/epubcfi.js index fb5e538..23d63f1 100644 --- a/src/epubcfi.js +++ b/src/epubcfi.js @@ -1,4 +1,4 @@ -import {extend, type, findChildren, RangeObject} from "./utils/core"; +import {extend, type, findChildren, RangeObject, isNumber} from "./utils/core"; /** EPUB CFI spec: http://www.idpf.org/epub/linking/cfi/epub-cfi.html @@ -193,10 +193,14 @@ class EpubCFI { var assertion = termialStr.match(/\[(.*)\]/); if(assertion && assertion[1]){ - characterOffset = parseInt(termialStr.split("[")[0]) || null; + characterOffset = parseInt(termialStr.split("[")[0]); textLocationAssertion = assertion[1]; } else { - characterOffset = parseInt(termialStr) || null; + characterOffset = parseInt(termialStr); + } + + if (!isNumber(characterOffset)) { + characterOffset = null; } return { @@ -294,12 +298,12 @@ class EpubCFI { cfiString += this.segmentString(this.path); // Add Range, if present - if(this.start) { + if(this.range && this.start) { cfiString += ","; cfiString += this.segmentString(this.start); } - if(this.end) { + if(this.range && this.end) { cfiString += ","; cfiString += this.segmentString(this.end); } @@ -313,6 +317,11 @@ class EpubCFI { var stepsA, stepsB; var terminalA, terminalB; + var rangeAStartSteps, rangeAEndSteps; + var rangeBEndSteps, rangeBEndSteps; + var rangeAStartTerminal, rangeAEndTerminal; + var rangeBStartTerminal, rangeBEndTerminal; + if(typeof cfiOne === "string") { cfiOne = new EpubCFI(cfiOne); } @@ -968,6 +977,23 @@ class EpubCFI { return cfi; } + + collapse(toStart) { + if (!this.range) { + return; + } + + this.range = false; + + if (toStart) { + this.path.steps = this.path.steps.concat(this.start.steps); + this.path.terminal = this.start.terminal; + } else { + this.path.steps = this.path.steps.concat(this.end.steps); + this.path.terminal = this.end.terminal; + } + + } } export default EpubCFI; diff --git a/src/locations.js b/src/locations.js index 71e9c0f..4f2dcf3 100644 --- a/src/locations.js +++ b/src/locations.js @@ -43,13 +43,13 @@ class Locations { this.q.pause(); this.spine.each(function(section) { - - this.q.enqueue(this.process.bind(this), section); - + if (section.linear) { + this.q.enqueue(this.process.bind(this), section); + } }.bind(this)); return this.q.run().then(function() { - this.total = this._locations.length-1; + this.total = this._locations.length - 1; if (this._currentCfi) { this.currentLocation = this._currentCfi; @@ -164,11 +164,22 @@ class Locations { } locationFromCfi(cfi){ + let loc; + if (EpubCFI.prototype.isCfiString(cfi)) { + cfi = new EpubCFI(cfi); + } // Check if the location has not been set yet if(this._locations.length === 0) { return -1; } - return locationOf(cfi.start, this._locations, this.epubcfi.compare); + + loc = locationOf(cfi, this._locations, this.epubcfi.compare); + + if (loc > this.total) { + return this.total; + } + + return loc; } percentageFromCfi(cfi) { @@ -185,6 +196,7 @@ class Locations { if (!loc || !this.total) { return 0; } + return (loc / this.total); } @@ -202,16 +214,26 @@ class Locations { return cfi; } - cfiFromPercentage(value){ - var percentage = (value > 1) ? value / 100 : value; // Normalize value to 0-1 - var loc = Math.ceil(this.total * percentage); + cfiFromPercentage(percentage){ + let loc; + if (percentage > 1) { + console.warn("Normalize cfiFromPercentage value to between 0 - 1"); + } + // Make sure 1 goes to very end + if (percentage >= 1) { + let cfi = new EpubCFI(this._locations[this.total]); + cfi.collapse(); + return cfi.toString(); + } + + loc = Math.ceil(this.total * percentage); return this.cfiFromLocation(loc); } load(locations){ this._locations = JSON.parse(locations); - this.total = this._locations.length-1; + this.total = this._locations.length - 1; return this._locations; } diff --git a/src/managers/views/iframe.js b/src/managers/views/iframe.js index 3280a78..aca22a6 100644 --- a/src/managers/views/iframe.js +++ b/src/managers/views/iframe.js @@ -544,8 +544,8 @@ class IframeView { var targetPos = this.contents.locationOf(target, this.settings.ignoreClass); return { - "left": window.scrollX + parentPos.left + targetPos.left, - "top": window.scrollY + parentPos.top + targetPos.top + "left": window.scrollX + targetPos.left, + "top": window.scrollY + targetPos.top }; } diff --git a/src/rendition.js b/src/rendition.js index 8c74e0f..b4bdcbc 100644 --- a/src/rendition.js +++ b/src/rendition.js @@ -244,8 +244,11 @@ class Rendition { var moveTo; // Check if this is a book percentage - if (this.book.locations.length && isFloat(target)) { - target = this.book.locations.cfiFromPercentage(target); + if (this.book.locations.length && + (isFloat(target) || + (typeof target === "string" && target == parseFloat(target))) // Handle 1.0 + ) { + target = this.book.locations.cfiFromPercentage(parseFloat(target)); } section = this.book.spine.get(target); @@ -487,7 +490,7 @@ class Rendition { location.then(function(result) { this.location = result; - this.percentage = this.book.locations.percentageFromCfi(result); + this.percentage = this.book.locations.percentageFromCfi(result.start); if (this.percentage != null) { this.location.percentage = this.percentage; } @@ -496,7 +499,7 @@ class Rendition { }.bind(this)); } else if (location) { this.location = location; - this.percentage = this.book.locations.percentageFromCfi(location); + this.percentage = this.book.locations.percentageFromCfi(location.start); if (this.percentage != null) { this.location.percentage = this.percentage; } @@ -515,14 +518,14 @@ class Rendition { var location = this.manager.currentLocation(); if (location && location.then && typeof location.then === "function") { location.then(function(result) { - var percentage = this.book.locations.percentageFromCfi(result); + var percentage = this.book.locations.percentageFromCfi(result.start); if (percentage != null) { result.percentage = percentage; } return result; }.bind(this)); } else if (location) { - var percentage = this.book.locations.percentageFromCfi(location); + var percentage = this.book.locations.percentageFromCfi(location.start); if (percentage != null) { location.percentage = percentage; } diff --git a/src/section.js b/src/section.js index 1cb911a..492f572 100644 --- a/src/section.js +++ b/src/section.js @@ -13,7 +13,7 @@ import { replaceBase } from "./utils/replacements"; class Section { constructor(item, hooks){ this.idref = item.idref; - this.linear = item.linear; + this.linear = item.linear === "yes"; this.properties = item.properties; this.index = item.index; this.href = item.href; diff --git a/src/spine.js b/src/spine.js index 052b05e..9a2d531 100644 --- a/src/spine.js +++ b/src/spine.js @@ -48,6 +48,7 @@ class Spine { var manifestItem = this.manifest[item.idref]; var spineItem; + item.index = index; item.cfiBase = this.epubcfi.generateChapterComponent(this.spineNodeIndex, item.index, item.idref); if (item.href) { @@ -62,8 +63,39 @@ class Spine { } } - item.prev = function(){ return this.get(index-1); }.bind(this); - item.next = function(){ return this.get(index+1); }.bind(this); + if (item.linear === "yes") { + item.prev = function() { + let prevIndex = item.index; + while (prevIndex > 0) { + let prev = this.get(prevIndex-1); + if (prev && prev.linear) { + return prev; + } + prevIndex -= 1; + } + return; + }.bind(this); + + item.next = function() { + let nextIndex = item.index; + while (nextIndex < this.spineItems.length-1) { + let next = this.get(nextIndex+1); + if (next && next.linear) { + return next; + } + nextIndex += 1; + } + return; + }.bind(this); + } else { + item.prev = function() { + return; + } + item.next = function() { + return; + } + } + spineItem = new Section(item, this.hooks); @@ -87,10 +119,18 @@ class Spine { get(target) { var index = 0; - if(this.epubcfi.isCfiString(target)) { + if (typeof target === "undefined") { + while (index < this.spineItems.length) { + let next = this.spineItems[index]; + if (next && next.linear) { + break; + } + index += 1; + } + } else if(this.epubcfi.isCfiString(target)) { let cfi = new EpubCFI(target); index = cfi.spinePos; - } else if(target && (typeof target === "number" || isNaN(target) === false)){ + } else if(typeof target === "number" || isNaN(target) === false){ index = target; } else if(target && target.indexOf("#") === 0) { index = this.spineById[target.substring(1)]; diff --git a/src/utils/core.js b/src/utils/core.js index ab2ec62..b4c5bd0 100644 --- a/src/utils/core.js +++ b/src/utils/core.js @@ -105,9 +105,8 @@ export function locationOf(item, array, compareFunction, _start, _end) { compared = compareFunction(array[pivot], item); if(end-start === 1) { - return compared > 0 ? pivot : pivot + 1; + return compared >= 0 ? pivot : pivot + 1; } - if(compared === 0) { return pivot; }