diff options
Diffstat (limited to 'lib/epub.js/src/spine.js')
-rw-r--r-- | lib/epub.js/src/spine.js | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/lib/epub.js/src/spine.js b/lib/epub.js/src/spine.js new file mode 100644 index 0000000..519b67c --- /dev/null +++ b/lib/epub.js/src/spine.js @@ -0,0 +1,274 @@ +import EpubCFI from "./epubcfi"; +import Hook from "./utils/hook"; +import Section from "./section"; +import {replaceBase, replaceCanonical, replaceMeta} from "./utils/replacements"; + +/** + * A collection of Spine Items + */ +class Spine { + constructor() { + this.spineItems = []; + this.spineByHref = {}; + this.spineById = {}; + + this.hooks = {}; + this.hooks.serialize = new Hook(); + this.hooks.content = new Hook(); + + // Register replacements + this.hooks.content.register(replaceBase); + this.hooks.content.register(replaceCanonical); + this.hooks.content.register(replaceMeta); + + this.epubcfi = new EpubCFI(); + + this.loaded = false; + + this.items = undefined; + this.manifest = undefined; + this.spineNodeIndex = undefined; + this.baseUrl = undefined; + this.length = undefined; + } + + /** + * Unpack items from a opf into spine items + * @param {Packaging} _package + * @param {method} resolver URL resolver + * @param {method} canonical Resolve canonical url + */ + unpack(_package, resolver, canonical) { + + this.items = _package.spine; + this.manifest = _package.manifest; + this.spineNodeIndex = _package.spineNodeIndex; + this.baseUrl = _package.baseUrl || _package.basePath || ""; + this.length = this.items.length; + + this.items.forEach( (item, index) => { + var manifestItem = this.manifest[item.idref]; + var spineItem; + + item.index = index; + item.cfiBase = this.epubcfi.generateChapterComponent(this.spineNodeIndex, item.index, item.idref); + + if (item.href) { + item.url = resolver(item.href, true); + item.canonical = canonical(item.href); + } + + if(manifestItem) { + item.href = manifestItem.href; + item.url = resolver(item.href, true); + item.canonical = canonical(item.href); + + if(manifestItem.properties.length){ + item.properties.push.apply(item.properties, manifestItem.properties); + } + } + + if (item.linear === "yes") { + item.prev = function() { + let prevIndex = item.index; + while (prevIndex > 0) { + let prev = this.get(prevIndex-1); + if (prev && prev.linear) { + return prev; + } + prevIndex -= 1; + } + return; + }.bind(this); + item.next = function() { + let nextIndex = item.index; + while (nextIndex < this.spineItems.length-1) { + let next = this.get(nextIndex+1); + if (next && next.linear) { + return next; + } + nextIndex += 1; + } + return; + }.bind(this); + } else { + item.prev = function() { + return; + } + item.next = function() { + return; + } + } + + + spineItem = new Section(item, this.hooks); + + this.append(spineItem); + + + }); + + this.loaded = true; + } + + /** + * Get an item from the spine + * @param {string|number} [target] + * @return {Section} section + * @example spine.get(); + * @example spine.get(1); + * @example spine.get("chap1.html"); + * @example spine.get("#id1234"); + */ + get(target) { + var index = 0; + + if (typeof target === "undefined") { + while (index < this.spineItems.length) { + let next = this.spineItems[index]; + if (next && next.linear) { + break; + } + index += 1; + } + } else if(this.epubcfi.isCfiString(target)) { + let cfi = new EpubCFI(target); + index = cfi.spinePos; + } else if(typeof target === "number" || isNaN(target) === false){ + index = target; + } else if(typeof target === "string" && target.indexOf("#") === 0) { + index = this.spineById[target.substring(1)]; + } else if(typeof target === "string") { + // Remove fragments + target = target.split("#")[0]; + index = this.spineByHref[target] || this.spineByHref[encodeURI(target)]; + } + + return this.spineItems[index] || null; + } + + /** + * Append a Section to the Spine + * @private + * @param {Section} section + */ + append(section) { + var index = this.spineItems.length; + section.index = index; + + this.spineItems.push(section); + + // Encode and Decode href lookups + // see pr for details: https://github.com/futurepress/epub.js/pull/358 + this.spineByHref[decodeURI(section.href)] = index; + this.spineByHref[encodeURI(section.href)] = index; + this.spineByHref[section.href] = index; + + this.spineById[section.idref] = index; + + return index; + } + + /** + * Prepend a Section to the Spine + * @private + * @param {Section} section + */ + prepend(section) { + // var index = this.spineItems.unshift(section); + this.spineByHref[section.href] = 0; + this.spineById[section.idref] = 0; + + // Re-index + this.spineItems.forEach(function(item, index){ + item.index = index; + }); + + return 0; + } + + // insert(section, index) { + // + // }; + + /** + * Remove a Section from the Spine + * @private + * @param {Section} section + */ + remove(section) { + var index = this.spineItems.indexOf(section); + + if(index > -1) { + delete this.spineByHref[section.href]; + delete this.spineById[section.idref]; + + return this.spineItems.splice(index, 1); + } + } + + /** + * Loop over the Sections in the Spine + * @return {method} forEach + */ + each() { + return this.spineItems.forEach.apply(this.spineItems, arguments); + } + + /** + * Find the first Section in the Spine + * @return {Section} first section + */ + first() { + let index = 0; + + do { + let next = this.get(index); + + if (next && next.linear) { + return next; + } + index += 1; + } while (index < this.spineItems.length) ; + } + + /** + * Find the last Section in the Spine + * @return {Section} last section + */ + last() { + let index = this.spineItems.length-1; + + do { + let prev = this.get(index); + if (prev && prev.linear) { + return prev; + } + index -= 1; + } while (index >= 0); + } + + destroy() { + this.each((section) => section.destroy()); + + this.spineItems = undefined + this.spineByHref = undefined + this.spineById = undefined + + this.hooks.serialize.clear(); + this.hooks.content.clear(); + this.hooks = undefined; + + this.epubcfi = undefined; + + this.loaded = false; + + this.items = undefined; + this.manifest = undefined; + this.spineNodeIndex = undefined; + this.baseUrl = undefined; + this.length = undefined; + } +} + +export default Spine; |