@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 f=c===""?t+r.href:r.href,a={title:r.title,references:[f]};n.set(a,r),o.push(a)}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);if((c===t||c==="")&&r.title&&(l.isStartOfResource(r.href)?n.push(r):o.push(r)),r.children?.items?.length){const h=l.collectTocCandidates(r.children.items,t,e,s+1);n.push(...h.atStart),o.push(...h.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};
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@readium/shared",
3
- "version": "2.3.1",
3
+ "version": "2.3.2",
4
4
  "type": "module",
5
5
  "description": "Shared models to be used across other Readium projects and implementations in Typescript",
6
6
  "author": "readium",
@@ -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
- const matchesBare = linkBare === bare || linkBare === '';
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
- if ((linkBare === bare || linkBare === '') && link.title) {
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 {