diff --git a/books b/books index 950c742..ab9755a 160000 --- a/books +++ b/books @@ -1 +1 @@ -Subproject commit 950c742b3d66cc7ac53bd0663a41315f001da1c4 +Subproject commit ab9755a74714b647290c861f666515de220935d8 diff --git a/src/book.js b/src/book.js index f465cd5..9d6f328 100644 --- a/src/book.js +++ b/src/book.js @@ -1129,6 +1129,19 @@ EPUBJS.Book.prototype.setGap = function(gap) { } }; +EPUBJS.Book.prototype.chapter = function(path) { + var spinePos = this.spineIndexByURL[path]; + var spineItem; + var chapter; + + if(spinePos){ + spineItem = this.spine[spinePos]; + chapter = new EPUBJS.Chapter(spineItem, this.store); + chapter.load(); + } + return chapter; +}; + EPUBJS.Book.prototype.unload = function(){ if(this.settings.restore && localStorage) { diff --git a/src/chapter.js b/src/chapter.js index b4fbf72..4b6cb92 100644 --- a/src/chapter.js +++ b/src/chapter.js @@ -10,18 +10,26 @@ EPUBJS.Chapter = function(spineObject, store){ this.pages = 1; this.store = store; this.epubcfi = new EPUBJS.EpubCFI(); + this.deferred = new RSVP.defer(); + this.loaded = this.deferred.promise; }; -EPUBJS.Chapter.prototype.contents = function(_store){ +EPUBJS.Chapter.prototype.load = function(_store){ var store = _store || this.store; + var promise; // if(this.store && (!this.book.online || this.book.contained)) if(store){ - return store.get(href); + promise = store.get(this.href); }else{ - return EPUBJS.core.request(href, 'xml'); + promise = EPUBJS.core.request(this.absolute, 'xml'); } - + + promise.then(function(xml){ + this.setDocument(xml); + }.bind(this)); + + return promise; }; EPUBJS.Chapter.prototype.url = function(_store){ @@ -71,13 +79,27 @@ EPUBJS.Chapter.prototype.getID = function(){ }; EPUBJS.Chapter.prototype.unload = function(store){ - this.contents = null; + this.document = null; if(this.tempUrl && store) { store.revokeUrl(this.tempUrl); this.tempUrl = false; } }; +EPUBJS.Chapter.prototype.setDocument = function(_document){ + this.document = _document.implementation.createDocument( + _document.namespaceURI, //namespace to use + null, //empty document + _document.doctype //doctype (null for XML) + ); + this.contents = this.document.importNode( + _document.documentElement, //node to import + true //clone its descendants + ); + this.document.appendChild(this.contents); + this.deferred.resolve(this.contents); +}; + EPUBJS.Chapter.prototype.cfiFromRange = function(_range) { var range; var startXpath, endXpath; @@ -85,17 +107,17 @@ EPUBJS.Chapter.prototype.cfiFromRange = function(_range) { var cleanTextContent, cleanEndTextContent; // Check for Contents - if(!this.contents) return; + if(!this.document) return; startXpath = EPUBJS.core.getElementXPath(_range.startContainer); // console.log(startContainer) endXpath = EPUBJS.core.getElementXPath(_range.endContainer); - startContainer = this.contents.evaluate(startXpath, this.contents, EPUBJS.core.nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; + startContainer = this.document.evaluate(startXpath, this.document, EPUBJS.core.nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; if(!_range.collapsed) { - endContainer = this.contents.evaluate(endXpath, this.contents, EPUBJS.core.nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; + endContainer = this.document.evaluate(endXpath, this.document, EPUBJS.core.nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; } - range = this.contents.createRange(); + range = this.document.createRange(); // Find Exact Range in original document if(startContainer) { try { @@ -116,7 +138,7 @@ EPUBJS.Chapter.prototype.cfiFromRange = function(_range) { cleanStartTextContent = EPUBJS.core.cleanStringForXpath(_range.startContainer.textContent); startXpath = "//text()[contains(.," + cleanStartTextContent + ")]"; - startContainer = this.contents.evaluate(startXpath, this.contents, EPUBJS.core.nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; + startContainer = this.document.evaluate(startXpath, this.document, EPUBJS.core.nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; if(startContainer){ // console.log("Found with Fuzzy"); @@ -125,7 +147,7 @@ EPUBJS.Chapter.prototype.cfiFromRange = function(_range) { if(!_range.collapsed) { cleanEndTextContent = EPUBJS.core.cleanStringForXpath(_range.endContainer.textContent); endXpath = "//text()[contains(.," + cleanEndTextContent + ")]"; - endContainer = this.contents.evaluate(endXpath, this.contents, EPUBJS.core.nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; + endContainer = this.document.evaluate(endXpath, this.document, EPUBJS.core.nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; if(endContainer) { range.setEnd(endContainer, _range.endOffset); } @@ -137,3 +159,71 @@ EPUBJS.Chapter.prototype.cfiFromRange = function(_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; + + 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); + //Add CFI to list + matches.push(cfi); + } + + 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 ( ! /^\s*$/.test(node.data) ) { + return NodeFilter.FILTER_ACCEPT; + } else { + return NodeFilter.FILTER_REJECT; + } + } + }, false); + var node; + while ((node = treeWalker.nextNode())) { + func(node); + } + +}; \ No newline at end of file diff --git a/src/renderer.js b/src/renderer.js index 4a731dd..071eb57 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -153,7 +153,7 @@ EPUBJS.Renderer.prototype.load = function(url){ render.then(function(contents) { var formated; - this.currentChapter.contents = this.render.document; + this.currentChapter.setDocument(this.render.document); this.contents = contents; this.doc = this.render.document; diff --git a/tests/chapter.js b/tests/chapter.js new file mode 100644 index 0000000..d59fd87 --- /dev/null +++ b/tests/chapter.js @@ -0,0 +1,80 @@ +module('Chapter'); +asyncTest("Create a Chapter", 2, function() { + var book = ePub('/reader/moby-dick/', { width: 400, height: 600 }); + + book.ready.all.then(function(){ + var chapter = book.chapter("chapter_001.xhtml"); + equal(chapter.spinePos, 6, "Chapter Pos is correct" ); + equal(chapter.href, "chapter_001.xhtml", "Chapter href is correct" ); + start(); + }); + +}); + +asyncTest("Load a Chapter", 2, function() { + var book = ePub('/reader/moby-dick/', { width: 400, height: 600 }); + + book.ready.all.then(function(){ + var chapter = book.chapter("chapter_001.xhtml"); + // var loaded = chapter.load(); + start(); + equal(chapter.href, "chapter_001.xhtml", "Chapter href is correct" ); + stop(); + + chapter.loaded.then(function(){ + equal(chapter.document.firstChild.nodeName, "html", "Document HTML is loaded" ); + start(); + }); + + + }); + +}); + +asyncTest("Find a single query in a Chapter", 3, function() { + var book = ePub('/reader/moby-dick/', { width: 400, height: 600 }); + + book.ready.all.then(function(){ + var chapter = book.chapter("chapter_001.xhtml"); + // var loaded = chapter.load(); + start(); + equal(chapter.href, "chapter_001.xhtml", "Chapter href is correct" ); + stop(); + + chapter.loaded.then(function(){ + var results = chapter.find("pythagorean maxim"); + + equal(results.length, 1, "Results are returned" ); + equal(results[0], "epubcfi(/6/14[xchapter_001]!4/2/24/2[c001p0011]/1:227,4/2/24/2[c001p0011]/1:244)", "CFI is generated"); + + start(); + }); + + + }); + +}); + +asyncTest("Find a query with several results in a Chapter", 3, function() { + var book = ePub('/reader/moby-dick/', { width: 400, height: 600 }); + + book.ready.all.then(function(){ + var chapter = book.chapter("chapter_001.xhtml"); + // var loaded = chapter.load(); + start(); + equal(chapter.href, "chapter_001.xhtml", "Chapter href is correct"); + stop(); + + chapter.loaded.then(function(){ + var results = chapter.find("yet"); + + equal(results.length, 4, "Results are returned" ); + equal(results[3], "epubcfi(/6/14[xchapter_001]!4/2/28/2[c001p0015]/1:314,4/2/28/2[c001p0015]/1:317)", "CFI is generated"); + + start(); + }); + + + }); + +}); \ No newline at end of file diff --git a/tests/index.html b/tests/index.html index 8da01a2..77b0579 100644 --- a/tests/index.html +++ b/tests/index.html @@ -51,6 +51,7 @@ +