diff --git a/epub.js b/epub.js index 1fd73f4..8310ce3 100644 --- a/epub.js +++ b/epub.js @@ -55,12 +55,12 @@ try { class EPub extends EventEmitter { constructor(fname, imageroot, linkroot) { super(); - + this.filename = fname; - + this.imageroot = (imageroot || "/images/").trim(); this.linkroot = (linkroot || "/links/").trim(); - + if (this.imageroot.substr(-1) != "/") { this.imageroot += "/"; } @@ -68,7 +68,7 @@ class EPub extends EventEmitter { this.linkroot += "/"; } } - + /** * EPub#parse(options) -> undefined * - options (object): An optional options object to override xml2jsOptions @@ -80,17 +80,17 @@ class EPub extends EventEmitter { this.containerFile = false; this.mimeFile = false; this.rootFile = false; - + this.metadata = {}; this.manifest = {}; this.guide = []; this.spine = {toc: false, contents: []}; this.flow = []; this.toc = []; - + this.open(); } - + /** * EPub#open() -> undefined @@ -105,15 +105,15 @@ class EPub extends EventEmitter { this.emit("error", new Error("Invalid/missing file")); return; } - + if (!this.zip.names || !this.zip.names.length) { this.emit("error", new Error("No files in archive")); return; } - + this.checkMimeType(); }; - + /** * EPub#checkMimeType() -> undefined * @@ -122,7 +122,7 @@ class EPub extends EventEmitter { **/ checkMimeType() { var i, len; - + for (i = 0, len = this.zip.names.length; i < len; i++) { if (this.zip.names[i].toLowerCase() == "mimetype") { this.mimeFile = this.zip.names[i]; @@ -139,16 +139,16 @@ class EPub extends EventEmitter { return; } var txt = data.toString("utf-8").toLowerCase().trim(); - + if (txt != "application/epub+zip") { this.emit("error", new Error("Unsupported mime type")); return; } - + this.getRootFiles(); }).bind(this)); }; - + /** * EPub#getRootFiles() -> undefined * @@ -168,7 +168,7 @@ class EPub extends EventEmitter { this.emit("error", new Error("No container file in archive")); return; } - + this.zip.readFile(this.containerFile, (function (err, data) { if (err) { this.emit("error", new Error("Reading archive failed")); @@ -176,20 +176,20 @@ class EPub extends EventEmitter { } var xml = data.toString("utf-8").toLowerCase().trim(), xmlparser = new xml2js.Parser(xml2jsOptions); - + xmlparser.on("end", (function (result) { - + if (!result.rootfiles || !result.rootfiles.rootfile) { this.emit("error", new Error("No rootfiles found")); console.dir(result); return; } - + var rootfile = result.rootfiles.rootfile, filename = false, i, len; - + if (Array.isArray(rootfile)) { - + for (i = 0, len = rootfile.length; i < len; i++) { if (rootfile[i]["@"]["media-type"] && rootfile[i]["@"]["media-type"] == "application/oebps-package+xml" && @@ -198,7 +198,7 @@ class EPub extends EventEmitter { break; } } - + } else if (rootfile["@"]) { if (rootfile["@"]["media-type"] != "application/oebps-package+xml" || !rootfile["@"]["full-path"]) { this.emit("error", new Error("Rootfile in unknown format")); @@ -206,40 +206,40 @@ class EPub extends EventEmitter { } filename = rootfile["@"]["full-path"].toLowerCase().trim(); } - + if (!filename) { this.emit("error", new Error("Empty rootfile")); return; } - - + + for (i = 0, len = this.zip.names.length; i < len; i++) { if (this.zip.names[i].toLowerCase() == filename) { this.rootFile = this.zip.names[i]; break; } } - + if (!this.rootFile) { this.emit("error", new Error("Rootfile not found from archive")); return; } - + this.handleRootFile(); - + }).bind(this)); - + xmlparser.on("error", (function (err) { - this.emit("error", new Error("Parsing container XML failed")); + this.emit("error", new Error("Parsing container XML failed in getRootFiles: " + err.message)); return; }).bind(this)); - + xmlparser.parseString(xml); - - + + }).bind(this)); }; - + /** * EPub#handleRootFile() -> undefined * @@ -254,19 +254,19 @@ class EPub extends EventEmitter { } var xml = data.toString("utf-8"), xmlparser = new xml2js.Parser(xml2jsOptions); - + xmlparser.on("end", this.parseRootFile.bind(this)); - + xmlparser.on("error", (function (err) { - this.emit("error", new Error("Parsing container XML failed")); + this.emit("error", new Error("Parsing container XML failed in handleRootFile: " + err.message)); return; }).bind(this)); - + xmlparser.parseString(xml); - + }).bind(this)); }; - + /** * EPub#parseRootFile() -> undefined * @@ -274,9 +274,9 @@ class EPub extends EventEmitter { * Emits "end" if no TOC **/ parseRootFile(rootfile) { - + this.version = rootfile['@'].version || '2.0'; - + var i, len, keys, keyparts, key; keys = Object.keys(rootfile); for (i = 0, len = keys.length; i < len; i++) { @@ -297,14 +297,14 @@ class EPub extends EventEmitter { break; } } - + if (this.spine.toc) { this.parseTOC(); } else { this.emit("end"); } }; - + /** * EPub#parseMetadata() -> undefined * @@ -312,7 +312,7 @@ class EPub extends EventEmitter { **/ parseMetadata(metadata) { var i, j, len, keys, keyparts, key; - + keys = Object.keys(metadata); for (i = 0, len = keys.length; i < len; i++) { keyparts = keys[i].split(":"); @@ -388,7 +388,7 @@ class EPub extends EventEmitter { break; } } - + var metas = metadata['meta'] || {}; Object.keys(metas).forEach(function(key) { var meta = metas[key]; @@ -399,13 +399,13 @@ class EPub extends EventEmitter { if (meta['#'] && meta['@'].property) { this.metadata[meta['@'].property] = meta['#']; } - + if(meta.name && meta.name =="cover"){ this.metadata[meta.name] = meta.content; } }, this); }; - + /** * EPub#parseManifest() -> undefined * @@ -415,23 +415,23 @@ class EPub extends EventEmitter { var i, len, path = this.rootFile.split("/"), element, path_str; path.pop(); path_str = path.join("/"); - + if (manifest.item) { for (i = 0, len = manifest.item.length; i < len; i++) { if (manifest.item[i]['@']) { element = manifest.item[i]['@']; - + if (element.href && element.href.substr(0, path_str.length) != path_str) { element.href = path.concat([element.href]).join("/"); } - + this.manifest[manifest.item[i]['@'].id] = element; - + } } } }; - + /** * EPub#parseGuide() -> undefined * @@ -462,7 +462,7 @@ class EPub extends EventEmitter { } } }; - + /** * EPub#parseSpine() -> undefined * @@ -471,11 +471,11 @@ class EPub extends EventEmitter { parseSpine(spine) { var i, len, path = this.rootFile.split("/"), element; path.pop(); - + if (spine['@'] && spine['@'].toc) { this.spine.toc = this.manifest[spine['@'].toc] || false; } - + if (spine.itemref) { if(!Array.isArray(spine.itemref)){ spine.itemref = [spine.itemref]; @@ -490,7 +490,7 @@ class EPub extends EventEmitter { } this.flow = this.spine.contents; }; - + /** * EPub#parseTOC() -> undefined * @@ -499,12 +499,12 @@ class EPub extends EventEmitter { parseTOC() { var i, len, path = this.spine.toc.href.split("/"), id_list = {}, keys; path.pop(); - + keys = Object.keys(this.manifest); for (i = 0, len = keys.length; i < len; i++) { id_list[this.manifest[keys[i]].href] = keys[i]; } - + this.zip.readFile(this.spine.toc.href, (function (err, data) { if (err) { this.emit("error", new Error("Reading archive failed")); @@ -512,25 +512,25 @@ class EPub extends EventEmitter { } var xml = data.toString("utf-8"), xmlparser = new xml2js.Parser(xml2jsOptions); - + xmlparser.on("end", (function (result) { if (result.navMap && result.navMap.navPoint) { this.toc = this.walkNavMap(result.navMap.navPoint, path, id_list); } - + this.emit("end"); }).bind(this)); - + xmlparser.on("error", (function (err) { - this.emit("error", new Error("Parsing container XML failed")); + this.emit("error", new Error("Parsing container XML failed in TOC: " + err.message)); return; }).bind(this)); - + xmlparser.parseString(xml); - + }).bind(this)); }; - + /** * EPub#walkNavMap(branch, path, id_list,[, level]) -> Array * - branch (Array | Object): NCX NavPoint object @@ -543,21 +543,21 @@ class EPub extends EventEmitter { **/ walkNavMap(branch, path, id_list, level) { level = level || 0; - + // don't go too far if (level > 7) { return []; } - + var output = []; - + if (!Array.isArray(branch)) { branch = [branch]; } - + for (var i = 0; i < branch.length; i++) { if (branch[i].navLabel) { - + var title = ''; if (branch[i].navLabel && typeof branch[i].navLabel.text == 'string') { title = branch[i].navLabel && branch[i].navLabel.text || branch[i].navLabel===branch[i].navLabel && branch[i].navLabel.text.length > 0 ? @@ -571,17 +571,17 @@ class EPub extends EventEmitter { if (branch[i].content && branch[i].content["@"] && typeof branch[i].content["@"].src == 'string') { href = branch[i].content["@"].src.trim(); } - + var element = { level: level, order: order, title: title }; - + if (href) { href = path.concat([href]).join("/"); element.href = href; - + if (id_list[element.href]) { // link existing object element = this.manifest[id_list[element.href]]; @@ -593,7 +593,7 @@ class EPub extends EventEmitter { element.href = href; element.id = (branch[i]["@"] && branch[i]["@"].id || "").trim(); } - + output.push(element); } } @@ -603,7 +603,7 @@ class EPub extends EventEmitter { } return output; }; - + /** * EPub#getChapter(id, callback) -> undefined * - id (String): Manifest id value for a chapter @@ -618,88 +618,88 @@ class EPub extends EventEmitter { callback(err); return; } - + var i, len, path = this.rootFile.split("/"), keys = Object.keys(this.manifest); path.pop(); - + // remove linebreaks (no multi line matches in JS regex!) str = str.replace(/\r?\n/g, "\u0000"); - + // keep only contents str.replace(/]*?>(.*)<\/body[^>]*?>/i, function (o, d) { str = d.trim(); }); - + // remove