mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-07-11 11:51:36 +00:00
got it to build, quick n dirty; epubjs patch
This commit is contained in:
parent
c67c586c98
commit
2baf50da12
43 changed files with 14616 additions and 203 deletions
357
client/static/libs/epubjs-patch/navigation.js
Normal file
357
client/static/libs/epubjs-patch/navigation.js
Normal file
|
|
@ -0,0 +1,357 @@
|
|||
import {qs, qsa, querySelectorByType, filterChildren, getParentByTagName} from "./utils/core";
|
||||
|
||||
/**
|
||||
* Navigation Parser
|
||||
* @param {document} xml navigation html / xhtml / ncx
|
||||
*/
|
||||
class Navigation {
|
||||
constructor(xml) {
|
||||
this.toc = [];
|
||||
this.tocByHref = {};
|
||||
this.tocById = {};
|
||||
|
||||
this.landmarks = [];
|
||||
this.landmarksByType = {};
|
||||
|
||||
this.length = 0;
|
||||
if (xml) {
|
||||
this.parse(xml);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse out the navigation items
|
||||
* @param {document} xml navigation html / xhtml / ncx
|
||||
*/
|
||||
parse(xml) {
|
||||
let isXml = xml.nodeType;
|
||||
let html;
|
||||
let ncx;
|
||||
|
||||
if (isXml) {
|
||||
html = qs(xml, "html");
|
||||
ncx = qs(xml, "ncx");
|
||||
}
|
||||
|
||||
if (!isXml) {
|
||||
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);
|
||||
}
|
||||
|
||||
this.length = 0;
|
||||
|
||||
this.unpack(this.toc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack navigation items
|
||||
* @private
|
||||
* @param {array} toc
|
||||
*/
|
||||
unpack(toc) {
|
||||
var item;
|
||||
|
||||
for (var i = 0; i < toc.length; i++) {
|
||||
item = toc[i];
|
||||
|
||||
if (item.href) {
|
||||
this.tocByHref[item.href] = i;
|
||||
}
|
||||
|
||||
if (item.id) {
|
||||
this.tocById[item.id] = i;
|
||||
}
|
||||
|
||||
this.length++;
|
||||
|
||||
if (item.subitems.length) {
|
||||
this.unpack(item.subitems);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an item from the navigation
|
||||
* @param {string} target
|
||||
* @return {object} navItem
|
||||
*/
|
||||
get(target) {
|
||||
var index;
|
||||
|
||||
if(!target) {
|
||||
return this.toc;
|
||||
}
|
||||
|
||||
if(target.indexOf("#") === 0) {
|
||||
index = this.tocById[target.substring(1)];
|
||||
} else if(target in this.tocByHref){
|
||||
index = this.tocByHref[target];
|
||||
}
|
||||
|
||||
return this.getByIndex(target, index, this.toc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an item from navigation subitems recursively by index
|
||||
* @param {string} target
|
||||
* @param {number} index
|
||||
* @param {array} navItems
|
||||
* @return {object} navItem
|
||||
*/
|
||||
getByIndex(target, index, navItems) {
|
||||
if (navItems.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const item = navItems[index];
|
||||
if (item && (target === item.id || target === item.href)) {
|
||||
return item;
|
||||
} else {
|
||||
let result;
|
||||
for (let i = 0; i < navItems.length; ++i) {
|
||||
result = this.getByIndex(target, index, navItems[i].subitems);
|
||||
if (result) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a landmark by type
|
||||
* List of types: https://idpf.github.io/epub-vocabs/structure/
|
||||
* @param {string} type
|
||||
* @return {object} landmarkItem
|
||||
*/
|
||||
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
|
||||
* @param {document} navHtml
|
||||
* @return {array} navigation list
|
||||
*/
|
||||
parseNav(navHtml){
|
||||
var navElement = querySelectorByType(navHtml, "nav", "toc");
|
||||
var list = [];
|
||||
|
||||
if (!navElement) return list;
|
||||
|
||||
let navList = filterChildren(navElement, "ol", true);
|
||||
if (!navList) return list;
|
||||
|
||||
list = this.parseNavList(navList);
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses lists in the toc
|
||||
* @param {document} navListHtml
|
||||
* @param {string} parent id
|
||||
* @return {array} navigation list
|
||||
*/
|
||||
parseNavList(navListHtml, parent) {
|
||||
const result = [];
|
||||
|
||||
if (!navListHtml) return result;
|
||||
if (!navListHtml.children) return result;
|
||||
|
||||
for (let i = 0; i < navListHtml.children.length; i++) {
|
||||
const item = this.navItem(navListHtml.children[i], parent);
|
||||
|
||||
if (item) {
|
||||
result.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a navItem
|
||||
* @private
|
||||
* @param {element} item
|
||||
* @return {object} navItem
|
||||
*/
|
||||
navItem(item, parent) {
|
||||
let id = item.getAttribute("id") || undefined;
|
||||
let content = filterChildren(item, "a", true)
|
||||
|| filterChildren(item, "span", true);
|
||||
|
||||
if (!content) {
|
||||
return;
|
||||
}
|
||||
|
||||
let src = content.getAttribute("href") || "";
|
||||
|
||||
if (!id) {
|
||||
id = src;
|
||||
}
|
||||
let text = content.textContent || "";
|
||||
|
||||
let subitems = [];
|
||||
let nested = filterChildren(item, "ol", true);
|
||||
if (nested) {
|
||||
subitems = this.parseNavList(nested, id);
|
||||
}
|
||||
|
||||
return {
|
||||
"id": id,
|
||||
"href": src,
|
||||
"label": text,
|
||||
"subitems" : subitems,
|
||||
"parent" : parent
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
this.landmarksByType[item.type] = i;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
* @param {document} navHtml
|
||||
* @return {array} navigation list
|
||||
*/
|
||||
parseNcx(tocXml){
|
||||
var navPoints = qsa(tocXml, "navPoint");
|
||||
var length = navPoints.length;
|
||||
var i;
|
||||
var toc = {};
|
||||
var list = [];
|
||||
var item, parent;
|
||||
|
||||
if(!navPoints || length === 0) return list;
|
||||
|
||||
for (i = 0; i < length; ++i) {
|
||||
item = this.ncxItem(navPoints[i]);
|
||||
toc[item.id] = item;
|
||||
if(!item.parent) {
|
||||
list.push(item);
|
||||
} else {
|
||||
parent = toc[item.parent];
|
||||
parent.subitems.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ncxItem
|
||||
* @private
|
||||
* @param {element} item
|
||||
* @return {object} ncxItem
|
||||
*/
|
||||
ncxItem(item){
|
||||
var id = item.getAttribute("id") || false,
|
||||
content = qs(item, "content"),
|
||||
src = content.getAttribute("src"),
|
||||
navLabel = qs(item, "navLabel"),
|
||||
text = navLabel.textContent ? navLabel.textContent : "",
|
||||
subitems = [],
|
||||
parentNode = item.parentNode,
|
||||
parent;
|
||||
|
||||
if(parentNode && (parentNode.nodeName === "navPoint" || parentNode.nodeName.split(':').slice(-1)[0] === "navPoint")) {
|
||||
parent = parentNode.getAttribute("id");
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
"id": id,
|
||||
"href": src,
|
||||
"label": text,
|
||||
"subitems" : subitems,
|
||||
"parent" : parent
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Spine Items
|
||||
* @param {object} json the items to be loaded
|
||||
* @return {Array} navItems
|
||||
*/
|
||||
load(json) {
|
||||
return json.map(item => {
|
||||
item.label = item.title;
|
||||
item.subitems = item.children ? this.load(item.children) : [];
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* forEach pass through
|
||||
* @param {Function} fn function to run on each item
|
||||
* @return {method} forEach loop
|
||||
*/
|
||||
forEach(fn) {
|
||||
return this.toc.forEach(fn);
|
||||
}
|
||||
}
|
||||
|
||||
export default Navigation;
|
||||
Loading…
Add table
Add a link
Reference in a new issue