summaryrefslogtreecommitdiff
path: root/lib/epub.js/src/spine.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/epub.js/src/spine.js')
-rw-r--r--lib/epub.js/src/spine.js274
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;