@readium/shared 2.3.1 → 2.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getTime as g,getHtmlId as v}from"../../html/Locations.js";import{Profile as p}from"../../Profiles.js";import{isNptStartOfResource as k,formatNptTime as I,parseNptTime as T}from"../../../util/npt.js";class l{constructor(i,t,e=[],s=[],n=void 0){this._trimmedLinkMap=new Map,this._allItems=i,this.linkMap=t,this._conformsTo=e,this.tocLinks=s,this._tocDepth=n}static build(i,t={}){const e=i.toc?.items??[],s=i.readingOrder.items,{depth:n}=t,o=i.metadata?.conformsTo??[],r=new Map,c=[];for(let h=0;h<s.length;h++){const f=s[h],a=l.bareHref(f.href),d=f.title??l.findTitleInToc(e,a,n),u=l.collectChildrenFromToc(e,a,n,1,r),m={title:d,references:[f.href],children:u.length>0?u:void 0};r.set(m,f),c.push(m)}return new l(c,r,o,e,n)}augment(i){for(const t of this.flatAll){const e=this.linkFor(t);if(!e)continue;const s=i(t,e);s.position!==void 0&&(t.position=s.position),s.scroll!==void 0&&(t.scroll=s.scroll),s.role!==void 0&&(t.role=s.role)}}get depth(){return this._depth}set depth(i){this._depth!==i&&(this._depth=i,this._tocDepth=i,this._items=void 0,this._flat=void 0,this._toc=void 0)}get items(){return this._items||(this._depth!==void 0?(this._trimmedLinkMap=new Map,this._items=l.trimToDepth(this._allItems,this._depth,this.linkMap,this._trimmedLinkMap)):this._items=this._allItems),this._items}locate(i){const t=i.href.split("#")[0],e=g(i.locations),s=v(i.locations),n=i.locations.progression;if(e!==void 0){let o=-1/0,r;for(const c of this.flat){if(!this.itemMatchesHref(c,t))continue;const h=this.itemStartTime(c,t);h!==void 0&&h<=e&&h>o&&(o=h,r=c)}if(r)return r}if(s){const o=this.flat.find(r=>this.itemMatchesHref(r,t)?r.references.some(c=>c.split("#")[1]===s):!1);if(o)return o}if(n!==void 0){let o=-1/0,r;for(const h of this.flat){if(!this.itemMatchesHref(h,t))continue;const f=this.itemScrollPosition(h);f!==void 0&&f<=n&&f>o&&(o=f,r=h)}if(r)return r;const c=this.items.find(h=>this.itemMatchesHref(h,t));if(c?.children?.length&&!c.children.some(h=>h.scroll!==void 0)){const h=Math.min(Math.floor(n*c.children.length),c.children.length-1);return c.children[h]}}return this.flat.find(o=>this.itemMatchesHref(o,t))}adjacentTo(i){const t=this.flat.indexOf(i);return{previous:t>0?this.flat[t-1]:void 0,next:t>=0&&t<this.flat.length-1?this.flat[t+1]:void 0}}segmentsForHref(i){const t=i.split("#")[0],e=this.items.find(s=>this.itemMatchesHref(s,t));return e?e.children?.length?e.children:[e]:[]}ancestors(i){return this.ancestorPath(this.items,i)??[]}linkFor(i){return this.linkMap.get(i)??this._trimmedLinkMap.get(i)}get contextualizedToc(){return this._toc||(this._toc=this.tocLinks.length>0?this.buildTocEntries(this.tocLinks,this._tocDepth,1):this._allItems.map(i=>this.entryFor(this.linkFor(i),i))),this._toc}tocEntryFor(i){const t=this.linkFor(i);if(!t)return;const e=this.findTocEntryByLink(this.contextualizedToc,t);if(e)return e;const s=this.nearestTocEntryForResource(t.href,i);return s||this.previousResolvedTocEntry(t)}get flat(){return this._flat||(this._flat=this.flattenItems(this.items)),this._flat}get flatAll(){return this.flattenItems(this._allItems)}static findTitleInToc(i,t,e){const{atStart:s,fragments:n}=l.collectTocCandidates(i,t,e,1);if(s.length>0)return s[0].title;if(n.length===1)return n[0].title}static collectChildrenFromToc(i,t,e,s,n){if(e!==void 0&&s>e)return[];const o=[];for(const r of i){const c=l.bareHref(r.href);if((c===t||c==="")&&r.title&&!l.isStartOfResource(r.href)){const
|
|
1
|
+
import{getTime as g,getHtmlId as v}from"../../html/Locations.js";import{Profile as p}from"../../Profiles.js";import{isNptStartOfResource as k,formatNptTime as I,parseNptTime as T}from"../../../util/npt.js";class l{constructor(i,t,e=[],s=[],n=void 0){this._trimmedLinkMap=new Map,this._allItems=i,this.linkMap=t,this._conformsTo=e,this.tocLinks=s,this._tocDepth=n}static build(i,t={}){const e=i.toc?.items??[],s=i.readingOrder.items,{depth:n}=t,o=i.metadata?.conformsTo??[],r=new Map,c=[];for(let h=0;h<s.length;h++){const f=s[h],a=l.bareHref(f.href),d=f.title??l.findTitleInToc(e,a,n),u=l.collectChildrenFromToc(e,a,n,1,r),m={title:d,references:[f.href],children:u.length>0?u:void 0};r.set(m,f),c.push(m)}return new l(c,r,o,e,n)}augment(i){for(const t of this.flatAll){const e=this.linkFor(t);if(!e)continue;const s=i(t,e);s.position!==void 0&&(t.position=s.position),s.scroll!==void 0&&(t.scroll=s.scroll),s.role!==void 0&&(t.role=s.role)}}get depth(){return this._depth}set depth(i){this._depth!==i&&(this._depth=i,this._tocDepth=i,this._items=void 0,this._flat=void 0,this._toc=void 0)}get items(){return this._items||(this._depth!==void 0?(this._trimmedLinkMap=new Map,this._items=l.trimToDepth(this._allItems,this._depth,this.linkMap,this._trimmedLinkMap)):this._items=this._allItems),this._items}locate(i){const t=i.href.split("#")[0],e=g(i.locations),s=v(i.locations),n=i.locations.progression;if(e!==void 0){let o=-1/0,r;for(const c of this.flat){if(!this.itemMatchesHref(c,t))continue;const h=this.itemStartTime(c,t);h!==void 0&&h<=e&&h>o&&(o=h,r=c)}if(r)return r}if(s){const o=this.flat.find(r=>this.itemMatchesHref(r,t)?r.references.some(c=>c.split("#")[1]===s):!1);if(o)return o}if(n!==void 0){let o=-1/0,r;for(const h of this.flat){if(!this.itemMatchesHref(h,t))continue;const f=this.itemScrollPosition(h);f!==void 0&&f<=n&&f>o&&(o=f,r=h)}if(r)return r;const c=this.items.find(h=>this.itemMatchesHref(h,t));if(c?.children?.length&&!c.children.some(h=>h.scroll!==void 0)){const h=Math.min(Math.floor(n*c.children.length),c.children.length-1);return c.children[h]}}return this.flat.find(o=>this.itemMatchesHref(o,t))}adjacentTo(i){const t=this.flat.indexOf(i);return{previous:t>0?this.flat[t-1]:void 0,next:t>=0&&t<this.flat.length-1?this.flat[t+1]:void 0}}segmentsForHref(i){const t=i.split("#")[0],e=this.items.find(s=>this.itemMatchesHref(s,t));return e?e.children?.length?e.children:[e]:[]}ancestors(i){return this.ancestorPath(this.items,i)??[]}linkFor(i){return this.linkMap.get(i)??this._trimmedLinkMap.get(i)}get contextualizedToc(){return this._toc||(this._toc=this.tocLinks.length>0?this.buildTocEntries(this.tocLinks,this._tocDepth,1):this._allItems.map(i=>this.entryFor(this.linkFor(i),i))),this._toc}tocEntryFor(i){const t=this.linkFor(i);if(!t)return;const e=this.findTocEntryByLink(this.contextualizedToc,t);if(e)return e;const s=this.nearestTocEntryForResource(t.href,i);return s||this.previousResolvedTocEntry(t)}get flat(){return this._flat||(this._flat=this.flattenItems(this.items)),this._flat}get flatAll(){return this.flattenItems(this._allItems)}static findTitleInToc(i,t,e){const{atStart:s,fragments:n}=l.collectTocCandidates(i,t,e,1);if(s.length>0)return s[0].title;if(n.length===1)return n[0].title}static collectChildrenFromToc(i,t,e,s,n){if(e!==void 0&&s>e)return[];const o=[];for(const r of i){const c=l.bareHref(r.href),h=r.href.startsWith("#");if((c===t||h&&c==="")&&r.title&&!l.isStartOfResource(r.href)){const a=c===""?t+r.href:r.href,d={title:r.title,references:[a]};n.set(d,r),o.push(d)}r.children?.items?.length&&o.push(...l.collectChildrenFromToc(r.children.items,t,e,s+1,n))}return o}static collectTocCandidates(i,t,e,s){if(e!==void 0&&s>e)return{atStart:[],fragments:[]};const n=[],o=[];for(const r of i){const c=l.bareHref(r.href),h=r.href.startsWith("#");if((c===t||h&&c==="")&&r.title&&(l.isStartOfResource(r.href)?n.push(r):o.push(r)),r.children?.items?.length){const f=l.collectTocCandidates(r.children.items,t,e,s+1);n.push(...f.atStart),o.push(...f.fragments)}}return{atStart:n,fragments:o}}static isStartOfResource(i){const t=i.split("#")[1];if(!t)return!0;const e=t.match(/(?:^|&)t=([^&]+)/);return e!==null&&k(e[1])}get linkToItem(){if(!this._linkToItem){this._linkToItem=new Map;for(const[i,t]of this.linkMap)this._linkToItem.set(t,i)}return this._linkToItem}buildTocEntries(i,t,e){return t!==void 0&&e>t?[]:i.map(s=>{const n=this.linkToItem.get(s)??this.resourceStartItem(s.href),o=s.children?.items?.length?this.buildTocEntries(s.children.items,t,e+1):[];return{...this.entryFor(s,n),children:o.length>0?o:void 0}})}tocLinksFlat(i,t,e){if(t!==void 0&&e>t)return[];const s=[];for(const n of i)s.push(n),n.children?.items?.length&&s.push(...this.tocLinksFlat(n.children.items,t,e+1));return s}entryFor(i,t){const e=this._conformsTo.includes(p.AUDIOBOOK);return{link:i,position:!e&&t?.position!==void 0?String(t.position):void 0,timestamp:e&&t?.position!==void 0?I(t.position):void 0}}resourceStartItem(i){const t=l.bareHref(i);return this._allItems.find(e=>this.itemMatchesHref(e,t))}findTocEntryByLink(i,t){for(const e of i){if(e.link===t)return e;if(e.children){const s=this.findTocEntryByLink(e.children,t);if(s)return s}}}nearestTocEntryForResource(i,t){const e=l.bareHref(i),s=this.tocLinksFlat(this.tocLinks,this._tocDepth,1).filter(f=>l.bareHref(f.href)===e).map(f=>({link:f,item:this.linkToItem.get(f)??this.resourceStartItem(f.href)}));if(s.length===0)return;const n=this._conformsTo.includes(p.AUDIOBOOK),o=n?t.position??0:t.scroll??0;let r,c=-1/0;for(const f of s){const a=n?f.item?.position??0:f.item?.scroll??0;a<=o&&a>c&&(c=a,r=f)}const h=r??s[0];return this.findTocEntryByLink(this.contextualizedToc,h.link)}previousResolvedTocEntry(i){const t=this._allItems.findIndex(e=>this.linkFor(e)===i);if(t!==-1)for(let e=t-1;e>=0;e--){const s=this.linkFor(this._allItems[e]);if(!s)continue;const n=l.bareHref(s.href),{atStart:o,fragments:r}=l.collectTocCandidates(this.tocLinks,n,this._tocDepth,1),c=o.filter(d=>l.bareHref(d.href)===n),h=r.filter(d=>l.bareHref(d.href)===n),f=c[0]??(h.length>0?h[h.length-1]:void 0);if(!f)continue;const a=this.findTocEntryByLink(this.contextualizedToc,f);if(a)return a}}static trimToDepth(i,t,e,s){return i.map(n=>{const o={...n,children:t>1&&n.children?.length?l.trimToDepth(n.children,t-1,e,s):void 0},r=e.get(n);return r&&s.set(o,r),o})}flattenItems(i){const t=[];for(const e of i)t.push(e),e.children&&t.push(...this.flattenItems(e.children));return t}itemStartTime(i,t){for(const e of i.references){const s=e.indexOf("#"),n=s>=0?e.slice(0,s):e,o=s>=0?e.slice(s+1):void 0;if((n||t)!==t)continue;if(!o)return;const c=o.match(/(?:^|&)t=([^&]+)/);return c?T(c[1]):void 0}}itemMatchesHref(i,t){return i.references.some(e=>{const s=e.split("#")[0];return s===t||s===""})}ancestorPath(i,t){for(const e of i){if(e===t)return[];if(e.children){const s=this.ancestorPath(e.children,t);if(s!==null)return[e,...s]}}return null}itemScrollPosition(i){return i.scroll}static bareHref(i){return i.split("#")[0]}}export{l as Timeline};
|
package/package.json
CHANGED
|
@@ -339,8 +339,11 @@ export class Timeline {
|
|
|
339
339
|
for (const link of tocLinks) {
|
|
340
340
|
const linkBare = Timeline.bareHref(link.href);
|
|
341
341
|
// Fragment-only hrefs (e.g. "#t=60" in single-track audio) have no bare href,
|
|
342
|
-
// so they are associated with the current resource.
|
|
343
|
-
|
|
342
|
+
// so they are associated with the current resource. A hrefless link (e.g. a
|
|
343
|
+
// <span> grouping heading) also bareHrefs to '', but names no resource at all
|
|
344
|
+
// and must not match every resource.
|
|
345
|
+
const isFragmentOnly = link.href.startsWith('#');
|
|
346
|
+
const matchesBare = linkBare === bare || (isFragmentOnly && linkBare === '');
|
|
344
347
|
if (matchesBare && link.title && !Timeline.isStartOfResource(link.href)) {
|
|
345
348
|
// Prepend the resource href when the link uses a fragment-only reference.
|
|
346
349
|
const ref = linkBare === '' ? bare + link.href : link.href;
|
|
@@ -382,7 +385,8 @@ export class Timeline {
|
|
|
382
385
|
|
|
383
386
|
for (const link of tocLinks) {
|
|
384
387
|
const linkBare = Timeline.bareHref(link.href);
|
|
385
|
-
|
|
388
|
+
const isFragmentOnly = link.href.startsWith('#');
|
|
389
|
+
if ((linkBare === bare || (isFragmentOnly && linkBare === '')) && link.title) {
|
|
386
390
|
if (Timeline.isStartOfResource(link.href)) {
|
|
387
391
|
atStart.push(link);
|
|
388
392
|
} else {
|