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

398 lines
9.6 KiB
JavaScript

EPUBJS.Chapter = function(spineObject, store, credentials){
this.href = spineObject.href;
this.absolute = spineObject.url;
this.id = spineObject.id;
this.spinePos = spineObject.index;
this.cfiBase = spineObject.cfiBase;
this.properties = spineObject.properties;
this.manifestProperties = spineObject.manifestProperties;
this.linear = spineObject.linear;
this.pages = 1;
this.store = store;
this.credentials = credentials;
this.epubcfi = new EPUBJS.EpubCFI();
this.deferred = new RSVP.defer();
this.loaded = this.deferred.promise;
EPUBJS.Hooks.mixin(this);
//-- Get pre-registered hooks for events
this.getHooks("beforeChapterRender");
// Cached for replacement urls from storage
this.caches = {};
};
EPUBJS.Chapter.prototype.load = function(_store, _credentials){
var store = _store || this.store;
var credentials = _credentials || this.credentials;
var promise;
// if(this.store && (!this.book.online || this.book.contained))
if(store){
promise = store.getXml(this.absolute);
}else{
promise = EPUBJS.core.request(this.absolute, 'xml', credentials);
}
promise.then(function(xml){
this.setDocument(xml);
this.deferred.resolve(this);
}.bind(this));
return promise;
};
EPUBJS.Chapter.prototype.render = function(_store){
return this.load().then(function(doc){
var head = doc.querySelector('head');
var base = doc.createElement("base");
base.setAttribute("href", this.absolute);
head.insertBefore(base, head.firstChild);
this.contents = doc;
return new RSVP.Promise(function (resolve, reject) {
this.triggerHooks("beforeChapterRender", function () {
resolve(doc);
}.bind(this), this);
}.bind(this));
}.bind(this))
.then(function(doc) {
var serializer = new XMLSerializer();
var contents = serializer.serializeToString(doc);
return contents;
}.bind(this));
};
EPUBJS.Chapter.prototype.url = function(_store){
var deferred = new RSVP.defer();
var store = _store || this.store;
var loaded;
var chapter = this;
var url;
if(store){
if(!this.tempUrl) {
store.getUrl(this.absolute).then(function(url){
chapter.tempUrl = url;
deferred.resolve(url);
});
} else {
url = this.tempUrl;
deferred.resolve(url);
}
}else{
url = this.absolute;
deferred.resolve(url);
}
/*
loaded = EPUBJS.core.request(url, 'xml', false);
loaded.then(function(contents){
chapter.contents = contents;
deferred.resolve(chapter.absolute);
}, function(error){
deferred.reject(error);
});
*/
return deferred.promise;
};
EPUBJS.Chapter.prototype.setPages = function(num){
this.pages = num;
};
EPUBJS.Chapter.prototype.getPages = function(num){
return this.pages;
};
EPUBJS.Chapter.prototype.getID = function(){
return this.ID;
};
EPUBJS.Chapter.prototype.unload = function(store){
this.document = null;
if(this.tempUrl && store) {
store.revokeUrl(this.tempUrl);
this.tempUrl = false;
}
};
EPUBJS.Chapter.prototype.setDocument = function(_document){
var uri = _document.namespaceURI;
var doctype = _document.doctype;
// Creates an empty document
this.document = _document.implementation.createDocument(
uri,
null,
null
);
this.contents = this.document.importNode(
_document.documentElement, //node to import
true //clone its descendants
);
this.document.appendChild(this.contents);
// Fix to apply wgxpath to new document in IE
if(!this.document.evaluate && document.evaluate) {
this.document.evaluate = document.evaluate;
}
// this.deferred.resolve(this.contents);
};
EPUBJS.Chapter.prototype.cfiFromRange = function(_range) {
var range;
var startXpath, endXpath;
var startContainer, endContainer;
var cleanTextContent, cleanEndTextContent;
// Check for Contents
if(!this.document) return;
if(typeof document.evaluate != 'undefined') {
startXpath = EPUBJS.core.getElementXPath(_range.startContainer);
// console.log(startContainer)
endXpath = EPUBJS.core.getElementXPath(_range.endContainer);
startContainer = this.document.evaluate(startXpath, this.document, EPUBJS.core.nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
if(!_range.collapsed) {
endContainer = this.document.evaluate(endXpath, this.document, EPUBJS.core.nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}
range = this.document.createRange();
// Find Exact Range in original document
if(startContainer) {
try {
range.setStart(startContainer, _range.startOffset);
if(!_range.collapsed && endContainer) {
range.setEnd(endContainer, _range.endOffset);
}
} catch (e) {
console.log("missed");
startContainer = false;
}
}
// Fuzzy Match
if(!startContainer) {
console.log("not found, try fuzzy match");
cleanStartTextContent = EPUBJS.core.cleanStringForXpath(_range.startContainer.textContent);
startXpath = "//text()[contains(.," + cleanStartTextContent + ")]";
startContainer = this.document.evaluate(startXpath, this.document, EPUBJS.core.nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
if(startContainer){
// console.log("Found with Fuzzy");
range.setStart(startContainer, _range.startOffset);
if(!_range.collapsed) {
cleanEndTextContent = EPUBJS.core.cleanStringForXpath(_range.endContainer.textContent);
endXpath = "//text()[contains(.," + cleanEndTextContent + ")]";
endContainer = this.document.evaluate(endXpath, this.document, EPUBJS.core.nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
if(endContainer) {
range.setEnd(endContainer, _range.endOffset);
}
}
}
}
} else {
range = _range; // Just evaluate the current documents range
}
// Generate the Cfi
return this.epubcfi.generateCfiFromRange(range, this.cfiBase);
};
EPUBJS.Chapter.prototype.find = function(_query){
var chapter = this;
var matches = [];
var query = _query.toLowerCase();
//var xpath = this.document.evaluate(".//text()[contains(translate(., '"+query.toUpperCase()+"', '"+query+"'),'"+query+"')]", this.document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
var find = function(node){
// Search String
var text = node.textContent.toLowerCase();
var range = chapter.document.createRange();
var cfi;
var pos;
var last = -1;
var excerpt;
var limit = 150;
while (pos != -1) {
pos = text.indexOf(query, last + 1);
if(pos != -1) {
// If Found, Create Range
range = chapter.document.createRange();
range.setStart(node, pos);
range.setEnd(node, pos + query.length);
//Generate CFI
cfi = chapter.cfiFromRange(range);
// Generate Excerpt
if(node.textContent.length < limit) {
excerpt = node.textContent;
} else {
excerpt = node.textContent.substring(pos-limit/2,pos+limit/2);
excerpt = "..." + excerpt + "...";
}
//Add CFI to list
matches.push({
cfi: cfi,
excerpt: excerpt
});
}
last = pos;
}
};
// Grab text nodes
/*
for ( var i=0 ; i < xpath.snapshotLength; i++ ) {
find(xpath.snapshotItem(i));
}
*/
this.textSprint(this.document, function(node){
find(node);
});
// Return List of CFIs
return matches;
};
EPUBJS.Chapter.prototype.textSprint = function(root, func) {
var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
acceptNode: function (node) {
if (node.data && ! /^\s*$/.test(node.data) ) {
return NodeFilter.FILTER_ACCEPT;
} else {
return NodeFilter.FILTER_REJECT;
}
}
}, false);
var node;
while ((node = treeWalker.nextNode())) {
func(node);
}
};
EPUBJS.Chapter.prototype.replace = function(query, func, finished, progress){
var items = this.contents.querySelectorAll(query),
resources = Array.prototype.slice.call(items),
count = resources.length;
if(count === 0) {
finished(false);
return;
}
resources.forEach(function(item){
var called = false;
var after = function(result, full){
if(called === false) {
count--;
if(progress) progress(result, full, count);
if(count <= 0 && finished) finished(true);
called = true;
}
};
func(item, after);
}.bind(this));
};
EPUBJS.Chapter.prototype.replaceWithStored = function(query, attr, func, callback) {
var _oldUrls,
_newUrls = {},
_store = this.store,
_cache = this.caches[query],
_uri = EPUBJS.core.uri(this.absolute),
_chapterBase = _uri.base,
_attr = attr,
_wait = 5,
progress = function(url, full, count) {
_newUrls[full] = url;
},
finished = function(notempty) {
if(callback) callback();
EPUBJS.core.values(_oldUrls).forEach(function(url){
_store.revokeUrl(url);
});
_cache = _newUrls;
};
if(!_store) return;
if(!_cache) _cache = {};
_oldUrls = EPUBJS.core.clone(_cache);
this.replace(query, function(link, done){
var src = link.getAttribute(_attr),
full = EPUBJS.core.resolveUrl(_chapterBase, src);
var replaceUrl = function(url) {
var timeout;
link.onload = function(){
clearTimeout(timeout);
done(url, full);
};
link.onerror = function(e){
clearTimeout(timeout);
done(url, full);
console.error(e);
};
if(query == "svg image") {
//-- SVG needs this to trigger a load event
link.setAttribute("externalResourcesRequired", "true");
}
if(query == "link[href]" && link.getAttribute("rel") !== "stylesheet") {
//-- Only Stylesheet links seem to have a load events, just continue others
done(url, full);
} else {
timeout = setTimeout(function(){
done(url, full);
}, _wait);
}
link.setAttribute(_attr, url);
};
if(full in _oldUrls){
replaceUrl(_oldUrls[full]);
_newUrls[full] = _oldUrls[full];
delete _oldUrls[full];
}else{
func(_store, full, replaceUrl, link);
}
}, finished, progress);
};