From 5f53b7223c74b2953820b76b9499bf3eee5b8184 Mon Sep 17 00:00:00 2001 From: Fred Chasen Date: Tue, 31 Oct 2017 18:27:26 -0700 Subject: [PATCH] Update toc nav subitem parsing --- examples/hypothesis.js | 16 +++++- src/navigation.js | 110 +++++++++++++++++++++++------------------ src/utils/core.js | 40 +++++++++++++-- 3 files changed, 110 insertions(+), 56 deletions(-) diff --git a/examples/hypothesis.js b/examples/hypothesis.js index bfd58d3..8fe829b 100644 --- a/examples/hypothesis.js +++ b/examples/hypothesis.js @@ -102,14 +102,22 @@ var $nav = document.getElementById("toc"), docfrag = document.createDocumentFragment(); - toc.forEach(function(chapter, index) { + var processTocItem = function(chapter, parent) { var item = document.createElement("li"); var link = document.createElement("a"); link.id = "chap-" + chapter.id; link.textContent = chapter.label; link.href = chapter.href; item.appendChild(link); - docfrag.appendChild(item); + parent.appendChild(item); + + if (chapter.subitems.length) { + var ul = document.createElement("ul"); + item.appendChild(ul); + chapter.subitems.forEach(function(subchapter) { + processTocItem(subchapter, ul); + }); + } link.onclick = function(){ var url = link.getAttribute("href"); @@ -118,6 +126,10 @@ return false; }; + } + + toc.forEach(function(chapter) { + processTocItem(chapter, docfrag); }); $nav.appendChild(docfrag); diff --git a/src/navigation.js b/src/navigation.js index 9f10b05..c47a01a 100644 --- a/src/navigation.js +++ b/src/navigation.js @@ -1,4 +1,4 @@ -import {qs, qsa, querySelectorByType} from "./utils/core"; +import {qs, qsa, querySelectorByType, filterChildren, getParentByTagName} from "./utils/core"; /** * Navigation Parser @@ -37,6 +37,8 @@ class Navigation { this.toc = this.parseNcx(xml); } + this.length = 0; + this.unpack(this.toc); } @@ -50,11 +52,22 @@ class Navigation { for (var i = 0; i < toc.length; i++) { item = toc[i]; - this.tocByHref[item.href] = i; - this.tocById[item.id] = 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); + } } - this.length = toc.length; } /** @@ -78,40 +91,6 @@ class Navigation { return this.toc[index]; } - createTocItem(linkElement, id) { - var list = [], - tocLinkElms = linkElement.childNodes, - tocLinkArray = Array.prototype.slice.call(tocLinkElms); - - var index = id ? id : 0; - tocLinkArray.forEach((linkElm) => { - if (linkElm.nodeName.toLowerCase() === 'li') { - var tocLink = qs(linkElm, 'a'), - tocLinkData = { - id: -1, - href: tocLink.getAttribute('href'), - label: tocLink.textContent, - parent: null - }, - subItemElm = qs(linkElm, 'ol'); - index++; - tocLinkData.id = index; - if (id) { - tocLinkData.parent = id; - } - list.push(tocLinkData); - if (subItemElm) { - var subitems = this.createTocItem(subItemElm, index); - if (subitems && subitems.length > 0) { - index = index + subitems.length; - list = list.concat(subitems); - } - } - } - }); - return list; - } - /** * Parse from a Epub > 3.0 Nav * @private @@ -120,8 +99,29 @@ class Navigation { */ parseNav(navHtml){ var navElement = querySelectorByType(navHtml, "nav", "toc"); - var tocItems = qs(navElement, "ol"); - return this.createTocItem(tocItems); + var navItems = navElement ? qsa(navElement, "li") : []; + var length = navItems.length; + var i; + var toc = {}; + var list = []; + var item, parent; + + if(!navItems || length === 0) return list; + + for (i = 0; i < length; ++i) { + item = this.navItem(navItems[i]); + if (item) { + toc[item.id] = item; + if(!item.parent) { + list.push(item); + } else { + parent = toc[item.parent]; + parent.subitems.push(item); + } + } + } + + return list; } /** @@ -131,16 +131,28 @@ class Navigation { * @return {object} navItem */ navItem(item){ - var id = item.getAttribute("id") || false, - content = qs(item, "a"), - src = content.getAttribute("href") || "", - text = content.textContent || "", - subitems = [], - parentNode = item.parentNode, - parent; + let id = item.getAttribute("id") || undefined; + let content = filterChildren(item, "a", true); - if(parentNode && parentNode.nodeName === "navPoint") { - parent = parentNode.getAttribute("id"); + if (!content) { + return; + } + + let src = content.getAttribute("href") || ""; + let text = content.textContent || ""; + let subitems = []; + let parentItem = getParentByTagName(item, "li"); + let parent; + + if (parentItem) { + parent = parentItem.getAttribute("id"); + } + + while (!parent && parentItem) { + parentItem = getParentByTagName(parentItem, "li"); + if (parentItem) { + parent = parentItem.getAttribute("id"); + } } return { diff --git a/src/utils/core.js b/src/utils/core.js index 34c04bc..962eacf 100644 --- a/src/utils/core.js +++ b/src/utils/core.js @@ -503,11 +503,41 @@ export function findChildren(el) { } export function parents(node) { - var nodes = [node]; - for (; node; node = node.parentNode) { - nodes.unshift(node); - } - return nodes + var nodes = [node]; + for (; node; node = node.parentNode) { + nodes.unshift(node); + } + return nodes +} + +export function filterChildren(el, nodeName, single) { + var result = []; + var childNodes = el.childNodes; + for (var i = 0; i < childNodes.length; i++) { + let node = childNodes[i]; + if (node.nodeType === 1 && node.nodeName.toLowerCase() === nodeName) { + if (single) { + return node; + } else { + result.push(node); + } + } + } + if (!single) { + return result; + } +} + +export function getParentByTagName(node, tagname) { + let parent; + if (node === null || tagname === '') return; + parent = node.parentNode; + while (parent.nodeType === 1) { + if (parent.tagName.toLowerCase() === tagname) { + return parent; + } + parent = parent.parentNode; + } } export class RangeObject {