From c977fc10d4b13a4555ee0c6c9e0f98cfa9cf21df Mon Sep 17 00:00:00 2001 From: Fred Chasen Date: Mon, 6 Nov 2017 11:39:14 -0800 Subject: [PATCH 1/3] Clean up pageList --- .travis.yml | 8 ++++ karma.conf.js | 2 +- package-lock.json | 96 +++++++++++++++++++++++++++++++---------------- package.json | 5 ++- src/book.js | 7 +++- src/pagelist.js | 47 +---------------------- 6 files changed, 83 insertions(+), 82 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2795743f7..d0cd9d3b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,3 +3,11 @@ node_js: - "node" notifications: email: false +sudo: false +addons: + chrome: stable +cache: + directories: + - node_modules +script: + - npm test diff --git a/karma.conf.js b/karma.conf.js index 7bbf7449d..6bb9dc7b9 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -100,7 +100,7 @@ module.exports = function(config) { // start these browsers // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher - browsers: ['PhantomJS'], + browsers: ['ChromeHeadless'], // Continuous Integration mode diff --git a/package-lock.json b/package-lock.json index 99009e6d0..dc0f79a1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "epubjs", - "version": "0.3.53", + "version": "0.3.57", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -27,6 +27,16 @@ "through2": "2.0.3" } }, + "JSONStream": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", + "integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=", + "dev": true, + "requires": { + "jsonparse": "1.3.1", + "through": "2.3.8" + } + }, "abbrev": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", @@ -5478,6 +5488,15 @@ "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", "dev": true }, + "fs-access": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", + "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", + "dev": true, + "requires": { + "null-check": "1.0.0" + } + }, "fs-exists-sync": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", @@ -6276,14 +6295,6 @@ } } }, - "string_decoder": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, "string-width": { "version": "1.0.2", "bundled": true, @@ -6294,6 +6305,14 @@ "strip-ansi": "3.0.1" } }, + "string_decoder": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, "stringstream": { "version": "0.0.5", "bundled": true, @@ -7909,6 +7928,12 @@ "sntp": "1.0.9" } }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, "highlight.js": { "version": "9.12.0", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.12.0.tgz", @@ -8872,16 +8897,6 @@ "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", "dev": true }, - "JSONStream": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", - "integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=", - "dev": true, - "requires": { - "jsonparse": "1.3.1", - "through": "2.3.8" - } - }, "jsprim": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", @@ -9042,6 +9057,16 @@ } } }, + "karma-chrome-launcher": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz", + "integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==", + "dev": true, + "requires": { + "fs-access": "1.0.1", + "which": "1.2.14" + } + }, "karma-mocha": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/karma-mocha/-/karma-mocha-1.3.0.tgz", @@ -10181,9 +10206,9 @@ } }, "mocha": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.0.tgz", - "integrity": "sha512-pIU2PJjrPYvYRqVpjXzj76qltO9uBYI7woYAMoxbSefsa+vqAfptjoeevd6bUgwD0mPIO+hv9f7ltvsNreL2PA==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.3.tgz", + "integrity": "sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg==", "dev": true, "requires": { "browser-stdout": "1.3.0", @@ -10193,6 +10218,7 @@ "escape-string-regexp": "1.0.5", "glob": "7.1.1", "growl": "1.9.2", + "he": "1.1.1", "json3": "3.3.2", "lodash.create": "3.1.1", "mkdirp": "0.5.1", @@ -10270,13 +10296,13 @@ "integrity": "sha1-ElGkuixEqS32mJvQKdoSGk8hCbA=", "dev": true, "requires": { + "JSONStream": "1.3.1", "browser-resolve": "1.11.2", "concat-stream": "1.5.2", "defined": "1.0.0", "detective": "4.5.0", "duplexer2": "0.1.4", "inherits": "2.0.3", - "JSONStream": "1.3.1", "parents": "1.0.1", "readable-stream": "2.3.3", "resolve": "1.3.3", @@ -10612,6 +10638,12 @@ "path-key": "2.0.1" } }, + "null-check": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", + "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", + "dev": true + }, "num2fraction": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", @@ -13549,15 +13581,6 @@ "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", "dev": true }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, "string-template": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", @@ -13575,6 +13598,15 @@ "strip-ansi": "3.0.1" } }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, "stringify-entities": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.1.tgz", diff --git a/package.json b/package.json index 23c89ec37..e76c74e4d 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "test": "test" }, "scripts": { - "test": "./node_modules/.bin/karma start --single-run --browsers PhantomJS", + "test": "./node_modules/.bin/karma start --single-run --browsers ChromeHeadless", "documentation": "./node_modules/.bin/gulp docs", "lint": "./node_modules/.bin/eslint -c .eslintrc.js src; exit 0", "start": "./node_modules/.bin/webpack-dev-server --inline --d", @@ -51,12 +51,13 @@ "jquery": "^3.2.1", "jshint": "^2.9.5", "karma": "^1.7.0", + "karma-chrome-launcher": "^2.2.0", "karma-mocha": "^1.3.0", "karma-mocha-reporter": "^2.2.4", "karma-phantomjs-launcher": "^1.0.4", "karma-sourcemap-loader": "^0.3.7", "karma-webpack": "^2.0.4", - "mocha": "^3.5.0", + "mocha": "^3.5.3", "mocha-loader": "^1.1.1", "morgan": "^1.8.2", "optimist": "^0.6.1", diff --git a/src/book.js b/src/book.js index d9ca2ad04..dce04da93 100644 --- a/src/book.js +++ b/src/book.js @@ -125,7 +125,7 @@ class Book { /** * @property {PageList} pagelist */ - this.pageList = new PageList(); + this.pageList = undefined; /** * @property {Url} url @@ -441,11 +441,14 @@ class Book { let navPath = opf.navPath || opf.ncxPath; let toc = opf.toc; + // From json manifest if (toc) { return new Promise((resolve, reject) => { this.navigation = new Navigation(toc); - this.pageList = new PageList(); // TODO: handle page lists + if (opf.pageList) { + this.pageList = new PageList(opf.pageList); // TODO: handle page lists from Manifest + } resolve(this.navigation); }); diff --git a/src/pagelist.js b/src/pagelist.js index 36be13ace..c99094a97 100644 --- a/src/pagelist.js +++ b/src/pagelist.js @@ -24,7 +24,6 @@ class PageList { this.toc = undefined; this.ncx = undefined; - this.lastPage if (xml) { this.pageList = this.parse(xml); } @@ -43,9 +42,9 @@ class PageList { var ncx = qs(xml, "ncx"); if(html) { - this.toc = this.parseNav(xml); + return this.parseNav(xml); } else if(ncx){ // Not supported - // this.toc = this.parseNcx(xml); + // return this.parseNcx(xml); return; } @@ -126,48 +125,6 @@ class PageList { this.totalPages = this.lastPage - this.firstPage; } - - /** - * Replace HREFs with CFI - * TODO: implement getting CFI from Href - */ - addCFIs() { - this.pageList.forEach(function(pg){ - if(!pg.cfi) { - // epubcfi.generateCfiFromHref(pg.href, book).then(function(cfi){ - // pg.cfi = cfi; - // pg.packageUrl = book.settings.packageUrl; - // }); - } - }); - } - - /* - EPUBJS.generateCfiFromHref(href, book) { - var uri = EPUBJS.core.uri(href); - var path = uri.path; - var fragment = uri.fragment; - var spinePos = book.spineIndexByURL[path]; - var loaded; - var deferred = new RSVP.defer(); - var epubcfi = new EPUBJS.EpubCFI(); - var spineItem; - - if(typeof spinePos !== "undefined"){ - spineItem = book.spine[spinePos]; - loaded = book.loadXml(spineItem.url); - loaded.then(function(doc){ - var element = doc.getElementById(fragment); - var cfi; - cfi = epubcfi.generateCfiFromElement(element, spineItem.cfiBase); - deferred.resolve(cfi); - }); - } - - return deferred.promise; - } - */ - /** * Get a PageList result from a EpubCFI * @param {string} cfi EpubCFI String From 7596f180d8451929ad8088b809046f30933a22ae Mon Sep 17 00:00:00 2001 From: Fred Chasen Date: Mon, 6 Nov 2017 11:57:34 -0800 Subject: [PATCH 2/3] Add landmark parsing --- src/navigation.js | 54 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src/navigation.js b/src/navigation.js index c47a01a29..0ebeca030 100644 --- a/src/navigation.js +++ b/src/navigation.js @@ -7,6 +7,7 @@ import {qs, qsa, querySelectorByType, filterChildren, getParentByTagName} from " class Navigation { constructor(xml) { this.toc = []; + this.landmarks = []; this.tocByHref = {}; this.tocById = {}; this.length = 0; @@ -33,6 +34,7 @@ class Navigation { this.toc = this.load(xml); } else if(html) { this.toc = this.parseNav(xml); + this.landmarks = this.parseLandmarks(xml); } else if(ncx){ this.toc = this.parseNcx(xml); } @@ -92,7 +94,7 @@ class Navigation { } /** - * Parse from a Epub > 3.0 Nav + * Parse toc from a Epub > 3.0 Nav * @private * @param {document} navHtml * @return {array} navigation list @@ -164,6 +166,56 @@ class Navigation { }; } + /** + * Parse landmarks from a Epub > 3.0 Nav + * @private + * @param {document} navHtml + * @return {array} landmarks list + */ + parseLandmarks(navHtml){ + var navElement = querySelectorByType(navHtml, "nav", "landmarks"); + var navItems = navElement ? qsa(navElement, "li") : []; + var length = navItems.length; + var i; + var list = []; + var item; + + if(!navItems || length === 0) return list; + + for (i = 0; i < length; ++i) { + item = this.landmarkItem(navItems[i]); + if (item) { + list.push(item); + } + } + + return list; + } + + /** + * Create a landmarkItem + * @private + * @param {element} item + * @return {object} landmarkItem + */ + landmarkItem(item){ + let content = filterChildren(item, "a", true); + + if (!content) { + return; + } + + let type = content.getAttributeNS("http://www.idpf.org/2007/ops", "type") || undefined; + let href = content.getAttribute("href") || ""; + let text = content.textContent || ""; + + return { + "href": href, + "label": text, + "type" : type + }; + } + /** * Parse from a Epub > 3.0 NC * @private From 3f16c7492ebd7ca98d8a366e7cc29c14edb42122 Mon Sep 17 00:00:00 2001 From: Fred Chasen Date: Mon, 6 Nov 2017 12:09:59 -0800 Subject: [PATCH 3/3] Add landmark method --- src/navigation.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/navigation.js b/src/navigation.js index 0ebeca030..6e6867674 100644 --- a/src/navigation.js +++ b/src/navigation.js @@ -7,9 +7,12 @@ import {qs, qsa, querySelectorByType, filterChildren, getParentByTagName} from " class Navigation { constructor(xml) { this.toc = []; - this.landmarks = []; this.tocByHref = {}; this.tocById = {}; + + this.landmarks = []; + this.landmarksByType = {}; + this.length = 0; if (xml) { this.parse(xml); @@ -93,6 +96,24 @@ class Navigation { return this.toc[index]; } + /** + * Get a landmark by type + * List of types: https://idpf.github.io/epub-vocabs/structure/ + * @param {string} type + * @return {object} landmarkItems + */ + landmark(type) { + var index; + + if(!type) { + return this.landmarks; + } + + index = this.landmarksByType[type]; + + return this.landmarks[index]; + } + /** * Parse toc from a Epub > 3.0 Nav * @private @@ -186,6 +207,7 @@ class Navigation { item = this.landmarkItem(navItems[i]); if (item) { list.push(item); + this.landmarksByType[item.type] = i; } }