mirror of
https://github.com/futurepress/epub.js.git
synced 2025-10-03 14:59:18 +02:00
Update epubcfi use xpath when available and generation fixes
This commit is contained in:
parent
08886428e2
commit
adf0464cf6
10 changed files with 193 additions and 33 deletions
|
@ -58,7 +58,6 @@
|
||||||
<script src="https://static.hypothes.is/lib/jquery.ui.widget.min.js"></script>
|
<script src="https://static.hypothes.is/lib/jquery.ui.widget.min.js"></script>
|
||||||
<script src="https://static.hypothes.is/lib/jquery.ui.autocomplete.min.js"></script>
|
<script src="https://static.hypothes.is/lib/jquery.ui.autocomplete.min.js"></script>
|
||||||
<script src="https://static.hypothes.is/lib/jquery.ui.core.min.js"></script>
|
<script src="https://static.hypothes.is/lib/jquery.ui.core.min.js"></script>
|
||||||
<script src="https://static.hypothes.is/lib/jquery.ui.widget.min.js"></script>
|
|
||||||
<script src="https://static.hypothes.is/lib/jquery.ui.menu.min.js"></script>
|
<script src="https://static.hypothes.is/lib/jquery.ui.menu.min.js"></script>
|
||||||
<script src="https://static.hypothes.is/lib/jquery.ui.position.min.js"></script>
|
<script src="https://static.hypothes.is/lib/jquery.ui.position.min.js"></script>
|
||||||
<script src="https://static.hypothes.is/lib/jquery.ui.effect.min.js"></script>
|
<script src="https://static.hypothes.is/lib/jquery.ui.effect.min.js"></script>
|
||||||
|
|
|
@ -105,16 +105,16 @@ EPUBJS.reader.NotesController = function() {
|
||||||
|
|
||||||
marker.style.verticalAlign = "super";
|
marker.style.verticalAlign = "super";
|
||||||
marker.style.fontSize = ".75em";
|
marker.style.fontSize = ".75em";
|
||||||
marker.style.position = "relative";
|
// marker.style.position = "relative";
|
||||||
marker.style.lineHeight = "1em";
|
marker.style.lineHeight = "1em";
|
||||||
|
|
||||||
mark.style.display = "inline-block";
|
// mark.style.display = "inline-block";
|
||||||
mark.style.padding = "2px";
|
mark.style.padding = "2px";
|
||||||
mark.style.backgroundColor = "#fffa96";
|
mark.style.backgroundColor = "#fffa96";
|
||||||
mark.style.borderRadius = "5px";
|
mark.style.borderRadius = "5px";
|
||||||
mark.style.cursor = "pointer";
|
mark.style.cursor = "pointer";
|
||||||
|
|
||||||
marker.id = annotation.anchor;
|
marker.id = "note-"+EPUBJS.core.uuid();
|
||||||
mark.innerHTML = annotations.indexOf(annotation) + 1 + "[Reader]";
|
mark.innerHTML = annotations.indexOf(annotation) + 1 + "[Reader]";
|
||||||
|
|
||||||
marker.appendChild(mark);
|
marker.appendChild(mark);
|
||||||
|
|
|
@ -101,6 +101,14 @@ EPUBJS.reader.ReaderController = function(book) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// book.on("book:atStart", function(){
|
||||||
|
// $prev.addClass("disabled");
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// book.on("book:atEnd", function(){
|
||||||
|
// $next.addClass("disabled");
|
||||||
|
// });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"slideOut" : slideOut,
|
"slideOut" : slideOut,
|
||||||
"slideIn" : slideIn,
|
"slideIn" : slideIn,
|
||||||
|
|
|
@ -34,17 +34,6 @@ EPUBJS.Reader = function(bookPath, _options) {
|
||||||
var search = window.location.search;
|
var search = window.location.search;
|
||||||
var parameters;
|
var parameters;
|
||||||
|
|
||||||
// Overide options with search parameters
|
|
||||||
if(search) {
|
|
||||||
parameters = search.slice(1).split("&");
|
|
||||||
parameters.forEach(function(p){
|
|
||||||
var split = p.split("=");
|
|
||||||
var name = split[0];
|
|
||||||
var value = split[1] || '';
|
|
||||||
_options[name] = value;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.settings = _.defaults(_options || {}, {
|
this.settings = _.defaults(_options || {}, {
|
||||||
bookPath : bookPath,
|
bookPath : bookPath,
|
||||||
restore : true,
|
restore : true,
|
||||||
|
@ -59,7 +48,18 @@ EPUBJS.Reader = function(bookPath, _options) {
|
||||||
history: true
|
history: true
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setBookKey(bookPath); //-- This could be username + path or any unique string
|
// Overide options with search parameters
|
||||||
|
if(search) {
|
||||||
|
parameters = search.slice(1).split("&");
|
||||||
|
parameters.forEach(function(p){
|
||||||
|
var split = p.split("=");
|
||||||
|
var name = split[0];
|
||||||
|
var value = split[1] || '';
|
||||||
|
reader.settings[name] = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setBookKey(this.settings.bookPath); //-- This could be username + path or any unique string
|
||||||
|
|
||||||
if(this.settings.restore && this.isSaved()) {
|
if(this.settings.restore && this.isSaved()) {
|
||||||
this.applySavedSettings();
|
this.applySavedSettings();
|
||||||
|
|
|
@ -121,7 +121,7 @@ EPUBJS.EpubCFI.prototype.parse = function(cfiStr) {
|
||||||
|
|
||||||
cfi.str = cfiStr;
|
cfi.str = cfiStr;
|
||||||
|
|
||||||
if(cfiStr.indexOf("epubcfi(") === 0) {
|
if(cfiStr.indexOf("epubcfi(") === 0 && cfiStr[cfiStr.length-1] === ")") {
|
||||||
// Remove intial epubcfi( and ending )
|
// Remove intial epubcfi( and ending )
|
||||||
cfiStr = cfiStr.slice(8, cfiStr.length-1);
|
cfiStr = cfiStr.slice(8, cfiStr.length-1);
|
||||||
}
|
}
|
||||||
|
@ -172,7 +172,7 @@ EPUBJS.EpubCFI.prototype.parse = function(cfiStr) {
|
||||||
} else {
|
} else {
|
||||||
cfi.steps.push({
|
cfi.steps.push({
|
||||||
"type" : "text",
|
"type" : "text",
|
||||||
'index' : parseInt(end) - 1,
|
'index' : (endInt - 1 ) / 2
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,7 +384,7 @@ EPUBJS.EpubCFI.prototype.generateCfiFromTextNode = function(anchor, offset, base
|
||||||
var parent = anchor.parentElement;
|
var parent = anchor.parentElement;
|
||||||
var steps = this.pathTo(parent);
|
var steps = this.pathTo(parent);
|
||||||
var path = this.generatePathComponent(steps);
|
var path = this.generatePathComponent(steps);
|
||||||
var index = [].slice.apply(parent.childNodes).indexOf(anchor) + 1;
|
var index = 1 + (2 * Array.prototype.indexOf.call(parent.childNodes, anchor));
|
||||||
return "epubcfi(" + base + "!" + path + "/"+index+":"+(offset || 0)+")";
|
return "epubcfi(" + base + "!" + path + "/"+index+":"+(offset || 0)+")";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -394,3 +394,64 @@ EPUBJS.EpubCFI.prototype.generateCfiFromRangeAnchor = function(range, base) {
|
||||||
return this.generateCfiFromTextNode(anchor, offset, base);
|
return this.generateCfiFromTextNode(anchor, offset, base);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
EPUBJS.EpubCFI.prototype.generateXpathFromSteps = function(steps) {
|
||||||
|
var xpath = [".", "*"];
|
||||||
|
|
||||||
|
steps.forEach(function(step){
|
||||||
|
var position = step.index + 1;
|
||||||
|
|
||||||
|
if(step.id){
|
||||||
|
xpath.push("*[position()=" + position + " and @id='" + step.id + "']");
|
||||||
|
} else if(step.type === "text") {
|
||||||
|
xpath.push("text()[" + position + "]");
|
||||||
|
} else {
|
||||||
|
xpath.push("*[" + position + "]");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return xpath.join("/");
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
EPUBJS.EpubCFI.prototype.generateRangeFromCfi = function(cfi, _doc) {
|
||||||
|
var doc = _doc || document;
|
||||||
|
var range = doc.createRange();
|
||||||
|
var lastStep;
|
||||||
|
var xpath;
|
||||||
|
var startContainer;
|
||||||
|
var textLength;
|
||||||
|
|
||||||
|
if(typeof cfi === 'string') {
|
||||||
|
cfi = this.parse(cfi);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check spinePos
|
||||||
|
if(cfi.spinePos === -1) {
|
||||||
|
// Not a valid CFI
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
xpath = this.generateXpathFromSteps(cfi.steps);
|
||||||
|
|
||||||
|
// Get the terminal step
|
||||||
|
lastStep = cfi.steps[cfi.steps.length-1];
|
||||||
|
startContainer = doc.evaluate(xpath, doc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
||||||
|
if(!startContainer) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(startContainer && cfi.characterOffset >= 0) {
|
||||||
|
textLength = startContainer.length;
|
||||||
|
if(cfi.characterOffset < textLength) {
|
||||||
|
range.setStart(startContainer, cfi.characterOffset);
|
||||||
|
range.setEnd(startContainer, textLength );
|
||||||
|
} else {
|
||||||
|
range.setStart(startContainer, cfi.characterOffset - 1 );
|
||||||
|
range.setEnd(startContainer, cfi.characterOffset );
|
||||||
|
}
|
||||||
|
} else if(startContainer) {
|
||||||
|
range.selectNode(startContainer);
|
||||||
|
}
|
||||||
|
// doc.defaultView.getSelection().addRange(range);
|
||||||
|
return range;
|
||||||
|
};
|
||||||
|
|
|
@ -12,11 +12,10 @@ EPUBJS.Layout.Reflowable.prototype.format = function(documentElement, _width, _h
|
||||||
var columnWidth = EPUBJS.core.prefixed('columnWidth');
|
var columnWidth = EPUBJS.core.prefixed('columnWidth');
|
||||||
|
|
||||||
//-- Check the width and create even width columns
|
//-- Check the width and create even width columns
|
||||||
var fullWidth = Math.floor(_width);
|
var width = Math.floor(_width);
|
||||||
var width = (fullWidth % 2 === 0) ? fullWidth : fullWidth - 1;
|
// var width = (fullWidth % 2 === 0) ? fullWidth : fullWidth - 0; // Not needed for single
|
||||||
var section = Math.floor(width / 8);
|
var section = Math.floor(width / 8);
|
||||||
var gap = (section % 2 === 0) ? section : section - 1;
|
var gap = (section % 2 === 0) ? section : section - 1;
|
||||||
|
|
||||||
this.documentElement = documentElement;
|
this.documentElement = documentElement;
|
||||||
//-- Single Page
|
//-- Single Page
|
||||||
this.spreadWidth = (width + gap);
|
this.spreadWidth = (width + gap);
|
||||||
|
@ -32,8 +31,8 @@ EPUBJS.Layout.Reflowable.prototype.format = function(documentElement, _width, _h
|
||||||
|
|
||||||
//-- Add columns
|
//-- Add columns
|
||||||
documentElement.style[columnAxis] = "horizontal";
|
documentElement.style[columnAxis] = "horizontal";
|
||||||
documentElement.style[columnGap] = gap+"px";
|
|
||||||
documentElement.style[columnWidth] = width+"px";
|
documentElement.style[columnWidth] = width+"px";
|
||||||
|
documentElement.style[columnGap] = gap+"px";
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pageWidth : this.spreadWidth,
|
pageWidth : this.spreadWidth,
|
||||||
|
@ -69,6 +68,7 @@ EPUBJS.Layout.ReflowableSpreads.prototype.format = function(documentElement, _wi
|
||||||
//-- Check the width and create even width columns
|
//-- Check the width and create even width columns
|
||||||
var fullWidth = Math.floor(_width);
|
var fullWidth = Math.floor(_width);
|
||||||
var width = (fullWidth % 2 === 0) ? fullWidth : fullWidth - 1;
|
var width = (fullWidth % 2 === 0) ? fullWidth : fullWidth - 1;
|
||||||
|
|
||||||
var section = Math.floor(width / 8);
|
var section = Math.floor(width / 8);
|
||||||
var gap = (section % 2 === 0) ? section : section - 1;
|
var gap = (section % 2 === 0) ? section : section - 1;
|
||||||
//-- Double Page
|
//-- Double Page
|
||||||
|
|
|
@ -163,6 +163,16 @@ EPUBJS.Render.Iframe.prototype.getPageNumberByElement = function(el){
|
||||||
return pg;
|
return pg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//-- Show the page containing an Element
|
||||||
|
EPUBJS.Render.Iframe.prototype.getPageNumberByRect = function(boundingClientRect){
|
||||||
|
var left, pg;
|
||||||
|
|
||||||
|
left = this.leftPos + boundingClientRect.left; //-- Calculate left offset compaired to scrolled position
|
||||||
|
pg = Math.floor(left / this.pageWidth) + 1; //-- pages start at 1
|
||||||
|
|
||||||
|
return pg;
|
||||||
|
};
|
||||||
|
|
||||||
// Return the root element of the content
|
// Return the root element of the content
|
||||||
EPUBJS.Render.Iframe.prototype.getBaseElement = function(){
|
EPUBJS.Render.Iframe.prototype.getBaseElement = function(){
|
||||||
return this.bodyEl;
|
return this.bodyEl;
|
||||||
|
|
|
@ -452,19 +452,28 @@ EPUBJS.Renderer.prototype.getPageCfi = function(prevEl){
|
||||||
|
|
||||||
// Goto a cfi position in the current chapter
|
// Goto a cfi position in the current chapter
|
||||||
EPUBJS.Renderer.prototype.gotoCfi = function(cfi){
|
EPUBJS.Renderer.prototype.gotoCfi = function(cfi){
|
||||||
var element;
|
|
||||||
var pg;
|
var pg;
|
||||||
|
var marker;
|
||||||
|
var range;
|
||||||
|
|
||||||
if(_.isString(cfi)){
|
if(_.isString(cfi)){
|
||||||
cfi = this.epubcfi.parse(cfi);
|
cfi = this.epubcfi.parse(cfi);
|
||||||
}
|
}
|
||||||
|
|
||||||
marker = this.epubcfi.addMarker(cfi, this.doc);
|
if(typeof document.evaluate === 'undefined') {
|
||||||
if(marker) {
|
marker = this.epubcfi.addMarker(cfi, this.doc);
|
||||||
pg = this.render.getPageNumberByElement(marker);
|
if(marker) {
|
||||||
// Must Clean up Marker before going to page
|
pg = this.render.getPageNumberByElement(marker);
|
||||||
this.epubcfi.removeMarker(marker, this.doc);
|
// Must Clean up Marker before going to page
|
||||||
this.page(pg);
|
this.epubcfi.removeMarker(marker, this.doc);
|
||||||
|
this.page(pg);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var range = this.epubcfi.generateRangeFromCfi(cfi, this.doc);
|
||||||
|
if(range) {
|
||||||
|
pg = this.render.getPageNumberByRect(range.getBoundingClientRect());
|
||||||
|
this.page(pg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -78,4 +78,32 @@ test("Compare CFI's", null, function() {
|
||||||
equal(epubcfi.compare("epubcfi(/6/2[cover]!/4/8/5:1)", "epubcfi(/6/2[cover]!/4/6/15:2)"), 1, "First Element is greater");
|
equal(epubcfi.compare("epubcfi(/6/2[cover]!/4/8/5:1)", "epubcfi(/6/2[cover]!/4/6/15:2)"), 1, "First Element is greater");
|
||||||
equal(epubcfi.compare("epubcfi(/6/2[cover]!/4/8/1:0)", "epubcfi(/6/2[cover]!/4/8/1:0)"), 0, "All Equal");
|
equal(epubcfi.compare("epubcfi(/6/2[cover]!/4/8/1:0)", "epubcfi(/6/2[cover]!/4/8/1:0)"), 0, "All Equal");
|
||||||
|
|
||||||
})
|
});
|
||||||
|
|
||||||
|
test("Generate XPath from Steps", null, function() {
|
||||||
|
var epubcfi = new EPUBJS.EpubCFI();
|
||||||
|
var cfi = epubcfi.parse("epubcfi(/6/12[xepigraph_001]!4/2/8[extracts]/1:0)");
|
||||||
|
var xpath = epubcfi.generateXpathFromSteps(cfi.steps);
|
||||||
|
|
||||||
|
equal(xpath, "./*/*[2]/*[1]/*[position()=4 and @id='extracts']/text()[1]", "Correct Xpath Generated");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
asyncTest("Generate Range from CFI", 1, function() {
|
||||||
|
var book = ePub('/reader/moby-dick/', { width: 400, height: 600 });
|
||||||
|
|
||||||
|
var render = book.renderTo("qunit-fixture");
|
||||||
|
|
||||||
|
var result = function(){
|
||||||
|
var displayed = book.gotoHref("epigraph_001.xhtml");
|
||||||
|
displayed.then(function(){
|
||||||
|
var epubcfi = new EPUBJS.EpubCFI();
|
||||||
|
var range = epubcfi.generateRangeFromCfi("epubcfi(/6/12[xepigraph_001]!4/2/8[extracts]/1:0)", book.renderer.doc);
|
||||||
|
equal( range.startContainer.data, "Extracts.", "Anchor is correct" );
|
||||||
|
start();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
render.then(result);
|
||||||
|
});
|
45
tools/server
Executable file
45
tools/server
Executable file
|
@ -0,0 +1,45 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
var connect = require('connect'),
|
||||||
|
colors = require('colors'),
|
||||||
|
argv = require('optimist').argv,
|
||||||
|
portfinder = require('portfinder');
|
||||||
|
|
||||||
|
var port = argv.p,
|
||||||
|
logger = argv.l,
|
||||||
|
log = console.log;
|
||||||
|
|
||||||
|
if (!argv.p) {
|
||||||
|
portfinder.basePort = 8080;
|
||||||
|
portfinder.getPort(function (err, port) {
|
||||||
|
if (err) throw err;
|
||||||
|
listen(port);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
listen(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function listen(port) {
|
||||||
|
var server = connect();
|
||||||
|
server.use(connect.static(__dirname + "../../"))
|
||||||
|
|
||||||
|
if(!logger) server.use(connect.logger(logger))
|
||||||
|
|
||||||
|
server.listen(port);
|
||||||
|
|
||||||
|
log('Starting up Server, serving '.yellow
|
||||||
|
+ __dirname.replace("tools", '').green
|
||||||
|
+ ' on port: '.yellow
|
||||||
|
+ port.toString().cyan);
|
||||||
|
log('Hit CTRL-C to stop the server');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
process.on('SIGINT', function () {
|
||||||
|
log('http-server stopped.'.red);
|
||||||
|
process.exit();
|
||||||
|
});
|
Loading…
Add table
Add a link
Reference in a new issue