1
0
Fork 0
mirror of https://github.com/futurepress/epub.js.git synced 2025-10-05 15:32:55 +02:00

Added locations, updated cfi to handle ranges

This commit is contained in:
Fred Chasen 2016-11-17 15:30:40 +01:00
parent dd14b692dc
commit 165d8a4875
8 changed files with 278 additions and 34 deletions

View file

@ -139,3 +139,17 @@ body {
#book-viewer iframe {
padding: 40px 40px;
}
#controls {
position: absolute;
bottom: 16px;
left: 50%;
width: 400px;
margin-left: -200px;
text-align: center;
display: none;
}
#controls > input[type=range] {
width: 400px;
}

143
examples/locations.html Normal file
View file

@ -0,0 +1,143 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>EPUB.js Spreads Example</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.1/jszip.min.js"></script>
<script src="../dist/epub.js"></script>
<link rel="stylesheet" type="text/css" href="examples.css">
</head>
<body>
<div id="title"></div>
<div id="viewer" class="spreads"></div>
<a id="prev" href="#prev" class="arrow"></a>
<a id="next" href="#next" class="arrow"></a>
<div id="controls">
<input id="current-percent" size="3" value="0" /> %
</div>
<script>
var controls = document.getElementById("controls");
var currentPage = document.getElementById("current-percent");
var slider = document.createElement("input");
var slide = function(){
var cfi = book.locations.cfiFromPercentage(slider.value);
rendition.display(cfi);
};
var mouseDown = false;
// Load the opf
var book = ePub("../books/moby-dick/OPS/package.opf");
var rendition = book.renderTo("viewer", {
width: "100%",
height: 500
});
var displayed = rendition.display();
var title = document.getElementById("title");
var next = document.getElementById("next");
next.addEventListener("click", function(e){
rendition.next();
e.preventDefault();
}, false);
var prev = document.getElementById("prev");
prev.addEventListener("click", function(e){
rendition.prev();
e.preventDefault();
}, false);
var keyListener = function(e){
// Left Key
if ((e.keyCode || e.which) == 37) {
rendition.prev();
}
// Right Key
if ((e.keyCode || e.which) == 39) {
rendition.next();
}
};
rendition.on("keyup", keyListener);
document.addEventListener("keyup", keyListener, false);
rendition.on("locationChanged", function(location){
console.log(location);
});
book.ready.then(function(){
// Load in stored locations from json or local storage
var key = book.key()+'-locations';
var stored = localStorage.getItem(key);
if (stored) {
return book.locations.load(stored);
} else {
// Or generate the locations on the fly
// Can pass an option number of chars to break sections by
// default is 150 chars
return book.locations.generate(600);
}
})
.then(function(locations){
controls.style.display = "block";
slider.setAttribute("type", "range");
slider.setAttribute("min", 0);
slider.setAttribute("max", 100);
// slider.setAttribute("max", book.locations.total+1);
slider.setAttribute("step", 1);
slider.setAttribute("value", 0);
slider.addEventListener("change", slide, false);
slider.addEventListener("mousedown", function(){
mouseDown = true;
}, false);
slider.addEventListener("mouseup", function(){
mouseDown = false;
}, false);
// Wait for book to be rendered to get current page
displayed.then(function(){
// Get the current CFI
var currentLocation = rendition.currentLocation();
// Get the Percentage (or location) from that CFI
var currentPage = book.locations.percentageFromCfi(currentLocation);
slider.value = currentPage;
currentPage.value = currentPage;
});
controls.appendChild(slider);
currentPage.addEventListener("change", function(){
var cfi = book.locations.cfiFromPercentage(currentPage.value/100);
rendition.display(cfi);
}, false);
// Listen for location changed event, get percentage from CFI
rendition.on('locationChanged', function(location){
var percent = book.locations.percentageFromCfi(location);
var percentage = Math.floor(percent * 100);
if(!mouseDown) {
slider.value = percentage;
}
currentPage.value = percentage;
});
// Save out the generated locations to JSON
localStorage.setItem(book.key()+'-locations', book.locations.save());
});
</script>
</body>
</html>

View file

@ -9,7 +9,7 @@
},
"scripts": {
"test": "./node_modules/.bin/karma start --single-run --browsers PhantomJS",
"start": "webpack-dev-server --inline --hot",
"start": "webpack-dev-server --inline",
"build": "./node_modules/.bin/gulp minify"
},
"author": "fchasen@gmail.com",

View file

@ -111,7 +111,7 @@ function Book(url, options){
/**
* @property {Locations} locations
*/
this.locations = new Locations(this.spine, this.load);
this.locations = new Locations(this.spine, this.load.bind(this));
/**
* @property {Navigation} navigation
@ -173,7 +173,7 @@ Book.prototype.open = function(input, what){
.then(this.openEpub.bind(this));
} else if(type == "opf") {
this.url = new Url(input);
opening = this.openPackaging(input);
opening = this.openPackaging(this.url.Path.toString());
} else {
this.url = new Url(input);
opening = this.openContainer(CONTAINER_PATH)
@ -223,7 +223,6 @@ Book.prototype.openContainer = function(url){
Book.prototype.openPackaging = function(url){
var packageUrl;
this.path = new Path(url);
return this.load(url)
.then(function(xml) {
this.packaging = new Packaging(xml);
@ -474,6 +473,16 @@ Book.prototype.range = function(cfiRange) {
})
};
/**
* Generates the Book Key using the identifer in the manifest or other string provided
* @param {[string]} identifier to use instead of metadata identifier
* @return {string} key
*/
Book.prototype.key = function(identifier){
var ident = identifier || this.package.metadata.identifier || this.url.filename;
return "epubjs:" + ePub.VERSION + ":" + ident;
};
//-- Enable binding events to book
EventEmitter(Book.prototype);

View file

@ -304,6 +304,9 @@ EpubCFI.prototype.toString = function() {
};
EpubCFI.prototype.compare = function(cfiOne, cfiTwo) {
var stepsA, stepsB;
var terminalA, terminalB;
if(typeof cfiOne === 'string') {
cfiOne = new EpubCFI(cfiOne);
}
@ -318,36 +321,52 @@ EpubCFI.prototype.compare = function(cfiOne, cfiTwo) {
return -1;
}
if (cfiOne.range) {
stepsA = cfiOne.path.steps.concat(cfiOne.start.steps);
terminalA = cfiOne.start.terminal;
} else {
stepsA = cfiOne.path.steps;
terminalA = cfiOne.path.terminal;
}
if (cfiTwo.range) {
stepsB = cfiTwo.path.steps.concat(cfiTwo.start.steps);
terminalB = cfiTwo.start.terminal;
} else {
stepsB = cfiTwo.path.steps;
terminalB = cfiTwo.path.terminal;
}
// Compare Each Step in the First item
for (var i = 0; i < cfiOne.path.steps.length; i++) {
if(!cfiTwo.path.steps[i]) {
for (var i = 0; i < stepsA.length; i++) {
if(!stepsA[i]) {
return -1;
}
if(!stepsB[i]) {
return 1;
}
if(cfiOne.path.steps[i].index > cfiTwo.path.steps[i].index) {
if(stepsA[i].index > stepsB[i].index) {
return 1;
}
if(cfiOne.path.steps[i].index < cfiTwo.path.steps[i].index) {
if(stepsA[i].index < stepsB[i].index) {
return -1;
}
// Otherwise continue checking
}
// All steps in First equal to Second and First is Less Specific
if(cfiOne.path.steps.length < cfiTwo.path.steps.length) {
if(stepsA.length < stepsB.length) {
return 1;
}
// Compare the charecter offset of the text node
if(cfiOne.path.terminal.offset > cfiTwo.path.terminal.offset) {
if(terminalA.offset > terminalB.offset) {
return 1;
}
if(cfiOne.path.terminal.offset < cfiTwo.path.terminal.offset) {
if(terminalA.offset < terminalB.offset) {
return -1;
}
// TODO: compare ranges
// CFI's are equal
return 0;
};
@ -485,7 +504,6 @@ EpubCFI.prototype.fromRange = function(range, base, ignoreClass) {
}
cfi.start = this.pathTo(start, startOffset, ignoreClass);
if (needsIgnoring) {
endOffset = this.patchOffset(end, endOffset, ignoreClass);
}
@ -504,7 +522,7 @@ EpubCFI.prototype.fromRange = function(range, base, ignoreClass) {
for (i = 0; i < len; i++) {
if (this.equalStep(cfi.start.steps[i], cfi.end.steps[i])) {
if(i == len-1) {
if(i === len-1) {
// Last step is equal, check terminals
if(cfi.start.terminal === cfi.end.terminal) {
// CFI's are equal

View file

@ -51,7 +51,7 @@ Locations.prototype.generate = function(chars) {
}
return this._locations;
// console.log(this.precentage(this.book.rendition.location.start), this.precentage(this.book.rendition.location.end));
// console.log(this.percentage(this.book.rendition.location.start), this.percentage(this.book.rendition.location.end));
}.bind(this));
};
@ -63,9 +63,10 @@ Locations.prototype.process = function(section) {
var range;
var doc = contents.ownerDocument;
var body = core.qs(doc, 'body');
var counter = 0;
this.sprint(contents, function(node) {
this.sprint(body, function(node) {
var len = node.length;
var dist;
var pos = 0;
@ -138,15 +139,17 @@ Locations.prototype.locationFromCfi = function(cfi){
if(this._locations.length === 0) {
return -1;
}
return core.locationOf(cfi, this._locations, this.epubcfi.compare);
return core.locationOf(cfi.start, this._locations, this.epubcfi.compare);
};
Locations.prototype.precentageFromCfi = function(cfi) {
Locations.prototype.percentageFromCfi = function(cfi) {
if(this._locations.length === 0) {
return null;
}
// Find closest cfi
var loc = this.locationFromCfi(cfi);
// Get percentage in total
return this.precentageFromLocation(loc);
return this.percentageFromLocation(loc);
};
Locations.prototype.percentageFromLocation = function(loc) {
@ -214,7 +217,7 @@ Locations.prototype.setCurrent = function(curr){
}
this.emit("changed", {
percentage: this.precentageFromLocation(loc)
percentage: this.percentageFromLocation(loc)
});
};

View file

@ -159,6 +159,15 @@ DefaultViewManager.prototype.display = function(section, target){
this.views.clear();
this.add(section)
.then(function(view){
// Move to correct place within the section, if needed
if(target) {
offset = view.locationOf(target);
this.moveTo(offset);
}
}.bind(this))
.then(function(){
var next;
if (this.layout.name === "pre-paginated" &&
@ -169,13 +178,7 @@ DefaultViewManager.prototype.display = function(section, target){
}
}
}.bind(this))
.then(function(view){
// Move to correct place within the section, if needed
if(target) {
offset = view.locationOf(target);
this.moveTo(offset);
}
.then(function(){
this.views.show();
@ -362,15 +365,34 @@ DefaultViewManager.prototype.current = function(){
};
DefaultViewManager.prototype.currentLocation = function(){
if (this.settings.axis === "vertical") {
this.location = this.scrolledLocation();
} else {
this.location = this.paginatedLocation();
}
return this.location;
};
DefaultViewManager.prototype.scrolledLocation = function(){
var view;
if(this.views.length) {
view = this.views.first();
return this.mapping.page(view, view.section.cfiBase);
}
};
DefaultViewManager.prototype.paginatedLocation = function(){
var view;
var start, end;
if(this.views.length) {
view = this.views.first();
start = container.left - view.position().left;
end = start + this.layout.spread;
return this.mapping.page(view, view.section.cfiBase);
start = this._bounds.left - view.position().left;
end = start + this.layout.spreadWidth;
return this.mapping.page(view, view.section.cfiBase, start, end);
}
};

View file

@ -315,7 +315,7 @@ Rendition.prototype.afterDisplayed = function(view){
* Report resize events and display the last seen location
* @private
*/
Rendition.prototype.onResized = function(){
Rendition.prototype.onResized = function(size){
if(this.location) {
this.display(this.location.start);
@ -454,6 +454,7 @@ Rendition.prototype.spread = function(spread, min){
/**
* Report the current location
* @private
*/
Rendition.prototype.reportLocation = function(){
return this.q.enqueue(function(){
@ -461,16 +462,50 @@ Rendition.prototype.reportLocation = function(){
if (location && location.then && typeof location.then === 'function') {
location.then(function(result) {
this.location = result;
this.percentage = this.book.locations.percentageFromCfi(result);
if (this.percentage != null) {
this.location.percentage = this.percentage;
}
this.emit("locationChanged", this.location);
}.bind(this));
} else if (location) {
this.location = location;
this.percentage = this.book.locations.percentageFromCfi(location);
if (this.percentage != null) {
this.location.percentage = this.percentage;
}
this.emit("locationChanged", this.location);
}
}.bind(this));
};
/**
* Get the Current Location CFI
* @return {EpubCFI} location (may be a promise)
*/
Rendition.prototype.currentLocation = function(){
var location = this.manager.currentLocation();
if (location && location.then && typeof location.then === 'function') {
location.then(function(result) {
var percentage = this.book.locations.percentageFromCfi(result);
if (percentage != null) {
result.percentage = percentage;
}
return result;
}.bind(this));
} else if (location) {
var percentage = this.book.locations.percentageFromCfi(location);
if (percentage != null) {
location.percentage = percentage;
}
return location;
}
};
/**
* Remove and Clean Up the Rendition
*/