1
0
Fork 0
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:
Fred Chasen 2014-04-09 10:54:01 -07:00
parent 08886428e2
commit adf0464cf6
10 changed files with 193 additions and 33 deletions

View file

@ -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>

View file

@ -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);

View file

@ -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,

View file

@ -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();

View file

@ -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;
};

View file

@ -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

View file

@ -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;

View file

@ -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);
}
} }
}; };

View file

@ -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
View 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();
});