@readium/navigator 2.10.3 → 2.11.0
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.
- package/dist/audio/AudioNavigator.js +1 -1
- package/dist/audio/AudioPoolManager.js +1 -1
- package/dist/audio/engine/WebAudioEngine.js +1 -1
- package/dist/divina/DivinaNavigator.js +1 -0
- package/dist/divina/DivinaPageManager.js +1 -0
- package/dist/divina/DivinaPagedPresenter.js +1 -0
- package/dist/divina/DivinaPeripherals.js +1 -0
- package/dist/divina/DivinaScrolledPresenter.js +1 -0
- package/dist/divina/DivinaSpreader.js +1 -0
- package/dist/divina/DivinaVariantSelector.js +1 -0
- package/dist/divina/preferences/DivinaDefaults.js +1 -0
- package/dist/divina/preferences/DivinaPreferences.js +1 -0
- package/dist/divina/preferences/DivinaPreferencesEditor.js +1 -0
- package/dist/divina/preferences/DivinaSettings.js +1 -0
- package/dist/divina/protection/DivinaNavigatorProtector.js +1 -0
- package/dist/epub/EpubNavigator.js +1 -1
- package/dist/epub/frame/FrameBlobBuilder.js +3 -1
- package/dist/epub/frame/FrameManager.js +1 -1
- package/dist/epub/frame/FramePoolManager.js +1 -1
- package/dist/epub/fxl/FXLFrameManager.js +1 -1
- package/dist/epub/fxl/FXLFramePoolManager.js +1 -1
- package/dist/epub/fxl/FXLPeripherals.js +2 -2
- package/dist/index.js +1 -1
- package/dist/peripherals/KeyboardConditionBridge.js +1 -1
- package/dist/peripherals/KeyboardPeripherals.js +1 -1
- package/dist/webpub/WebPubFrameManager.js +1 -1
- package/dist/webpub/WebPubFramePoolManager.js +1 -1
- package/dist/webpub/WebPubNavigator.js +1 -1
- package/package.json +5 -5
- package/src/audio/AudioNavigator.ts +107 -5
- package/src/audio/AudioPoolManager.ts +14 -8
- package/src/audio/engine/AudioEngine.ts +34 -1
- package/src/audio/engine/WebAudioEngine.ts +48 -3
- package/src/divina/DivinaNavigator.ts +704 -0
- package/src/divina/DivinaPageManager.ts +322 -0
- package/src/divina/DivinaPagedPresenter.ts +508 -0
- package/src/divina/DivinaPeripherals.ts +630 -0
- package/src/divina/DivinaScrolledPresenter.ts +516 -0
- package/src/divina/DivinaSpreader.ts +202 -0
- package/src/divina/DivinaVariantSelector.ts +77 -0
- package/src/divina/index.ts +8 -0
- package/src/divina/preferences/DivinaDefaults.ts +37 -0
- package/src/divina/preferences/DivinaPreferences.ts +77 -0
- package/src/divina/preferences/DivinaPreferencesEditor.ts +96 -0
- package/src/divina/preferences/DivinaSettings.ts +38 -0
- package/src/divina/preferences/index.ts +4 -0
- package/src/divina/protection/DivinaNavigatorProtector.ts +44 -0
- package/src/epub/EpubNavigator.ts +73 -7
- package/src/epub/frame/FrameBlobBuilder.ts +144 -39
- package/src/epub/frame/FrameManager.ts +7 -9
- package/src/epub/frame/FramePoolManager.ts +106 -48
- package/src/epub/fxl/FXLFrameManager.ts +69 -26
- package/src/epub/fxl/FXLFramePoolManager.ts +98 -53
- package/src/epub/fxl/FXLPeripherals.ts +22 -1
- package/src/index.ts +1 -0
- package/src/peripherals/KeyboardConditionBridge.ts +15 -5
- package/src/peripherals/KeyboardPeripherals.ts +13 -7
- package/src/webpub/WebPubFrameManager.ts +7 -9
- package/src/webpub/WebPubFramePoolManager.ts +5 -2
- package/src/webpub/WebPubNavigator.ts +44 -0
- package/types/src/audio/AudioNavigator.d.ts +29 -0
- package/types/src/audio/engine/AudioEngine.d.ts +27 -1
- package/types/src/audio/engine/WebAudioEngine.d.ts +14 -2
- package/types/src/divina/DivinaNavigator.d.ts +119 -0
- package/types/src/divina/DivinaPageManager.d.ts +66 -0
- package/types/src/divina/DivinaPagedPresenter.d.ts +122 -0
- package/types/src/divina/DivinaPeripherals.d.ts +121 -0
- package/types/src/divina/DivinaScrolledPresenter.d.ts +125 -0
- package/types/src/divina/DivinaSpreader.d.ts +65 -0
- package/types/src/divina/DivinaVariantSelector.d.ts +30 -0
- package/types/src/divina/index.d.ts +8 -0
- package/types/src/divina/preferences/DivinaDefaults.d.ts +18 -0
- package/types/src/divina/preferences/DivinaPreferences.d.ts +33 -0
- package/types/src/divina/preferences/DivinaPreferencesEditor.d.ts +20 -0
- package/types/src/divina/preferences/DivinaSettings.d.ts +26 -0
- package/types/src/divina/preferences/index.d.ts +4 -0
- package/types/src/divina/protection/DivinaNavigatorProtector.d.ts +14 -0
- package/types/src/epub/EpubNavigator.d.ts +7 -1
- package/types/src/epub/frame/FrameBlobBuilder.d.ts +19 -3
- package/types/src/epub/frame/FramePoolManager.d.ts +5 -1
- package/types/src/epub/fxl/FXLFrameManager.d.ts +2 -0
- package/types/src/epub/fxl/FXLFramePoolManager.d.ts +8 -2
- package/types/src/epub/fxl/FXLPeripherals.d.ts +3 -0
- package/types/src/index.d.ts +1 -0
- package/types/src/peripherals/KeyboardPeripherals.d.ts +1 -0
- package/types/src/webpub/WebPubNavigator.d.ts +2 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Loader as
|
|
1
|
+
import{Loader as a}from"@readium/navigator-html-injectables";import{ReadingProgression as d,Page as h}from"@readium/shared";import{FrameComms as c}from"../frame/FrameComms.js";import{isTypedOMSupported as n}from"./FXLPeripherals.js";import{KeyboardConditionBridge as l}from"../../peripherals/KeyboardConditionBridge.js";class g{constructor(t,e,s,r={},i=[],o=[]){this.frameIsAppended=!1,this.pendingUnwatchSelectors=[],this.currModules=[],this.viewportSize=void 0,this.cachedPage=void 0,this.peripherals=t,this.debugHref=s,this.contentProtectionConfig={...r},this.keyboardPeripheralsConfig=[...i],this.resizeWatchSelectors=[...o],this.frame=document.createElement("iframe"),this.frame.sandbox.value="allow-same-origin allow-scripts",this.frame.classList.add("readium-navigator-iframe"),this.frame.classList.add("blank"),this.frame.scrolling="no",this.frame.style.visibility="hidden",this.frame.ariaHidden="true",this.frame.style.display="none",this.frame.style.position="absolute",this.frame.style.pointerEvents="none",this.frame.style.transformOrigin="0 0",this.frame.style.transform="scale(1)",this.frame.style.background="#fff",this.frame.style.touchAction="none",this.frame.dataset.originalHref=s,this.source="about:blank",this.wrapper=document.createElement("div"),this.wrapper.style.position="relative",this.wrapper.style.contain="strict",this.wrapper.style.float=this.wrapper.style.cssFloat=e===d.rtl?"right":"left"}async load(t,e){return this.frameIsAppended||(this.wrapper.appendChild(this.frame),this.frameIsAppended=!0),this.source===e&&this.loadPromise&&[...this.currModules].sort().join("|")===[...t].sort().join("|")?this.loadPromise:(this.loaded&&this.source!==e&&this.window.stop(),this.source=e,this.loadPromise=new Promise((s,r)=>{if(this.loader&&this.loaded){const i=this.frame.contentWindow;if([...this.currModules].sort().join("|")===[...t].sort().join("|")){try{s(i),this.loadPromise=void 0}catch{}return}this.comms?.halt(),this.loader.destroy(),this.loader=new a(i,t),this.currModules=t,this.comms=void 0;try{s(i),this.loadPromise=void 0}catch{}return}this.frame.addEventListener("load",()=>{const i=this.frame.contentWindow;this.loader=new a(i,t),this.currModules=t,this.peripherals.observe(this.wrapper),this.peripherals.observe(i);try{s(i)}catch{}},{once:!0}),this.frame.addEventListener("error",i=>{try{r(i.error),this.loadPromise=void 0}catch{}},{once:!0}),this.frame.style.removeProperty("display"),this.frame.contentWindow.location.replace(this.source)}),this.loadPromise)}loadPageSize(){if(this.viewportSize)return this.viewportSize;const t=this.frame.contentWindow,e=t.document.head.querySelector("meta[name=viewport]");if(e){const s=/(\w+) *= *([^\s,]+)/g;let r,i=0,o=0;for(;r=s.exec(e.content);)r[1]==="width"?i=Number.parseFloat(r[2]):r[1]==="height"&&(o=Number.parseFloat(r[2]));if(i>0&&o>0)return this.viewportSize={width:i,height:o},this.viewportSize}return{width:t.document.body.scrollWidth,height:t.document.body.scrollHeight}}update(t){if(!this.loaded)return;const e=this.loadPageSize(),s=Math.min(this.wrapper.clientWidth/e.width,this.wrapper.clientHeight/e.height);n()?(this.frame.attributeStyleMap.set("width",CSS.px(e.width)),this.frame.attributeStyleMap.set("height",CSS.px(e.height)),this.frame.attributeStyleMap.set("transform",new CSSTransformValue([new CSSScale(s,s)]))):(this.frame.style.height=`${e.height}px`,this.frame.style.width=`${e.width}px`,this.frame.style.transform=`scale(${s})`);const r=this.frame.getBoundingClientRect(),i=this.wrapper.clientHeight-r.height;if(n())if(this.frame.attributeStyleMap.set("top",CSS.px(i/2)),t===h.left){const o=this.wrapper.clientWidth-r.width;this.frame.attributeStyleMap.set("left",CSS.px(o))}else if(t===h.center){const o=this.wrapper.clientWidth-r.width;this.frame.attributeStyleMap.set("left",CSS.px(o/2))}else this.frame.attributeStyleMap.set("left",CSS.px(0));else if(this.frame.style.top=`${i/2}px`,t===h.left){const o=this.wrapper.clientWidth-r.width;this.frame.style.left=`${o}px`}else if(t===h.center){const o=this.wrapper.clientWidth-r.width;this.frame.style.left=`${o/2}px`}else this.frame.style.left="0px";this.frame.style.removeProperty("visibility"),this.frame.ariaHidden=null,this.frame.style.removeProperty("pointer-events"),this.frame.classList.remove("blank"),this.frame.classList.add("loaded")}async destroy(){this.conditionBridge?.destroy(),await this.unfocus(),this.loader?.destroy(),this.wrapper.remove()}async unload(){if(this.loaded)return this.deselect(),this.frame.style.visibility="hidden",this.frame.ariaHidden="true",this.frame.style.pointerEvents="none",this.frame.classList.add("blank"),this.frame.classList.remove("loaded"),this.comms?.halt(),this.loader?.destroy(),this.comms=void 0,this.frame.blur(),new Promise((t,e)=>{this.frame.addEventListener("load",()=>{try{this.showPromise=void 0,t()}catch{}},{once:!0}),this.frame.addEventListener("error",s=>{try{this.showPromise=void 0,e(s.error)}catch{}},{once:!0}),this.source="about:blank",this.frame.contentWindow.location.replace("about:blank"),this.frame.style.display="none",this.frame.style.width="0px",this.frame.style.height="0px"})}deselect(){this.frame.contentWindow?.getSelection()?.removeAllRanges()}async unfocus(){if(this.frame.parentElement)return this.comms===void 0?void 0:(this.frame.blur(),new Promise((t,e)=>{this.comms?.send("unfocus",void 0,s=>{this.comms?.halt(),this.showPromise=void 0,t()})}));this.comms?.halt()}applyContentProtection(){this.comms||this.comms.resume(),this.comms.send("peripherals_protection",this.contentProtectionConfig),this.keyboardPeripheralsConfig&&this.keyboardPeripheralsConfig.length>0&&(this.conditionBridge?.destroy(),this.conditionBridge=new l(this.keyboardPeripheralsConfig,t=>{t.length>0&&this.comms.send("keyboard_peripherals",t)}),this.conditionBridge.setup()),this.contentProtectionConfig.protectPrinting?.disable&&this.comms.send("print_protection",this.contentProtectionConfig.protectPrinting),this.resizeWatchSelectors.forEach(t=>{this.comms.send("decoration_resize",{action:"watch",selector:t})}),this.pendingUnwatchSelectors.forEach(t=>{this.comms.send("decoration_resize",{action:"unwatch",selector:t})}),this.pendingUnwatchSelectors=[]}addResizeTarget(t){this.resizeWatchSelectors.includes(t)||this.resizeWatchSelectors.push(t),this.pendingUnwatchSelectors=this.pendingUnwatchSelectors.filter(e=>e!==t),this.comms?.ready&&this.comms.send("decoration_resize",{action:"watch",selector:t})}removeResizeTarget(t){this.resizeWatchSelectors=this.resizeWatchSelectors.filter(e=>e!==t),this.comms?.ready?this.comms.send("decoration_resize",{action:"unwatch",selector:t}):this.pendingUnwatchSelectors.includes(t)||this.pendingUnwatchSelectors.push(t)}async show(t){if(!this.frame.parentElement){console.warn("Trying to show frame that is not attached to the DOM");return}if(!this.loaded){this.showPromise=void 0;return}return this.showPromise?(this.cachedPage!==t&&(this.update(t),this.cachedPage=t),this.showPromise):(this.cachedPage=t,this.comms?this.comms.resume():this.comms=new c(this.frame.contentWindow,this.source),this.showPromise=new Promise((e,s)=>{this.comms.send("focus",void 0,r=>{this.update(this.cachedPage),this.applyContentProtection(),e()})}),this.showPromise)}async activate(){return new Promise((t,e)=>{if(!this.comms)return t();this.comms?.send("activate",void 0,()=>{t()})})}get element(){return this.wrapper}get iframe(){return this.frame}get realSize(){return this.frame.getBoundingClientRect()}get loaded(){return this.frame.contentWindow&&this.frame.contentWindow.location.href!=="about:blank"}set width(t){if(n()){const e=this.wrapper.attributeStyleMap.get("width");if(e instanceof CSSUnitValue&&e.unit==="percent"&&e.value===t)return;this.wrapper.attributeStyleMap.set("width",CSS.percent(t))}else{const e=`${t}%`;if(this.wrapper.style.width===e)return;this.wrapper.style.width=e}}set height(t){if(n()){const e=this.wrapper.attributeStyleMap.get("height");if(e instanceof CSSUnitValue&&e.unit==="px"&&e.value===t)return;this.wrapper.attributeStyleMap.set("height",CSS.px(t))}else{const e=`${t}px`;if(this.wrapper.style.height===e)return;this.wrapper.style.height=e}}get window(){if(!this.frame.contentWindow)throw Error("Trying to use frame window when it doesn't exist");return this.frame.contentWindow}get atLeft(){return this.window.scrollX<5}get atRight(){return this.window.scrollX>this.window.document.scrollingElement.scrollWidth-this.window.innerWidth-5}get msg(){return this.comms}get ldr(){return this.loader}}export{g as FXLFrameManager};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ReadingProgression as w,Page as u}from"@readium/shared";import k from"../frame/FrameBlobBuilder.js";import{FXLFrameManager as M}from"./FXLFrameManager.js";import{FXLPeripherals as x}from"./FXLPeripherals.js";import{Spread as y,FXLSpreader as B,Orientation as T}from"./FXLSpreader.js";const O=8,v=5,U=300,A=15e3,D=250,I=150,z=500;class X{constructor(e,t,s,a,n,r,l=[]){if(this.pool=new Map,this.blobs=new Map,this.inprogress=new Map,this.delayedShow=new Map,this.delayedTimeout=new Map,this.previousFrames=[],this.injector=null,this.width=0,this.height=0,this.transform="",this.currentSlide=0,this.spread=!0,this.orientationInternal=-1,this.container=e,this.positions=t,this.pub=s,this.injector=a??null,this.contentProtectionConfig=n||{},this.keyboardPeripheralsConfig=r||[],this.resizeWatchSelectors=[...l],this.spreadPresentation=s.metadata.otherMetadata?.spread||y.auto,this.pub.metadata.effectiveReadingProgression!==w.rtl&&this.pub.metadata.effectiveReadingProgression!==w.ltr)throw Error("Unsupported reading progression for EPUB");this.spreader=new B(this.pub),this.containerHeightCached=e.clientHeight,this.bookElement=document.createElement("div"),this.bookElement.ariaLabel="Book",this.bookElement.tabIndex=-1,this.updateBookStyle(!0),this.spineElement=document.createElement("div"),this.spineElement.ariaLabel="Spine",this.bookElement.appendChild(this.spineElement),this.container.appendChild(this.bookElement),this.updateSpineStyle(!0),this.peripherals=new x(this),this.pub.readingOrder.items.forEach(h=>{const d=new M(this.peripherals,this.pub.metadata.effectiveReadingProgression,h.href,this.contentProtectionConfig,this.keyboardPeripheralsConfig,this.resizeWatchSelectors);this.spineElement.appendChild(d.element),this.pool.set(h.href,d),d.width=100/this.length*(h.properties?.otherProperties.orientation===T.landscape||h.properties?.otherProperties.addBlank?this.perPage:1),d.height=this.height})}set listener(e){this._listener=e}get listener(){return this._listener}get doNotDisturb(){return this.peripherals.pan.touchID>0}resizeHandler(e=!0,t=!0){this.currentSlide+this.perPage>this.length&&(this.currentSlide=this.length<=this.perPage?0:this.length-1),this.containerHeightCached=this.container.clientHeight,this.orientationInternal=-1,this.updateSpineStyle(!0),e&&(this.currentSlide=this.reAlign(),this.slideToCurrent(!t,t)),clearTimeout(this.resizeTimeout),this.resizeTimeout=window.setTimeout(()=>{this.pool.forEach((s,a)=>{let n=this.pub.readingOrder.items.findIndex(h=>h.href===a);const r=this.pub.readingOrder.items[n];if(s.width=100/this.length*(r.properties?.otherProperties.orientation===T.landscape||r.properties?.otherProperties.addBlank?this.perPage:1),s.height=this.height,!s.loaded)return;const l=this.spreader.findByLink(r);s.update(this.spreadPosition(l,r))})},D)}updateDimensions(){this.width=this.bookElement.clientWidth,this.height=this.bookElement.clientHeight}get rtl(){return this.pub.metadata.effectiveReadingProgression===w.rtl}get single(){return!this.spread||this.portrait}get perPage(){return this.spread&&!this.portrait?2:1}get threshold(){return 50}get portrait(){return this.spreadPresentation===y.none?!0:(this.orientationInternal===-1&&(this.orientationInternal=this.containerHeightCached>this.container.clientWidth?1:0),this.orientationInternal===1)}updateSpineStyle(e,t=!0){let s="0";this.updateDimensions(),this.perPage>1&&(s=`${this.width/2}px`);const a={transition:e?`all ${t?I:z}ms ease-out`:"all 0ms ease-out",marginRight:this.rtl?s:"0",marginLeft:this.rtl?"0":s,width:`${this.width/this.perPage*this.length}px`,transform:this.transform,contain:"content"};Object.assign(this.spineElement.style,a)}updateBookStyle(e=!1){if(e){const t={overflow:"hidden",direction:this.pub.metadata.effectiveReadingProgression,cursor:"",height:"100%",width:"100%",position:"relative",outline:"none",transition:this.peripherals?.dragState?"none":"transform .15s ease-in-out",touchAction:"none"};Object.assign(this.bookElement.style,t)}this.bookElement.style.transform=`scale(${this.peripherals?.scale||1})`+(this.peripherals?` translate3d(${this.peripherals.pan.translateX}px, ${this.peripherals.pan.translateY}px, 0px)`:"")}goTo(e){if(this.slength<=this.perPage)return;e=this.reAlign(e);const t=this.currentSlide;this.currentSlide=Math.min(Math.max(e,0),this.length-1),t!==this.currentSlide&&this.slideToCurrent(!1)}onChange(){this.peripherals.scale=1,this.updateBookStyle()}get offset(){return(this.rtl?1:-1)*this.currentSlide*(this.width/this.perPage)}get length(){if(this.single)return this.slength;const e=this.slength+this.nLandscape;return this.shift&&e%2===0?e+1:e}get slength(){return this.pub.readingOrder.items.length||0}get shift(){return this.spreader.shift}get nLandscape(){return this.spreader.nLandscape}setPerPage(e){e===null?this.spread=!0:e===1?this.spread=!1:this.spread=!0,requestAnimationFrame(()=>this.resizeHandler(!0))}slideToCurrent(e,t=!0){if(this.updateDimensions(),e)requestAnimationFrame(()=>{requestAnimationFrame(()=>{const s=`translate3d(${this.offset}px, 0, 0)`;this.spineElement.style.transform!==s&&(this.transform=s,this.updateSpineStyle(!0,t),this.deselect())})});else{const s=`translate3d(${this.offset}px, 0, 0)`;if(this.spineElement.style.transform===s)return;this.transform=s,this.updateSpineStyle(!1),this.deselect()}}bounce(e=!1){requestAnimationFrame(()=>{this.transform=`translate3d(${this.offset+50*(e?1:-1)}px, 0, 0)`,this.updateSpineStyle(!0,!0),setTimeout(()=>{this.transform=`translate3d(${this.offset}px, 0, 0)`,this.updateSpineStyle(!0,!0)},100)})}next(e=1){if(this.slength<=this.perPage)return!1;const t=this.currentSlide;return this.currentSlide=Math.min(this.currentSlide+e,this.length-1),this.perPage>1&&this.currentSlide%2&&this.currentSlide--,this.currentSlide===t&&(this.currentSlide+1,this.length),t!==this.currentSlide?(this.slideToCurrent(!0),this.onChange(),!0):(this.bounce(this.rtl),!1)}prev(e=1){if(this.slength<=this.perPage)return!1;const t=this.currentSlide;return this.currentSlide=Math.max(this.currentSlide-e,0),this.perPage>1&&this.currentSlide%2&&this.currentSlide++,t!==this.currentSlide?(this.slideToCurrent(!0),this.onChange(),!0):(this.bounce(!this.rtl),!1)}get ownerWindow(){return this.container.ownerDocument.defaultView||window}async destroy(){let e=this.inprogress.values(),t=e.next();const s=[];for(;t.value;)s.push(t.value),t=e.next();s.length>0&&await Promise.allSettled(s),this.inprogress.clear();let a=this.pool.values(),n=a.next();for(;n.value;)await n.value.destroy(),n=a.next();this.pool.clear(),this.blobs.forEach(r=>URL.revokeObjectURL(r)),this.injector?.dispose(),this.container.childNodes.forEach(r=>{(r.nodeType===Node.ELEMENT_NODE||r.nodeType===Node.TEXT_NODE)&&r.remove()})}makeSpread(e){return this.perPage<2?[this.pub.readingOrder.items[e]]:this.spreader.currentSpread(e,this.perPage)}reAlign(e=this.currentSlide){return e%2&&!this.single&&e++,e}spreadPosition(e,t){return this.perPage<2?u.center:e.length<2?u.center:t.href===e[0].href?this.rtl?u.right:u.left:this.rtl?u.left:u.right}async waitForItem(e){if(this.inprogress.has(e)&&await this.inprogress.get(e),this.delayedShow.has(e)){const t=this.delayedTimeout.get(e);t>0?clearTimeout(t):await this.delayedShow.get(e),this.delayedTimeout.set(e,0),this.delayedShow.delete(e)}}async cancelShowing(e){if(this.delayedShow.has(e)){const t=this.delayedTimeout.get(e);t>0&&clearTimeout(t),this.delayedShow.delete(e)}}async update(e,t,s,a=!1){let n=this.pub.readingOrder.items.findIndex(h=>h.href===t.href);if(n<0)throw Error("Href not found in reading order");this.currentSlide!==n&&(this.currentSlide=this.reAlign(n),this.slideToCurrent(!0));const r=this.makeSpread(this.currentSlide);this.perPage>1&&n++;for(const h of r)await this.waitForItem(h.href);const l=new Promise(async(h,d)=>{const f=[],g=[];this.positions.forEach((i,o)=>{(o>n+O||o<n-O)&&(f.includes(i.href)||f.push(i.href)),o<n+v&&o>n-v&&(g.includes(i.href)||g.push(i.href))}),f.forEach(async i=>{g.includes(i)||this.pool.has(i)&&(this.cancelShowing(i),await this.pool.get(i)?.unload())}),this.currentBaseURL!==void 0&&e.baseURL!==this.currentBaseURL&&(this.blobs.forEach(i=>URL.revokeObjectURL(i)),this.blobs.clear()),this.currentBaseURL=e.baseURL;const L=async i=>{const o=e.readingOrder.findIndexWithHref(i),p=e.readingOrder.items[o];if(p){if(!this.blobs.has(i)){const S=await new k(e,this.currentBaseURL||"",p,{injector:this.injector}).build(!0);this.blobs.set(i,S)}this.delayedShow.has(i)||this.delayedShow.set(i,new Promise((b,S)=>{let P=!1;const R=window.setTimeout(async()=>{this.delayedTimeout.set(i,0);const F=this.makeSpread(this.reAlign(o)),C=this.spreadPosition(F,p),E=this.pool.get(i);await E.load(s,this.blobs.get(i)),this.peripherals.isScaled||await E.show(C),this.delayedShow.delete(i),P=!0,b()},U);setTimeout(()=>{!P&&this.delayedShow.has(i)&&S(`Offscreen load timeout: ${i}`)},A),this.delayedTimeout.set(i,R)}))}};try{await Promise.all(g.map(i=>L(i)))}catch(i){d(i)}const c=[];for(const i of r){const o=this.pool.get(i.href),p=this.blobs.get(i.href);p&&(this.cancelShowing(i.href),await o.load(s,p),await o.show(this.spreadPosition(r,i)),this.previousFrames.push(o),await o.activate(),c.push(o))}for(;this.previousFrames.length>0;){const i=this.previousFrames.shift();i&&!c.includes(i)&&await i.unfocus()}this.previousFrames=c;const m=this.container.ownerDocument.activeElement;m&&m.tagName==="IFRAME"&&!c.some(i=>i.iframe===m)&&c[0]?.iframe.focus({preventScroll:!0}),h()});for(const h of r)this.inprogress.set(h.href,l);await l;for(const h of r)this.inprogress.delete(h.href)}addResizeTarget(e){this.resizeWatchSelectors.includes(e)||this.resizeWatchSelectors.push(e),this.pool.forEach(t=>t.addResizeTarget(e))}removeResizeTarget(e){this.resizeWatchSelectors=this.resizeWatchSelectors.filter(t=>t!==e),this.pool.forEach(t=>t.removeResizeTarget(e))}get currentFrames(){if(this.perPage<2){const t=this.pub.readingOrder.items[this.currentSlide];return[this.pool.get(t.href)]}return this.spreader.currentSpread(this.currentSlide,this.perPage).map(t=>this.pool.get(t.href))}get currentBounds(){const e={x:0,y:0,width:0,height:0,top:0,right:0,bottom:0,left:0,toJSON(){return this}};return this.currentFrames.forEach(t=>{if(!t)return;const s=t.realSize;e.x=Math.min(e.x,s.x),e.y=Math.min(e.y,s.y),e.width+=s.width,e.height=Math.max(e.height,s.height),e.top=Math.min(e.top,s.top),e.right=Math.min(e.right,s.right),e.bottom=Math.min(e.bottom,s.bottom),e.left=Math.min(e.left,s.left)}),e}get viewport(){const e={readingOrder:[],progressions:new Map,positions:null};return(this.perPage<2?[this.pub.readingOrder.items[this.currentSlide]]:this.spreader.currentSpread(this.currentSlide,this.perPage)).forEach(s=>{e.readingOrder.push(s.href),e.progressions.set(s.href,{start:0,end:1})}),e.positions=this.getCurrentNumbers(),e}getCurrentNumbers(){if(this.perPage<2)return[this.pub.readingOrder.items[this.currentSlide].properties?.otherProperties.number];const e=this.spreader.currentSpread(this.currentSlide,this.perPage);return e.length>1?[e[0].properties?.otherProperties.number,e[e.length-1].properties?.otherProperties.number]:[e[0].properties?.otherProperties.number]}deselect(){this.currentFrames?.forEach(e=>e?.deselect())}}export{X as FXLFramePoolManager};
|
|
1
|
+
import{ReadingProgression as b,Page as g}from"@readium/shared";import A from"../frame/FrameBlobBuilder.js";import{FXLFrameManager as I}from"./FXLFrameManager.js";import{FXLPeripherals as D,isTypedOMSupported as P}from"./FXLPeripherals.js";import{Spread as E,FXLSpreader as B,Orientation as T}from"./FXLSpreader.js";const C=8,k=5,U=300,_=15e3,z=250,M=150,v=500;class X{constructor(e,t,s,h,r,n,l,a=[],f){if(this.pool=new Map,this.blobs=new Map,this.inprogress=new Map,this.delayedShow=new Map,this.delayedTimeout=new Map,this.previousFrames=[],this.injector=null,this.width=0,this.height=0,this.currentSlide=0,this.spread=!0,this.orientationInternal=-1,this.bookScale=null,this.bookTranslate=null,this.bookTransform=null,this.container=e,this.positions=t,this.pub=s,this.injector=r??null,this.contentProtectionConfig=n||{},this.keyboardPeripheralsConfig=l||[],this.resizeWatchSelectors=[...a],this.upgradeInsecureRequests=f??"always",this.spreadPresentation=s.metadata.otherMetadata?.spread||E.auto,this.pub.metadata.effectiveReadingProgression!==b.rtl&&this.pub.metadata.effectiveReadingProgression!==b.ltr)throw Error("Unsupported reading progression for fixed-layout EPUB");this.spreader=new B(this.pub),this.containerHeightCached=e.clientHeight,this.bookElement=document.createElement("div"),this.bookElement.ariaLabel="Book",this.bookElement.tabIndex=-1,this.updateBookStyle(!0),this.spineElement=document.createElement("div"),this.spineElement.ariaLabel="Spine",this.bookElement.appendChild(this.spineElement),this.container.appendChild(this.bookElement),this.updateSpineStyle(!0),this.peripherals=new D(this);const d=document.createDocumentFragment();this.pub.readingOrder.items.forEach(p=>{const u=new I(this.peripherals,this.pub.metadata.effectiveReadingProgression,p.href,this.contentProtectionConfig,this.keyboardPeripheralsConfig,this.resizeWatchSelectors);this.pool.set(p.href,u),u.width=100/this.length*(p.properties?.otherProperties.orientation===T.landscape||p.properties?.otherProperties.addBlank?this.perPage:1),u.height=this.height,d.appendChild(u.wrapper)}),this.spineElement.appendChild(d)}set listener(e){this._listener=e}get listener(){return this._listener}get doNotDisturb(){return this.peripherals.pan.touchID>0}resizeHandler(e=!0,t=!0){this.currentSlide+this.perPage>this.length&&(this.currentSlide=this.length<=this.perPage?0:this.length-1),this.containerHeightCached=this.container.clientHeight,this.orientationInternal=-1,this.updateSpineStyle(!0),e&&(this.currentSlide=this.reAlign(),this.slideToCurrent(!t,t)),clearTimeout(this.resizeTimeout),this.resizeTimeout=window.setTimeout(()=>{const s=100/this.length,h=new Map(this.pub.readingOrder.items.map(r=>[r.href,r]));this.pool.forEach((r,n)=>{const l=h.get(n);l&&requestAnimationFrame(()=>{if(r.width=s*(l.properties?.otherProperties.orientation===T.landscape||l.properties?.otherProperties.addBlank?this.perPage:1),r.height=this.height,!r.loaded)return;const a=this.spreader.findByLink(l);r.update(this.spreadPosition(a,l))})})},z)}updateDimensions(){this.width=this.bookElement.clientWidth,this.height=this.bookElement.clientHeight}get rtl(){return this.pub.metadata.effectiveReadingProgression===b.rtl}get single(){return!this.spread||this.portrait}get perPage(){return this.spread&&!this.portrait?2:1}get threshold(){return 50}get portrait(){return this.spreadPresentation===E.none?!0:(this.orientationInternal===-1&&(this.orientationInternal=this.containerHeightCached>this.container.clientWidth?1:0),this.orientationInternal===1)}updateSpineStyle(e,t=!0){let s=0;if(this.updateDimensions(),this.perPage>1&&(s=this.width/2),P())this.spineElement.attributeStyleMap.set("transition",new CSSUnparsedValue([e?`all ${t?M:v}ms ease-out`:"all 0ms ease-out"])),this.spineElement.attributeStyleMap.set("margin-right",CSS.px(this.rtl?s:0)),this.spineElement.attributeStyleMap.set("margin-left",CSS.px(this.rtl?0:s)),this.spineElement.attributeStyleMap.set("width",CSS.px(this.width/this.perPage*this.length)),this.transform?this.spineElement.attributeStyleMap.set("transform",new CSSTransformValue([new CSSTranslate(CSS.px(this.transform||0),CSS.px(0),CSS.px(0))])):this.spineElement.attributeStyleMap.delete("transform"),this.spineElement.attributeStyleMap.set("contain",new CSSUnparsedValue(["content"]));else{const h={transition:e?`all ${t?M:v}ms ease-out`:"all 0ms ease-out",marginRight:this.rtl?`${s}px`:"0",marginLeft:this.rtl?"0":`${s}px`,width:`${this.width/this.perPage*this.length}px`,transform:this.transform?`translate3d(${this.transform}px, 0px, 0px)`:"",contain:"content"};Object.assign(this.spineElement.style,h)}}updateBookStyle(e=!1){if(e){const r={overflow:"hidden",direction:this.pub.metadata.effectiveReadingProgression,cursor:"",height:"100%",width:"100%",position:"relative",outline:"none",transition:this.peripherals?.dragState?"none":"transform .15s ease-in-out",touchAction:"none"};Object.assign(this.bookElement.style,r)}const t=this.peripherals?.scale||1,s=this.peripherals?.pan.translateX||0,h=this.peripherals?.pan.translateY||0;P()?(this.bookTransform||(this.bookScale=new CSSScale(1,1),this.bookTranslate=new CSSTranslate(CSS.px(0),CSS.px(0),CSS.px(0)),this.bookTransform=new CSSTransformValue([this.bookScale,this.bookTranslate])),this.bookScale.x.value=t,this.bookScale.y.value=t,this.bookTranslate.x.value=s,this.bookTranslate.y.value=h,this.bookElement.attributeStyleMap.set("transform",this.bookTransform)):this.bookElement.style.transform=`scale(${t}) translate3d(${s}px, ${h}px, 0px)`}goTo(e){if(this.slength<=this.perPage)return;e=this.reAlign(e);const t=this.currentSlide;this.currentSlide=Math.min(Math.max(e,0),this.length-1),t!==this.currentSlide&&this.slideToCurrent(!1)}onChange(){this.peripherals.scale=1,this.updateBookStyle()}get offset(){return(this.rtl?1:-1)*this.currentSlide*(this.width/this.perPage)}get length(){if(this.single)return this.slength;const e=this.slength+this.nLandscape;return this.shift&&e%2===0?e+1:e}get slength(){return this.pub.readingOrder.items.length||0}get shift(){return this.spreader.shift}get nLandscape(){return this.spreader.nLandscape}setPerPage(e){e===null?this.spread=!0:e===1?this.spread=!1:this.spread=!0,requestAnimationFrame(()=>this.resizeHandler(!0))}slideToCurrent(e,t=!0){this.updateDimensions(),e?requestAnimationFrame(()=>{requestAnimationFrame(()=>{this.transform=this.offset,this.updateSpineStyle(!0,t),this.deselect()})}):(this.transform=this.offset,this.updateSpineStyle(!1),this.deselect())}bounce(e=!1){requestAnimationFrame(()=>{this.transform=this.offset+50*(e?1:-1),this.updateSpineStyle(!0,!0),setTimeout(()=>{this.transform=this.offset,this.updateSpineStyle(!0,!0)},100)})}next(e=1){if(this.slength<=this.perPage)return!1;const t=this.currentSlide;return this.currentSlide=Math.min(this.currentSlide+e,this.length-1),this.perPage>1&&this.currentSlide%2&&this.currentSlide--,this.currentSlide===t&&(this.currentSlide+1,this.length),t!==this.currentSlide?(this.slideToCurrent(!0),this.onChange(),!0):(this.bounce(this.rtl),!1)}prev(e=1){if(this.slength<=this.perPage)return!1;const t=this.currentSlide;return this.currentSlide=Math.max(this.currentSlide-e,0),this.perPage>1&&this.currentSlide%2&&this.currentSlide++,t!==this.currentSlide?(this.slideToCurrent(!0),this.onChange(),!0):(this.bounce(!this.rtl),!1)}get ownerWindow(){return this.container.ownerDocument.defaultView||window}async destroy(){let e=this.inprogress.values(),t=e.next();const s=[];for(;t.value;)s.push(t.value),t=e.next();s.length>0&&await Promise.allSettled(s),this.inprogress.clear();let h=this.pool.values(),r=h.next();for(;r.value;)await r.value.destroy(),r=h.next();this.pool.clear(),this.blobs.forEach(n=>n.reset()),this.blobs.clear(),this.injector?.dispose(),this.container.childNodes.forEach(n=>{(n.nodeType===Node.ELEMENT_NODE||n.nodeType===Node.TEXT_NODE)&&n.remove()})}makeSpread(e){return this.perPage<2?[this.pub.readingOrder.items[e]]:this.spreader.currentSpread(e,this.perPage)}reAlign(e=this.currentSlide){return e%2&&!this.single&&e++,e}spreadPosition(e,t){return this.perPage<2?g.center:e.length<2?g.center:t.href===e[0].href?this.rtl?g.right:g.left:this.rtl?g.left:g.right}async waitForItem(e){if(this.inprogress.has(e)&&await this.inprogress.get(e),this.delayedShow.has(e)){const t=this.delayedTimeout.get(e);t>0?clearTimeout(t):await this.delayedShow.get(e),this.delayedTimeout.set(e,0),this.delayedShow.delete(e)}}async cancelShowing(e){if(this.delayedShow.has(e)){const t=this.delayedTimeout.get(e);t>0&&clearTimeout(t),this.delayedShow.delete(e)}}async update(e,t,s,h=!1){let r=this.pub.readingOrder.items.findIndex(a=>a.href===t.href);if(r<0)throw Error("Href not found in reading order");this.currentSlide!==r&&(this.currentSlide=this.reAlign(r),this.slideToCurrent(!0));const n=this.makeSpread(this.currentSlide);this.perPage>1&&r++;for(const a of n)await this.waitForItem(a.href);const l=new Promise(async(a,f)=>{const d=[],p=[];this.positions.forEach((i,o)=>{(o>r+C||o<r-C)&&(d.includes(i.href)||d.push(i.href)),o<r+k&&o>r-k&&(p.includes(i.href)||p.push(i.href))}),d.forEach(async i=>{p.includes(i)||this.pool.has(i)&&(this.cancelShowing(i),await this.pool.get(i)?.unload(),this.blobs.get(i)?.reset())}),this.currentBaseURL!==void 0&&e.baseURL!==this.currentBaseURL&&(this.blobs.forEach(i=>i.reset()),this.blobs.clear()),this.currentBaseURL=e.baseURL;const u=async i=>{const o=e.readingOrder.findIndexWithHref(i),c=e.readingOrder.items[o];c&&(this.blobs.has(i)||this.blobs.set(i,new A(e,this.currentBaseURL||"",c,{injector:this.injector,upgradeInsecureRequests:this.upgradeInsecureRequests})),this.delayedShow.has(i)||this.delayedShow.set(i,new Promise((x,F)=>{let w=!1;const O=window.setTimeout(async()=>{this.delayedTimeout.set(i,0);const R=this.makeSpread(this.reAlign(o)),L=this.spreadPosition(R,c),y=this.pool.get(i);await y.load(s,await this.blobs.get(i).build(!0)),this.peripherals.isScaled||await y.show(L),this.delayedShow.delete(i),w=!0,x()},U);setTimeout(()=>{!w&&this.delayedShow.has(i)&&F(`Offscreen load timeout: ${i}`)},_),this.delayedTimeout.set(i,O)})))};try{await Promise.all(p.map(i=>u(i)))}catch(i){f(i)}const m=[];for(const i of n){const o=this.pool.get(i.href),c=this.blobs.get(i.href);c&&(this.cancelShowing(i.href),await o.load(s,await c.build(!0)),await o.show(this.spreadPosition(n,i)),this.previousFrames.push(o),await o.activate(),m.push(o))}for(;this.previousFrames.length>0;){const i=this.previousFrames.shift();i&&!m.includes(i)&&await i.unfocus()}this.previousFrames=m;const S=this.container.ownerDocument.activeElement;S&&S.tagName==="IFRAME"&&!m.some(i=>i.iframe===S)&&m[0]?.iframe.focus({preventScroll:!0}),a()});for(const a of n)this.inprogress.set(a.href,l);try{await l}finally{for(const a of n)this.inprogress.delete(a.href)}}addResizeTarget(e){this.resizeWatchSelectors.includes(e)||this.resizeWatchSelectors.push(e),this.pool.forEach(t=>t.addResizeTarget(e))}removeResizeTarget(e){this.resizeWatchSelectors=this.resizeWatchSelectors.filter(t=>t!==e),this.pool.forEach(t=>t.removeResizeTarget(e))}get currentFrames(){if(this.perPage<2){const t=this.pub.readingOrder.items[this.currentSlide];return[this.pool.get(t.href)]}return this.spreader.currentSpread(this.currentSlide,this.perPage).map(t=>this.pool.get(t.href))}get currentBounds(){const e={x:0,y:0,width:0,height:0,top:0,right:0,bottom:0,left:0,toJSON(){return this}};return this.currentFrames.forEach(t=>{if(!t)return;const s=t.realSize;e.x=Math.min(e.x,s.x),e.y=Math.min(e.y,s.y),e.width+=s.width,e.height=Math.max(e.height,s.height),e.top=Math.min(e.top,s.top),e.right=Math.min(e.right,s.right),e.bottom=Math.min(e.bottom,s.bottom),e.left=Math.min(e.left,s.left)}),e}get viewport(){const e={readingOrder:[],progressions:new Map,positions:null};return(this.perPage<2?[this.pub.readingOrder.items[this.currentSlide]]:this.spreader.currentSpread(this.currentSlide,this.perPage)).forEach(s=>{e.readingOrder.push(s.href),e.progressions.set(s.href,{start:0,end:1})}),e.positions=this.getCurrentNumbers(),e}getCurrentNumbers(){if(this.perPage<2)return[this.pub.readingOrder.items[this.currentSlide].properties?.otherProperties.number];const e=this.spreader.currentSpread(this.currentSlide,this.perPage);return e.length>1?[e[0].properties?.otherProperties.number,e[e.length-1].properties?.otherProperties.number]:[e[0].properties?.otherProperties.number]}deselect(){this.currentFrames?.forEach(e=>e?.deselect())}}export{X as FXLFramePoolManager};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{FXLCoordinator as
|
|
1
|
+
import{FXLCoordinator as f}from"./FXLCoordinator.js";import{FXLPeripheralsDebug as X}from"./FXLPeripheralsDebug.js";const p=6,d=1.02,u=50,b=()=>typeof window.CSSTransformValue<"u";class T{constructor(t,e=!1){this.dragState=0,this.minimumMoved=!1,this.pan={startX:0,endX:0,startY:0,overscrollX:0,overscrollY:0,letItGo:!1,preventClick:!1,translateX:0,translateY:0,touchID:0},this.pinch={startDistance:0,startScale:0,target:{X:0,Y:0},touchN:0,startTranslate:{X:0,Y:0}},this._scale=1,this.scaleDebouncer=0,this.frameBounds=null,this.debugger=null,this.btouchstartHandler=this.touchstartHandler.bind(this),this.btouchendHandler=this.touchendHandler.bind(this),this.btouchmoveHandler=this.touchmoveHandler.bind(this),this.bdblclickHandler=this.dblclickHandler.bind(this),this.bmousedownHandler=this.mousedownHandler.bind(this),this.bmouseupHandler=this.mouseupHandler.bind(this),this.bmousemoveHandler=this.mousemoveHandler.bind(this),this.moveFrame=0,this.dragTranslate=null,this.dragTransform=null,this.manager=t,this.coordinator=new f,this.attachEvents(),e&&(this.debugger=new X)}get scale(){return this._scale}set scale(t){isNaN(t)&&(t=1),window.clearTimeout(this.scaleDebouncer),this.scaleDebouncer=window.setTimeout(()=>{this.dragState===0&&this.scale<d&&(this.pan.translateX=0,this.pan.translateY=0,this.clearPan(),this.manager.updateBookStyle()),this.manager.listener("zoom",t)},100),this._scale=t}attachEvents(){this.observe(this.manager.spineElement),this.pan={startX:0,startY:0,endX:0,overscrollX:0,overscrollY:0,letItGo:!1,preventClick:!1,translateX:0,translateY:0,touchID:0},this.pinch={startDistance:0,startScale:0,target:{X:0,Y:0},startTranslate:{X:0,Y:0},touchN:0}}clearPan(){this.pan.letItGo=!1,this.pan.touchID=0,this.pan.endX=0,this.pan.overscrollX=0,this.pan.overscrollY=0}clearPinch(){this.pinch={startDistance:0,startScale:this.pinch.startScale,target:{X:0,Y:0},touchN:0,startTranslate:{X:0,Y:0}}}observe(t){t.addEventListener("touchstart",this.btouchstartHandler),t.addEventListener("touchend",this.btouchendHandler),t.addEventListener("touchmove",this.btouchmoveHandler,{passive:!0}),t.addEventListener("dblclick",this.bdblclickHandler,{passive:!0}),t.addEventListener("mousedown",this.bmousedownHandler),t.addEventListener("mouseup",this.bmouseupHandler),t.addEventListener("mousemove",this.bmousemoveHandler)}clickHandler(t){}touchstartHandler(t){if(["TEXTAREA","OPTION","INPUT","SELECT"].indexOf(t.target.nodeName)!==-1)return;switch(t.stopPropagation(),this.frameBounds=this.manager.currentBounds,this.coordinator.refreshOuterPixels(this.frameBounds),t.touches.length){case 3:return;case 2:{t.preventDefault(),this.pinch.startDistance=this.coordinator.getTouchDistance(t);const r=this.startTouch(t);this.pan.startX=r.X,this.pan.startY=r.Y,this.dragState=2,this.manager.updateBookStyle(!0),this.isScaled?(this.pinch.target.X-=this.pan.translateX*(this.pinch.startScale/this.scale),this.pinch.target.Y-=this.pan.translateY*(this.pinch.startScale/this.scale),this.pinch.target={X:0,Y:0},this.pinch.startScale=1/this.scale):(this.pinch.target={X:0,Y:0},this.pinch.startScale=this.scale),this.pinch.startTranslate={X:this.pan.translateX,Y:this.pan.translateY},this.debugger?.show&&(this.debugger.DOM.touch2.style.display="",this.debugger.DOM.center.style.display="",this.debugger.DOM.pinchTarget.style.display="");return}case 1:this.pan.touchID=t.touches[0].identifier,this.debugger?.show&&(this.debugger.DOM.touch1.style.display="");default:this.dragState<1&&(this.dragState=1),this.manager.updateBookStyle(!0)}this.manager.updateSpineStyle(!1);const h=this.startTouch(t);this.pan.startX=h.X,this.pan.startY=h.Y}startTouch(t){const e=this.coordinator.getTouchCenter(t)||this.coordinator.getBibiEventCoord(t);return{X:e.X-this.manager.width/2-this.pan.translateX*this.scale+this.manager.width/2,Y:e.Y-this.manager.height/2-this.pan.translateY*this.scale+this.manager.height/2}}touchendHandler(t){if(t.stopPropagation(),!t.touches||t.touches.length===0)this.pan.endX&&!this.isScaled?(this.pinch.touchN&&(this.pan.endX=this.pan.startX),this.updateAfterDrag()):!this.pinch.touchN&&Math.abs(this.pan.overscrollX)>u&&Math.abs(this.pan.overscrollY)<u/2&&(this.pan.startX=0,this.pan.endX=-this.pan.overscrollX,this.updateAfterDrag()),this.dragState=0,this.minimumMoved=!1,this.clearPinch(),this.debugger?.show&&(this.debugger.DOM.center.style.display="none",this.debugger.DOM.touch1.style.display="none",this.debugger.DOM.touch2.style.display="none");else if(t.touches.length===1){this.dragState=1,t.touches[0].identifier!==this.pan.touchID&&(this.pan.touchID=t.touches[0].identifier),this.debugger?.show&&(this.debugger.DOM.center.style.display="none",this.debugger.DOM.touch2.style.display="none",this.debugger.DOM.pinchTarget.style.display="none");const e=this.startTouch(t);this.pan.startX=e.X,this.pan.startY=e.Y}window.setTimeout(()=>{this.manager.updateBookStyle(!0),this.dragState===0&&(this.scale<d&&(this.pan.translateX=0,this.pan.translateY=0),this.clearPan()),this.manager.updateBookStyle(!0)},50)}touchmoveHandler(t){t.stopPropagation();const e=this.coordinator.getBibiEventCoord(t);Math.abs(this.pan.startY-e.Y)+Math.abs(this.pan.startX-e.X)>5&&(this.minimumMoved||(this.manager.deselect(),this.minimumMoved=!0),this.dragState<1&&(this.dragState=1));const h=this.coordinator?.getTouchDistance(t);let r=!1;const g=this.scale;if(this.dragState===2&&h){if(this.pinch.touchN++,this.pinch.touchN<4)return;let a=h/this.pinch.startDistance*this.scale;a>=p&&(a=p),a<=d&&(a=1),this.scale=a,this.pinch.startDistance=h,r=!0}if(this.pan.letItGo===!1&&(this.pan.letItGo=Math.abs(this.pan.startY-e.Y)<Math.abs(this.pan.startX-e.X)),this.debugger?.show&&(this.debugger.DOM.touch1.style.top=`${e.Y-10}px`,this.debugger.DOM.touch1.style.left=`${e.X-10}px`,this.debugger.DOM.touch1.innerText=`${e.X.toFixed(2)},${e.Y.toFixed(2)}`),this.dragState>0&&this.isScaled||this.dragState>1){if(this.dragState===1){const s={X:e.X-this.manager.width/2,Y:e.Y-this.manager.height/2};this.pan.translateX=(s.X-(this.pan.startX-this.manager.width/2))*1/this.scale,this.pan.translateY=(s.Y-(this.pan.startY-this.manager.height/2))*1/this.scale}else if(this.dragState===2){const s=this.coordinator.getTouchCenter(t);if(this.debugger?.show){this.debugger.DOM.center.style.top=`${s.Y-5}px`,this.debugger.DOM.center.style.left=`${s.X-5}px`,this.debugger.DOM.center.innerText=`${s.X.toFixed(2)},${s.Y.toFixed(2)}`;const c=this.coordinator.getBibiEventCoord(t,1);this.debugger.DOM.touch2.style.top=`${c.Y-10}px`,this.debugger.DOM.touch2.style.left=`${c.X-10}px`,this.debugger.DOM.touch2.innerText=`${c.X.toFixed(2)},${c.Y.toFixed(2)}`}s.X-=this.manager.width/2,s.Y-=this.manager.height/2;let o=-s.X/g;o+=s.X/this.scale,this.pinch.target.X+=o,s.X+=this.pinch.target.X*this.scale/this.pinch.startScale;let n=-s.Y/g;n+=s.Y/this.scale,this.pinch.target.Y+=n,s.Y+=this.pinch.target.Y*this.scale/this.pinch.startScale;let l=(s.X-(this.pan.startX-this.manager.width/2))*1/this.scale,m=(s.Y-(this.pan.startY-this.manager.height/2))*1/this.scale;this.pan.translateX=l,this.pan.translateY=m,this.debugger?.show&&(this.debugger.DOM.pinchTarget.style.left=`${this.pinch.target.X*this.scale/this.pinch.startScale-5+this.manager.width/2}px`,this.debugger.DOM.pinchTarget.style.top=`${this.pinch.target.Y*this.scale/this.pinch.startScale-5+this.manager.height/2}px`,this.debugger.DOM.pinchTarget.innerText=`${(this.pinch.target.X*this.scale/this.pinch.startScale).toFixed(2)},${(this.pinch.target.Y*this.scale/this.pinch.startScale).toFixed(2)}`)}const a=this.frameBounds.width/6,i=this.frameBounds.height/6;this.pan.translateX<-a&&(this.pan.overscrollX=-(a+this.pan.translateX),this.pan.translateX=-a),this.pan.translateY<-i&&(this.pan.overscrollY=-(i+this.pan.translateY),this.pan.translateY=-i),this.pan.translateX>a&&(this.pan.overscrollX=a-this.pan.translateX,this.pan.translateX=a),this.pan.translateY>i&&(this.pan.overscrollY=i-this.pan.translateY,this.pan.translateY=i),r=!0,this.debugger?.show&&(this.debugger.DOM.stats.innerText=`TX: ${this.pan.translateX.toFixed(2)}
|
|
2
2
|
TY: ${this.pan.translateY.toFixed(2)}
|
|
3
3
|
Zoom: ${this.scale.toFixed(2)}
|
|
4
|
-
Overscroll: ${this.pan.overscrollX.toFixed(2)},${this.pan.overscrollY.toFixed(2)}`)}if(r){this.manager.updateBookStyle();return}if(this.dragState>0&&this.pan.letItGo){this.pan.endX=e.X;const i=this.manager.currentSlide*(this.manager.width/this.manager.perPage),s=this.pan.endX-this.pan.startX,
|
|
4
|
+
Overscroll: ${this.pan.overscrollX.toFixed(2)},${this.pan.overscrollY.toFixed(2)}`)}if(r){this.manager.updateBookStyle();return}if(this.dragState>0&&this.pan.letItGo){this.pan.endX=e.X;const i=this.manager.currentSlide*(this.manager.width/this.manager.perPage),s=this.pan.endX-this.pan.startX,o=this.manager.rtl?i+s:i-s,n=window.devicePixelRatio||1,l=Math.round((this.manager.rtl?1:-1)*o*n)/n;cancelAnimationFrame(this.moveFrame),this.moveFrame=requestAnimationFrame(()=>{requestAnimationFrame(()=>{b()?(this.dragTransform||(this.dragTranslate=new CSSTranslate(CSS.px(0),CSS.px(0),CSS.px(0)),this.dragTransform=new CSSTransformValue([this.dragTranslate])),this.dragTranslate.x.value=l,this.manager.spineElement.attributeStyleMap.set("transform",this.dragTransform)):this.manager.spineElement.style.transform=`translate3d(${l}px, 0, 0)`})})}}dblclickHandler(t){clearTimeout(this.dtimer),this.pdblclick=!0,this.dtimer=window.setTimeout(()=>this.pdblclick=!1,200),!this.disableDblClick&&this.isScaled&&(this.scale=1)}get isScaled(){return this.scale>1}addTouch(t){t.touches=[{pageX:t.pageX,pageY:t.pageY}]}mousedownHandler(t){this.isScaled&&(this.addTouch(t),this.touchstartHandler(t))}mouseupHandler(t){this.isScaled&&this.touchendHandler(t)}mousemoveHandler(t){this.isScaled&&t.buttons>0&&(t.preventDefault(),this.addTouch(t),this.touchmoveHandler(t))}updateAfterDrag(){const t=(this.manager.rtl?-1:1)*(this.pan.endX-this.pan.startX),e=Math.abs(t);t>0&&e>this.manager.threshold&&this.manager.slength>this.manager.perPage?this.manager.listener("no_less",void 0):t<0&&e>this.manager.threshold&&this.manager.slength>this.manager.perPage&&this.manager.listener("no_more",void 0),this.manager.slideToCurrent(!0,!0)}}export{T as FXLPeripherals,b as isTypedOMSupported};
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{MediaNavigator as o,Navigator as t,VisualNavigator as
|
|
1
|
+
import{MediaNavigator as o,Navigator as t,VisualNavigator as a}from"./Navigator.js";import{BUILTIN_DECORATION_TYPES as i,DecorationLayout as p,DecorationStyleType as f,DecorationWidth as g,decorationsEqual as m,resolveDecorationForWire as x,supportsDecorationStyle as s}from"@readium/decorator";import{ExperimentalWebPubNavigator as l,WebPubNavigator as P}from"./webpub/WebPubNavigator.js";import{WebPubBlobBuilder as d}from"./webpub/WebPubBlobBuilder.js";import{WebPubFrameManager as S}from"./webpub/WebPubFrameManager.js";import{WebPubFramePoolManager as D}from"./webpub/WebPubFramePoolManager.js";import{WebPubDefaults as R}from"./webpub/preferences/WebPubDefaults.js";import{WebPubPreferencesEditor as W}from"./webpub/preferences/WebPubPreferencesEditor.js";import{WebPubPreferences as F}from"./webpub/preferences/WebPubPreferences.js";import{WebPubSettings as L}from"./webpub/preferences/WebPubSettings.js";import{WebRSProperties as T,WebUserProperties as I}from"./webpub/css/Properties.js";import{WebPubCSS as O}from"./webpub/css/WebPubCSS.js";import{EpubNavigator as B}from"./epub/EpubNavigator.js";import{FrameComms as X}from"./epub/frame/FrameComms.js";import{FrameManager as H}from"./epub/frame/FrameManager.js";import{FramePoolManager as q}from"./epub/frame/FramePoolManager.js";import{FXLCoordinator as U,HorizontalThird as w,VerticalThird as G}from"./epub/fxl/FXLCoordinator.js";import{FXLFrameManager as Q}from"./epub/fxl/FXLFrameManager.js";import{FXLFramePoolManager as J}from"./epub/fxl/FXLFramePoolManager.js";import{FXLPeripherals as Z,isTypedOMSupported as $}from"./epub/fxl/FXLPeripherals.js";import{FXLSpreader as re,Orientation as oe,Spread as te}from"./epub/fxl/FXLSpreader.js";import{EpubDefaults as ne}from"./epub/preferences/EpubDefaults.js";import{EpubPreferencesEditor as pe}from"./epub/preferences/EpubPreferencesEditor.js";import{EpubPreferences as ge}from"./epub/preferences/EpubPreferences.js";import{EpubSettings as xe}from"./epub/preferences/EpubSettings.js";import{RSProperties as ue,UserProperties as le}from"./epub/css/Properties.js";import{ReadiumCSS as be}from"./epub/css/ReadiumCSS.js";import{DivinaNavigator as ce}from"./divina/DivinaNavigator.js";import{DivinaPage as ve,DivinaSpreader as De}from"./divina/DivinaSpreader.js";import{DivinaPageManager as Re,placeholderSVG as Ce}from"./divina/DivinaPageManager.js";import{DivinaPagedPresenter as he}from"./divina/DivinaPagedPresenter.js";import{DivinaScrolledPresenter as Me}from"./divina/DivinaScrolledPresenter.js";import{DivinaPeripherals as Ne}from"./divina/DivinaPeripherals.js";import{DivinaQuality as Ie,MOBILE_MAX_HEIGHT as Ae,MOBILE_MAX_WIDTH as Oe,gatherVariants as ye,selectVariant as Be}from"./divina/DivinaVariantSelector.js";import{DivinaDefaults as Xe}from"./divina/preferences/DivinaDefaults.js";import{DivinaPreferences as He,stripWidthRangeConfig as ke}from"./divina/preferences/DivinaPreferences.js";import{DivinaPreferencesEditor as ze}from"./divina/preferences/DivinaPreferencesEditor.js";import{DivinaSettings as we}from"./divina/preferences/DivinaSettings.js";import{WebAudioEngine as je}from"./audio/engine/WebAudioEngine.js";import{AudioPreferences as Ye}from"./audio/preferences/AudioPreferences.js";import{AudioDefaults as Ke}from"./audio/preferences/AudioDefaults.js";import{AudioSettings as $e}from"./audio/preferences/AudioSettings.js";import{AudioPreferencesEditor as rr}from"./audio/preferences/AudioPreferencesEditor.js";import{AudioNavigator as tr}from"./audio/AudioNavigator.js";import{LineLengths as nr}from"./helpers/lineLength.js";import{getScriptMode as pr}from"./helpers/scriptMode.js";import{BooleanPreference as gr,EnumPreference as mr,Preference as xr,RangePreference as sr}from"./preferences/Preference.js";import{TextAlignment as lr,experiments as Pr,filterRangeConfig as br,fontSizeRangeConfig as dr,fontWeightRangeConfig as cr,fontWidthRangeConfig as Sr,i18n as vr,letterSpacingRangeConfig as Dr,lineHeightRangeConfig as Er,lineLengthRangeConfig as Rr,paragraphIndentRangeConfig as Cr,paragraphSpacingRangeConfig as Wr,playbackRateRangeConfig as hr,settings as Fr,skipIntervalRangeConfig as Mr,volumeRangeConfig as Lr,wordSpacingRangeConfig as Nr,zoomRangeConfig as Tr}from"./preferences/Types.js";import{ensureBoolean as Ar,ensureEnumValue as Or,ensureExperiment as yr,ensureFilter as Br,ensureLessThanOrEqual as Vr,ensureMoreThanOrEqual as Xr,ensureNonNegative as _r,ensureString as Hr,ensureValueInRange as kr,withFallback as qr}from"./preferences/guards.js";import{Properties as Ur}from"./css/Properties.js";import{Injector as Gr}from"./injection/Injector.js";export{Ke as AudioDefaults,tr as AudioNavigator,Ye as AudioPreferences,rr as AudioPreferencesEditor,$e as AudioSettings,i as BUILTIN_DECORATION_TYPES,gr as BooleanPreference,p as DecorationLayout,f as DecorationStyleType,g as DecorationWidth,Xe as DivinaDefaults,ce as DivinaNavigator,ve as DivinaPage,Re as DivinaPageManager,he as DivinaPagedPresenter,Ne as DivinaPeripherals,He as DivinaPreferences,ze as DivinaPreferencesEditor,Ie as DivinaQuality,Me as DivinaScrolledPresenter,we as DivinaSettings,De as DivinaSpreader,mr as EnumPreference,ne as EpubDefaults,B as EpubNavigator,ge as EpubPreferences,pe as EpubPreferencesEditor,xe as EpubSettings,l as ExperimentalWebPubNavigator,U as FXLCoordinator,Q as FXLFrameManager,J as FXLFramePoolManager,Z as FXLPeripherals,re as FXLSpreader,X as FrameComms,H as FrameManager,q as FramePoolManager,w as HorizontalThird,Gr as Injector,nr as LineLengths,Ae as MOBILE_MAX_HEIGHT,Oe as MOBILE_MAX_WIDTH,o as MediaNavigator,t as Navigator,oe as Orientation,xr as Preference,Ur as Properties,ue as RSProperties,sr as RangePreference,be as ReadiumCSS,te as Spread,lr as TextAlignment,le as UserProperties,G as VerticalThird,a as VisualNavigator,je as WebAudioEngine,d as WebPubBlobBuilder,O as WebPubCSS,R as WebPubDefaults,S as WebPubFrameManager,D as WebPubFramePoolManager,P as WebPubNavigator,F as WebPubPreferences,W as WebPubPreferencesEditor,L as WebPubSettings,T as WebRSProperties,I as WebUserProperties,m as decorationsEqual,Ar as ensureBoolean,Or as ensureEnumValue,yr as ensureExperiment,Br as ensureFilter,Vr as ensureLessThanOrEqual,Xr as ensureMoreThanOrEqual,_r as ensureNonNegative,Hr as ensureString,kr as ensureValueInRange,Pr as experiments,br as filterRangeConfig,dr as fontSizeRangeConfig,cr as fontWeightRangeConfig,Sr as fontWidthRangeConfig,ye as gatherVariants,pr as getScriptMode,vr as i18n,$ as isTypedOMSupported,Dr as letterSpacingRangeConfig,Er as lineHeightRangeConfig,Rr as lineLengthRangeConfig,Cr as paragraphIndentRangeConfig,Wr as paragraphSpacingRangeConfig,Ce as placeholderSVG,hr as playbackRateRangeConfig,x as resolveDecorationForWire,Be as selectVariant,Fr as settings,Mr as skipIntervalRangeConfig,ke as stripWidthRangeConfig,s as supportsDecorationStyle,Lr as volumeRangeConfig,qr as withFallback,Nr as wordSpacingRangeConfig,Tr as zoomRangeConfig};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
class
|
|
1
|
+
class c{constructor(i,s){this.config=i,this.onUpdate=s,this.unsubs=[],this.conditionValues=new Map}setup(){let i=!0;const s=new Map;this.config.forEach(o=>o.keyCombos.forEach(t=>{if(t.condition){const n=s.get(t.condition)??[];n.push(t),s.set(t.condition,n)}})),s.forEach((o,t)=>{const n=t.subscribe(e=>{o.forEach(a=>this.conditionValues.set(a,e)),i||this.onUpdate(this.buildSerializable())});this.unsubs.push(n)}),i=!1,this.onUpdate(this.buildSerializable())}buildSerializable(){return this.config.map(i=>({...i,keyCombos:i.keyCombos.filter(s=>!s.condition||this.conditionValues.get(s)===!0).map(({condition:s,...o})=>o)})).filter(i=>i.keyCombos.length>0)}destroy(){this.unsubs.forEach(i=>i()),this.unsubs=[],this.conditionValues.clear()}}export{c as KeyboardConditionBridge};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{KeyCombinationManager as
|
|
1
|
+
import{KeyCombinationManager as i}from"@readium/navigator-html-injectables";const r="readium:navigator:keyboardPeripheral";class o{constructor(e={}){this.keyManager=new i,this.pagehideHandler=t=>{t.persisted||this.destroy()},this.setupKeyboardPeripherals(e.keyboardPeripherals||[])}setupKeyboardPeripherals(e){if(e.length>0){const t=n=>{const d=new CustomEvent(r,{detail:n});window.dispatchEvent(d)};this.keydownHandler=this.keyManager.createUnifiedHandler("",e,t),this.keydownHandler&&document.addEventListener("keydown",this.keydownHandler,!0)}window.addEventListener("pagehide",this.pagehideHandler)}destroy(){this.keydownHandler&&(document.removeEventListener("keydown",this.keydownHandler,!0),this.keydownHandler=void 0),window.removeEventListener("pagehide",this.pagehideHandler)}}export{o as KeyboardPeripherals,r as NAVIGATOR_KEYBOARD_PERIPHERAL_EVENT};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Loader as o}from"@readium/navigator-html-injectables";import{FrameComms as n}from"../epub/frame/FrameComms.js";import{sML as h}from"@readium/helpers";import{KeyboardConditionBridge as a}from"../peripherals/KeyboardConditionBridge.js";class y{constructor(t,i={},s=[],e=[],r=[]){this.timelineFragmentIds=e,this.hidden=!0,this.destroyed=!1,this.pendingUnwatchSelectors=[],this.currModules=[],this.frame=document.createElement("iframe"),this.frame.classList.add("readium-navigator-iframe"),this.frame.style.visibility="hidden",this.frame.
|
|
1
|
+
import{Loader as o}from"@readium/navigator-html-injectables";import{FrameComms as n}from"../epub/frame/FrameComms.js";import{sML as h}from"@readium/helpers";import{KeyboardConditionBridge as a}from"../peripherals/KeyboardConditionBridge.js";class y{constructor(t,i={},s=[],e=[],r=[]){this.timelineFragmentIds=e,this.hidden=!0,this.destroyed=!1,this.pendingUnwatchSelectors=[],this.currModules=[],this.frame=document.createElement("iframe"),this.frame.classList.add("readium-navigator-iframe"),this.frame.style.visibility="hidden",this.frame.ariaHidden="true",this.frame.style.opacity="0",this.frame.style.position="absolute",this.frame.style.pointerEvents="none",this.frame.style.transition="visibility 0s, opacity 0.1s linear",this.frame.style.backgroundColor="#FFFFFF",this.source=t,this.contentProtectionConfig={...i},this.keyboardPeripheralsConfig=[...s],this.resizeWatchSelectors=[...r]}async load(t=[]){return new Promise((i,s)=>{if(this.loader){const e=this.frame.contentWindow;if([...this.currModules].sort().join("|")===[...t].sort().join("|")){try{i(e)}catch{}return}this.comms?.halt(),this.loader.destroy(),this.loader=new o(e,t),this.currModules=t,this.comms=void 0;try{i(e)}catch{}return}this.frame.onload=()=>{const e=this.frame.contentWindow;this.loader=new o(e,t),this.currModules=t;try{i(e)}catch{}},this.frame.onerror=e=>{try{s(e)}catch{}},this.frame.contentWindow.location.replace(this.source)})}applyContentProtection(){this.comms||this.comms.resume(),this.comms.send("peripherals_protection",this.contentProtectionConfig),this.keyboardPeripheralsConfig&&this.keyboardPeripheralsConfig.length>0&&(this.conditionBridge?.destroy(),this.conditionBridge=new a(this.keyboardPeripheralsConfig,t=>{t.length>0&&this.comms.send("keyboard_peripherals",t)}),this.conditionBridge.setup()),this.contentProtectionConfig.monitorScrollingExperimental&&this.comms.send("scroll_protection",{}),this.contentProtectionConfig.protectPrinting?.disable&&this.comms.send("print_protection",this.contentProtectionConfig.protectPrinting),this.resizeWatchSelectors.forEach(t=>{this.comms.send("decoration_resize",{action:"watch",selector:t})}),this.pendingUnwatchSelectors.forEach(t=>{this.comms.send("decoration_resize",{action:"unwatch",selector:t})}),this.pendingUnwatchSelectors=[]}addResizeTarget(t){this.resizeWatchSelectors.includes(t)||this.resizeWatchSelectors.push(t),this.pendingUnwatchSelectors=this.pendingUnwatchSelectors.filter(i=>i!==t),this.comms?.ready&&this.comms.send("decoration_resize",{action:"watch",selector:t})}removeResizeTarget(t){this.resizeWatchSelectors=this.resizeWatchSelectors.filter(i=>i!==t),this.comms?.ready?this.comms.send("decoration_resize",{action:"unwatch",selector:t}):this.pendingUnwatchSelectors.includes(t)||this.pendingUnwatchSelectors.push(t)}async destroy(){this.conditionBridge?.destroy(),await this.hide(),this.loader?.destroy(),this.frame.remove(),this.destroyed=!0}async hide(){if(!this.destroyed){if(this.frame.style.visibility="hidden",this.frame.ariaHidden="true",this.frame.style.opacity="0",this.frame.style.pointerEvents="none",this.hidden=!0,this.frame.parentElement)return this.comms===void 0||!this.comms.ready?void 0:new Promise((t,i)=>{this.comms?.send("unfocus",void 0,s=>{this.comms?.halt(),t()})});this.comms?.halt()}}async show(t){if(this.destroyed)throw Error("Trying to show frame when it doesn't exist");if(!this.frame.parentElement)throw Error("Trying to show frame that is not attached to the DOM");return this.comms?this.comms.resume():this.comms=new n(this.frame.contentWindow,this.source),new Promise((i,s)=>{this.comms?.send("activate",void 0,()=>{this.comms?.send("focus",void 0,()=>{this.applyContentProtection(),this.timelineFragmentIds.length>0&&this.comms?.send("timeline_entries",this.timelineFragmentIds);const e=()=>{this.frame.style.removeProperty("visibility"),this.frame.ariaHidden=null,this.frame.style.removeProperty("opacity"),this.frame.style.removeProperty("pointer-events"),this.hidden=!1,h.UA.WebKit&&this.comms?.send("force_webkit_recalc",void 0),i()};t!==void 0?this.comms?.send("go_progression",t,e):e()})})})}setCSSProperties(t){this.destroyed||!this.frame.contentWindow||(this.hidden&&!this.comms&&(this.comms=new n(this.frame.contentWindow,this.source),this.comms.halt()),this.comms?.send("update_properties",t))}get iframe(){if(this.destroyed)throw Error("Trying to use frame when it doesn't exist");return this.frame}get realSize(){if(this.destroyed)throw Error("Trying to use frame client rect when it doesn't exist");return this.frame.getBoundingClientRect()}get window(){if(this.destroyed||!this.frame.contentWindow)throw Error("Trying to use frame window when it doesn't exist");return this.frame.contentWindow}get msg(){return this.comms}get ldr(){return this.loader}}export{y as WebPubFrameManager};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{WebPubBlobBuilder as w}from"./WebPubBlobBuilder.js";import{WebPubFrameManager as y}from"./WebPubFrameManager.js";class R{constructor(e,i,s,n={},o=[],r,h=[]){this.pool=new Map,this.blobs=new Map,this.inprogress=new Map,this.pendingUpdates=new Map,this.injector=null,this.container=e,this.currentCssProperties=i,this.injector=s,this.contentProtectionConfig=n,this.keyboardPeripheralsConfig=[...o],this.getFragmentIds=r??(()=>[]),this.resizeWatchSelectors=[...h]}addResizeTarget(e){this.resizeWatchSelectors.includes(e)||this.resizeWatchSelectors.push(e),this.pool.forEach(i=>i.addResizeTarget(e))}removeResizeTarget(e){this.resizeWatchSelectors=this.resizeWatchSelectors.filter(i=>i!==e),this.pool.forEach(i=>i.removeResizeTarget(e))}async destroy(){let e=this.inprogress.values(),i=e.next();const s=[];for(;i.value;)s.push(i.value),i=e.next();s.length>0&&await Promise.allSettled(s),this.inprogress.clear();let n=this.pool.values(),o=n.next();for(;o.value;)await o.value.destroy(),o=n.next();this.pool.clear(),this.blobs.forEach(r=>{this.injector?.releaseBlobUrl?.(r),URL.revokeObjectURL(r)}),this.blobs.clear(),this.injector?.dispose(),this.container.childNodes.forEach(r=>{(r.nodeType===Node.ELEMENT_NODE||r.nodeType===Node.TEXT_NODE)&&r.remove()})}async update(e,i,s){const n=e.readingOrder.items;let o=n.findIndex(f=>f.href===i.href);if(o<0)throw Error(`Locator not found in reading order: ${i.href}`);const r=n[o].href;this.inprogress.has(r)&&await this.inprogress.get(r);const h=new Promise(async(f,u)=>{const g=[],d=[];e.readingOrder.items.forEach((t,
|
|
1
|
+
import{WebPubBlobBuilder as w}from"./WebPubBlobBuilder.js";import{WebPubFrameManager as y}from"./WebPubFrameManager.js";class R{constructor(e,i,s,n={},o=[],r,h=[]){this.pool=new Map,this.blobs=new Map,this.inprogress=new Map,this.pendingUpdates=new Map,this.injector=null,this.container=e,this.currentCssProperties=i,this.injector=s,this.contentProtectionConfig=n,this.keyboardPeripheralsConfig=[...o],this.getFragmentIds=r??(()=>[]),this.resizeWatchSelectors=[...h]}addResizeTarget(e){this.resizeWatchSelectors.includes(e)||this.resizeWatchSelectors.push(e),this.pool.forEach(i=>i.addResizeTarget(e))}removeResizeTarget(e){this.resizeWatchSelectors=this.resizeWatchSelectors.filter(i=>i!==e),this.pool.forEach(i=>i.removeResizeTarget(e))}async destroy(){let e=this.inprogress.values(),i=e.next();const s=[];for(;i.value;)s.push(i.value),i=e.next();s.length>0&&await Promise.allSettled(s),this.inprogress.clear();let n=this.pool.values(),o=n.next();for(;o.value;)await o.value.destroy(),o=n.next();this.pool.clear(),this.blobs.forEach(r=>{this.injector?.releaseBlobUrl?.(r),URL.revokeObjectURL(r)}),this.blobs.clear(),this.injector?.dispose(),this.container.childNodes.forEach(r=>{(r.nodeType===Node.ELEMENT_NODE||r.nodeType===Node.TEXT_NODE)&&r.remove()})}async update(e,i,s){const n=e.readingOrder.items;let o=n.findIndex(f=>f.href===i.href);if(o<0)throw Error(`Locator not found in reading order: ${i.href}`);const r=n[o].href;this.inprogress.has(r)&&await this.inprogress.get(r);const h=new Promise(async(f,u)=>{const g=[],d=[];e.readingOrder.items.forEach((t,l)=>{l!==o&&l!==o-1&&l!==o+1&&(g.includes(t.href)||g.push(t.href)),l===o&&(d.includes(t.href)||d.push(t.href))}),g.forEach(async t=>{d.includes(t)||this.pool.has(t)&&(await this.pool.get(t)?.destroy(),this.pool.delete(t))}),this.currentBaseURL!==void 0&&e.baseURL!==this.currentBaseURL&&(this.blobs.forEach(t=>{this.injector?.releaseBlobUrl?.(t),URL.revokeObjectURL(t)}),this.blobs.clear()),this.currentBaseURL=e.baseURL;const b=async t=>{if(this.pendingUpdates.has(t)&&this.pendingUpdates.get(t)?.inPool===!1){const c=this.blobs.get(t);c&&(this.injector?.releaseBlobUrl?.(c),URL.revokeObjectURL(c),this.blobs.delete(t),this.pendingUpdates.delete(t))}if(this.pool.has(t)){const c=this.pool.get(t);if(!this.blobs.has(t))await c.destroy(),this.pool.delete(t),this.pendingUpdates.delete(t);else{await c.load(s);return}}const l=e.readingOrder.findWithHref(t);if(!l)return;if(!this.blobs.has(t)){const m=await new w(e,this.currentBaseURL||"",l,{cssProperties:this.currentCssProperties,injector:this.injector}).build();this.blobs.set(t,m)}const p=new y(this.blobs.get(t),this.contentProtectionConfig,this.keyboardPeripheralsConfig,this.getFragmentIds(t),this.resizeWatchSelectors);t!==r&&await p.hide(),this.container.appendChild(p.iframe),await p.load(s),this.pool.set(t,p)};try{await Promise.all(d.map(t=>b(t)))}catch(t){u(t)}const a=this.pool.get(r);if(a?.source!==this._currentFrame?.source&&(await this._currentFrame?.hide(),a&&await a.load(s),a&&await a.show(i.locations.progression),this._currentFrame=a,a)){const t=this.container.ownerDocument.activeElement;t&&t.tagName==="IFRAME"&&t!==a.iframe&&a.iframe.focus({preventScroll:!0})}f()});this.inprogress.set(r,h);try{await h}finally{this.inprogress.delete(r)}}setCSSProperties(e){if(!((s,n)=>{const o=Object.keys(s),r=Object.keys(n);if(o.length!==r.length)return!1;for(const h of o)if(s[h]!==n[h])return!1;return!0})(this.currentCssProperties||{},e)){this.currentCssProperties=e,this.pool.forEach(s=>{s.setCSSProperties(e)});for(const s of this.blobs.keys())this.pendingUpdates.set(s,{inPool:this.pool.has(s)})}}get currentFrames(){return[this._currentFrame]}get currentBounds(){const e={x:0,y:0,width:0,height:0,top:0,right:0,bottom:0,left:0,toJSON(){return this}};return this.currentFrames.forEach(i=>{if(!i)return;const s=i.realSize;e.x=Math.min(e.x,s.x),e.y=Math.min(e.y,s.y),e.width+=s.width,e.height=Math.max(e.height,s.height),e.top=Math.min(e.top,s.top),e.right=Math.min(e.right,s.right),e.bottom=Math.min(e.bottom,s.bottom),e.left=Math.min(e.left,s.left)}),e}}export{R as WebPubFramePoolManager};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Feature as L,Link as u,Locator as p,LocatorText as P,getCssSelector as y,getHtmlId as S,LocatorLocations as k}from"@readium/shared";import{VisualNavigator as D}from"../Navigator.js";import{WebPubFramePoolManager as x}from"./WebPubFramePoolManager.js";import{WebPubModules as C}from"@readium/navigator-html-injectables";import{p as g}from"../_virtual/index.js";import{getScriptMode as I}from"../helpers/scriptMode.js";import{WebPubCSS as E}from"./css/WebPubCSS.js";import{WebUserProperties as O,WebRSProperties as A}from"./css/Properties.js";import{WebPubPreferences as F}from"./preferences/WebPubPreferences.js";import{WebPubDefaults as T}from"./preferences/WebPubDefaults.js";import{WebPubSettings as _}from"./preferences/WebPubSettings.js";import{WebPubPreferencesEditor as v}from"./preferences/WebPubPreferencesEditor.js";import{Injector as W}from"../injection/Injector.js";import{createReadiumWebPubRules as R}from"../injection/webpubInjectables.js";import{NavigatorProtector as H,NAVIGATOR_SUSPICIOUS_ACTIVITY_EVENT as b}from"../protection/NavigatorProtector.js";import{KeyboardPeripherals as M,NAVIGATOR_KEYBOARD_PERIPHERAL_EVENT as w}from"../peripherals/KeyboardPeripherals.js";import{supportsDecorationStyle as N,decorationsEqual as z,resolveDecorationForWire as m}from"@readium/decorator";const V=h=>({frameLoaded:h.frameLoaded||(()=>{}),positionChanged:h.positionChanged||(()=>{}),timelineItemChanged:h.timelineItemChanged||(()=>{}),tap:h.tap||(()=>!1),click:h.click||(()=>!1),zoom:h.zoom||(()=>{}),scroll:h.scroll||(()=>{}),customEvent:h.customEvent||(()=>{}),handleLocator:h.handleLocator||(()=>!1),textSelected:h.textSelected||(()=>{}),contentProtection:h.contentProtection||(()=>{}),contextMenu:h.contextMenu||(()=>{}),peripheral:h.peripheral||(()=>{})});function j(h,e){return h.length===e.length&&h.every((t,i)=>t===e[i])}class B extends D{constructor(e,t,i,r=void 0,o={preferences:{},defaults:{}}){super(),this.currentIndex=0,this._visibleFragmentIds=[],this._notifiedVisibleFragmentIds=[],this._preferencesEditor=null,this._injector=null,this._isNavigating=!1,this._navigatorProtector=null,this._keyboardPeripheralsManager=null,this._suspiciousActivityListener=null,this._keyboardPeripheralListener=null,this._decorations=new Map,this._decorationObservers=new Map,this._decorationHoveredDecorations=new Map,this._decorationActivationState=new Map,this._decorationHoverState=new Map,this._decorationActivationConsumed=!1,this.webViewport={readingOrder:[],progressions:new Map,positions:null},this.pub=t,this.container=e,this.listeners=V(i),this._preferences=new F(o.preferences),this._defaults=new T(o.defaults),this._settings=new _(this._preferences,this._defaults,this.hasDisplayTransformability),this._css=new E({rsProperties:new A({experiments:this._settings.experiments||null}),userProperties:new O({zoom:this._settings.zoom})});const n=R(t.readingOrder.items),l=o.injectables||{rules:[],allowedDomains:[]};if(this._injector=new W({rules:[...n,...l.rules],allowedDomains:l.allowedDomains}),this._contentProtection=o.contentProtection||{},this._decoratorConfig=o.decoratorConfig||{},this._decorationResizeSelectors=new Set(this._decoratorConfig.resizeWatchSelectors??[]),this._keyboardPeripherals=this.mergeKeyboardPeripherals(this._contentProtection,o.keyboardPeripherals||[]),(this._contentProtection.disableContextMenu||this._contentProtection.checkAutomation||this._contentProtection.checkIFrameEmbedding||this._contentProtection.monitorDevTools||this._contentProtection.protectPrinting?.disable)&&(this._navigatorProtector=new H(this._contentProtection),this._suspiciousActivityListener=a=>{const{type:s,...c}=a.detail;s==="context_menu"?this.listeners.contextMenu(c):this.listeners.contentProtection(s,c)},window.addEventListener(b,this._suspiciousActivityListener)),this._keyboardPeripherals.length>0&&(this._keyboardPeripheralsManager=new M({keyboardPeripherals:this._keyboardPeripherals}),this._keyboardPeripheralListener=a=>{const s=a.detail;this.listeners.peripheral(s)},window.addEventListener(w,this._keyboardPeripheralListener)),r&&typeof r.copyWithLocations=="function"){this.currentLocation=r;const a=this.pub.readingOrder.findIndexWithHref(r.href);a>=0&&(this.currentIndex=a)}else this.currentLocation=this.createCurrentLocator()}async load(){await this.updateCSS(!1);const e=this.compileCSSProperties(this._css);this.framePool=new x(this.container,e,this._injector,this._contentProtection,this._keyboardPeripherals,t=>this.pub.timeline.segmentsForHref(t).flatMap(i=>i.references).map(i=>{const r=i.indexOf("#");return r>=0?i.slice(r+1):""}).filter(Boolean),[...this._decorationResizeSelectors]),await this.apply()}get settings(){return Object.freeze({...this._settings})}get preferencesEditor(){return this._preferencesEditor===null&&(this._preferencesEditor=new v(this._preferences,this.settings,this.pub.metadata)),this._preferencesEditor}async submitPreferences(e){this._preferences=this._preferences.merging(e),await this.applyPreferences()}async applyPreferences(){this._settings=new _(this._preferences,this._defaults,this.hasDisplayTransformability),this._preferencesEditor!==null&&(this._preferencesEditor=new v(this._preferences,this.settings,this.pub.metadata)),await this.updateCSS(!0)}async updateCSS(e){this._css.update(this._settings),e&&await this.commitCSS(this._css)}compileCSSProperties(e){const t={};for(const[i,r]of Object.entries(e.rsProperties.toCSSProperties()))t[i]=r;for(const[i,r]of Object.entries(e.userProperties.toCSSProperties()))t[i]=r;return t}async commitCSS(e){const t=this.compileCSSProperties(e);this.framePool.setCSSProperties(t)}get _cframes(){return this.framePool.currentFrames}get hasDisplayTransformability(){return this.pub.metadata?.accessibility?.feature?.some(e=>e.value===L.DISPLAY_TRANSFORMABILITY.value)??!1}eventListener(e,t){switch(e){case"_pong":this.listeners.frameLoaded(this.framePool.currentFrames[0].iframe.contentWindow),this.listeners.positionChanged(this.currentLocation),this._notifyTimelineChange(this.currentLocation),this._reapplyDecorationsToCurrentFrame();break;case"first_visible_locator":const i=p.deserialize(t);if(!i)break;this.currentLocation=new p({href:this.currentLocation.href,type:this.currentLocation.type,title:this.currentLocation.title,locations:i?.locations,text:i?.text}),this.listeners.positionChanged(this.currentLocation),this._notifyTimelineChange(this.currentLocation);break;case"text_selected":{const a=t;a.locator=new p({href:this.currentLocation.href,type:this.currentLocation.type,text:new P({highlight:a.text})}),this.listeners.textSelected(a);break}case"decoration_activated":{this._handleDecorationActivated(t)&&(this._decorationActivationConsumed=!0);break}case"decoration_pointer_enter":this._handleDecorationPointerEnter(t);break;case"decoration_pointer_leave":this._handleDecorationPointerLeave(t);break;case"click":case"tap":if(this._decorationActivationConsumed){this._decorationActivationConsumed=!1;break}const r=t;if(r.interactiveElement){const a=new DOMParser().parseFromString(r.interactiveElement,"text/html").body.children[0];if(a.nodeType===a.ELEMENT_NODE&&a.nodeName==="A"&&a.hasAttribute("href")){const s=a.attributes.getNamedItem("href")?.value;if(s.startsWith("#"))this.go(this.currentLocation.copyWithLocations({fragments:[s.substring(1)]}),!1,()=>{});else if(s.startsWith("mailto:")||s.startsWith("tel:"))this.listeners.handleLocator(new u({href:s}).locator);else try{let c;if(s.startsWith("http://")||s.startsWith("https://"))c=s;else if(this.currentLocation.href.startsWith("http://")||this.currentLocation.href.startsWith("https://")){const f=new URL(this.currentLocation.href);c=new URL(s,f).href}else c=g.join(g.dirname(this.currentLocation.href),s);const d=this.pub.readingOrder.findWithHref(c);d?this.goLink(d,!1,()=>{}):(console.warn(`Internal link not found in readingOrder: ${c}`),this.listeners.handleLocator(new u({href:s}).locator))}catch(c){console.warn(`Couldn't resolve internal link for ${s}: ${c}`),this.listeners.handleLocator(new u({href:s}).locator)}}else console.log("Clicked on",a)}else if(e==="click"?this.listeners.click(r):this.listeners.tap(r))break;break;case"scroll":this.listeners.scroll(t);break;case"zoom":this.listeners.zoom(t);break;case"progress":this.syncLocation(t);break;case"content_protection":const o=t;this.listeners.contentProtection(o.type,o);break;case"context_menu":this.listeners.contextMenu(t);break;case"keyboard_peripherals":const n=t,l={...n,interactiveElement:void 0};n.interactiveElement&&(l.interactiveElement=new DOMParser().parseFromString(n.interactiveElement,"text/html").body.children[0]),this.listeners.peripheral(l);break;case"log":console.log(this.framePool.currentFrames[0]?.source?.split("/")[3],...t);break;default:this.listeners.customEvent(e,t);break}}determineModules(){const e=C.slice(),t=I(this.pub.metadata);return t==="cjk-vertical"||t==="mongolian-vertical"?e.map(i=>i==="webpub_snapper"?"cjk_vertical_snapper":i):e}attachListener(){this.framePool.currentFrames[0]?.msg&&(this.framePool.currentFrames[0].msg.listener=(e,t)=>{this.eventListener(e,t)}),this._reapplyDecorationsToCurrentFrame()}async apply(){if(await this.framePool.update(this.pub,this.currentLocation,this.determineModules()),this.attachListener(),this.pub.readingOrder.findIndexWithHref(this.currentLocation.href)<0)throw Error("Link for "+this.currentLocation.href+" not found!")}async destroy(){this._suspiciousActivityListener&&window.removeEventListener(b,this._suspiciousActivityListener),this._keyboardPeripheralListener&&window.removeEventListener(w,this._keyboardPeripheralListener),this._navigatorProtector?.destroy(),this._keyboardPeripheralsManager?.destroy(),await this.framePool?.destroy(),this._decorations.clear(),this._decorationObservers.clear(),this._decorationHoveredDecorations.clear(),this._decorationActivationState.clear(),this._decorationHoverState.clear(),this._decorationResizeSelectors.clear()}supportsDecorationStyle(e){return N(e,this._decoratorConfig.decorationTemplates)}registerDecorationObserver(e,t){this._decorationObservers.has(e)||this._decorationObservers.set(e,new Set),this._decorationObservers.get(e).add(t),t.onDecorationActivated&&(this._decorationActivationState.set(e,!0),this._sendDecorationActivatable(e,!0)),(t.onDecorationPointerEnter||t.onDecorationPointerLeave)&&(this._decorationHoverState.set(e,!0),this._sendDecorationHoverable(e,!0))}unregisterDecorationObserver(e){this._decorationObservers.forEach((t,i)=>{if(!t.has(e))return;t.delete(e);const r=[...t].some(n=>n.onDecorationActivated);this._decorationActivationState.has(i)&&!r&&(this._decorationActivationState.delete(i),this._sendDecorationActivatable(i,!1));const o=[...t].some(n=>n.onDecorationPointerEnter||n.onDecorationPointerLeave);this._decorationHoverState.has(i)&&!o&&(this._decorationHoverState.delete(i),this._sendDecorationHoverable(i,!1))})}_sendDecorationActivatable(e,t){const i=this.framePool?.currentFrames[0];i?.msg&&i.msg.send("decoration_activatable",{group:e,activatable:t})}_sendDecorationHoverable(e,t){const i=this.framePool?.currentFrames[0];i?.msg&&i.msg.send("decoration_hoverable",{group:e,hoverable:t})}addDecorationResizeTarget(e){this._decorationResizeSelectors.add(e),this.framePool?.addResizeTarget(e)}removeDecorationResizeTarget(e){this._decorationResizeSelectors.delete(e),this.framePool?.removeResizeTarget(e)}applyDecorations(e,t){const i=this._decorations.get(t)??[],r=new Map(i.map(d=>[d.id,d])),o=new Map(e.map(d=>[d.id,d])),n=[],l=[],a=[];for(const[d,f]of r)o.has(d)?z(f,o.get(d))||a.push(o.get(d)):n.push(d);for(const[d,f]of o)r.has(d)||l.push(f);this._decorations.set(t,e),this._sendDecorationOps(t,n,l,a,i);const s=this._decorationActivationState.get(t);s!==void 0&&this._sendDecorationActivatable(t,s);const c=this._decorationHoverState.get(t);c!==void 0&&this._sendDecorationHoverable(t,c)}_sendDecorationOps(e,t,i,r,o){const n=this.framePool?.currentFrames[0];if(!n?.msg)return;const l=this.currentLocation.href,a=new Map(o.map(s=>[s.id,s]));for(const s of t){const c=a.get(s);!c||c.locator.href!==l||n.msg.send("decorate",{group:e,action:"remove",decoration:{id:s}})}for(const s of i)s.locator.href===l&&n.msg.send("decorate",{group:e,action:"add",decoration:m(s,this._decoratorConfig.decorationTemplates)});for(const s of r)s.locator.href===l&&n.msg.send("decorate",{group:e,action:"update",decoration:m(s,this._decoratorConfig.decorationTemplates)})}_reapplyDecorationsToCurrentFrame(){const e=this.framePool?.currentFrames[0];if(!e?.msg)return;const t=this.currentLocation.href;for(const[i,r]of this._decorations){const o=r.filter(n=>n.locator.href===t);if(o.length!==0){e.msg.send("decorate",{group:i,action:"clear"});for(const n of o)e.msg.send("decorate",{group:i,action:"add",decoration:m(n,this._decoratorConfig.decorationTemplates)})}}for(const[i,r]of this._decorationActivationState)e.msg.send("decoration_activatable",{group:i,activatable:r});for(const[i,r]of this._decorationHoverState)e.msg.send("decoration_hoverable",{group:i,hoverable:r})}_handleDecorationActivated(e){const t=this._decorationObservers.get(e.group);if(!t||t.size===0)return!1;const i=(this._decorations.get(e.group)??[]).find(n=>n.id===e.decorationId);if(!i)return!1;const r={decoration:i,group:e.group,rect:e.rect,point:e.point};let o=!1;for(const n of t)n.onDecorationActivated?.(r)&&(o=!0);return o}_handleDecorationPointerEnter(e){const t=this._decorationObservers.get(e.group);if(!t||t.size===0)return;const i=(this._decorations.get(e.group)??[]).find(o=>o.id===e.decorationId);if(!i)return;this._decorationHoveredDecorations.set(e.group,i);const r={decoration:i,group:e.group,rect:e.rect,point:e.point};for(const o of t)o.onDecorationPointerEnter?.(r)}_handleDecorationPointerLeave(e){const t=this._decorationObservers.get(e.group);if(!t||t.size===0)return;const i=(this._decorations.get(e.group)??[]).find(o=>o.id===e.decorationId)??this._decorationHoveredDecorations.get(e.group);if(this._decorationHoveredDecorations.delete(e.group),!i)return;const r={decoration:i,group:e.group,rect:e.rect,point:e.point};for(const o of t)o.onDecorationPointerLeave?.(r)}async changeResource(e){if(e===0)return!1;const t=this.pub.readingOrder.findIndexWithHref(this.currentLocation.href),i=Math.max(0,Math.min(this.pub.readingOrder.items.length-1,t+e));return i===t?!1:(this.currentIndex=i,this.currentLocation=this.createCurrentLocator(),await this.apply(),!0)}updateViewport(e){this.webViewport.readingOrder=[],this.webViewport.progressions.clear(),this.webViewport.positions=null,this.currentLocation&&(this.webViewport.readingOrder.push(this.currentLocation.href),this.webViewport.progressions.set(this.currentLocation.href,e),this.currentLocation.locations?.position!==void 0&&(this.webViewport.positions=[this.currentLocation.locations.position]))}async syncLocation(e){const t=e;this.currentLocation&&(this.currentLocation=this.currentLocation.copyWithLocations({progression:t.start})),this.updateViewport(t),this.listeners.positionChanged(this.currentLocation);const i=t.fragmentId?this.currentLocation.copyWithLocations({fragments:[`#${t.fragmentId}`]}):this.currentLocation;this._visibleFragmentIds=t.visibleFragmentIds??[],this._notifyTimelineChange(i),await this.framePool.update(this.pub,this.currentLocation,this.determineModules())}goBackward(e,t){if(this._isNavigating){t(!1);return}this._isNavigating=!0,this.changeResource(-1).then(i=>{this._isNavigating=!1,t(i)})}goForward(e,t){if(this._isNavigating){t(!1);return}this._isNavigating=!0,this.changeResource(1).then(i=>{this._isNavigating=!1,t(i)})}get currentLocator(){return this.currentLocation}get viewport(){return this.webViewport}get isScrollStart(){const e=this.viewport.readingOrder[0];return this.viewport.progressions.get(e)?.start===0}get isScrollEnd(){const e=this.viewport.readingOrder[this.viewport.readingOrder.length-1];return this.viewport.progressions.get(e)?.end===1}get canGoBackward(){const e=this.pub.readingOrder.items[0]?.href;return!(this.viewport.progressions.has(e)&&this.viewport.progressions.get(e)?.start===0)}get canGoForward(){const e=this.pub.readingOrder.items[this.pub.readingOrder.items.length-1]?.href;return!(this.viewport.progressions.has(e)&&this.viewport.progressions.get(e)?.end===1)}get readingProgression(){return this.pub.metadata.effectiveReadingProgression}get publication(){return this.pub}get timeline(){const e=this.pub.timeline;return this._wrappedTimeline||(this._wrappedTimeline=new Proxy(e,{get:(t,i)=>{if(i!=="navigableFrom"){const r=Reflect.get(t,i,t);return typeof r=="function"?r.bind(t):r}return r=>{const o=this._visibleFragmentIds;let n=(o.length?t.locate(this.currentLocation.copyWithLocations({fragments:[`#${o[0]}`]})):void 0)??r;const l=(o.length?t.locate(this.currentLocation.copyWithLocations({fragments:[`#${o[o.length-1]}`]})):void 0)??r;if(this.isScrollStart){const c=t.ancestors(n).find(d=>d.references.includes(this.currentLocation.href));c&&(n=c)}const a=t.navigableFrom(n).previous,s=t.navigableFrom(l).next;return{previous:a,next:s}}}})),this._wrappedTimeline}async loadLocator(e,t){let i=!1,r=y(e.locations);if(e.text?.highlight?i=await new Promise((a,s)=>{this.framePool.currentFrames[0].msg.send("go_text",r?[e.text?.serialize(),r]:e.text?.serialize(),c=>a(c))}):r&&(i=await new Promise((a,s)=>{this.framePool.currentFrames[0].msg.send("go_text",["",r],c=>a(c))})),i){t(i);return}const o=S(e.locations);if(o&&(i=await new Promise((a,s)=>{this.framePool.currentFrames[0].msg.send("go_id",o,c=>a(c))})),i){t(i);return}const n=e?.locations?.progression;n&&n>0?i=await new Promise((a,s)=>{this.framePool.currentFrames[0].msg.send("go_progression",n,c=>a(c))}):i=!0,t(i)}go(e,t,i){const r=e.href.split("#")[0];if(!this.pub.readingOrder.findWithHref(r))return i(this.listeners.handleLocator(e));const n=this.pub.readingOrder.findIndexWithHref(r);if(n>=0&&(this.currentIndex=n),this._isNavigating){i(!1);return}this._isNavigating=!0,this.currentLocation=this.createCurrentLocator(),this.apply().then(()=>this.loadLocator(e,l=>{this._isNavigating=!1,i(l)})).then(()=>{this.attachListener()})}goLink(e,t,i){return this.go(e.locator,t,i)}createCurrentLocator(){const t=this.pub.readingOrder.items[this.currentIndex];if(!t)throw new Error("No current resource available");const r=this.currentLocation&&this.currentLocation.href===t.href&&this.currentLocation.locations.progression?this.currentLocation.locations.progression:0;return this.pub.manifest.locatorFromLink(t)||new p({href:t.href,type:t.type||"text/html",locations:new k({fragments:[],progression:r,position:this.currentIndex+1})})}_notifyTimelineChange(e){const t=this.timeline.locate(e),i=!j(this._visibleFragmentIds,this._notifiedVisibleFragmentIds);(t!==this._currentTimelineItem||i)&&(this._currentTimelineItem=t,this._notifiedVisibleFragmentIds=this._visibleFragmentIds,this.listeners.timelineItemChanged(t))}}const ce=B;export{ce as ExperimentalWebPubNavigator,B as WebPubNavigator};
|
|
1
|
+
import{Feature as w,Link as u,Locator as f,LocatorText as P,getCssSelector as y,getHtmlId as S,LocatorLocations as k}from"@readium/shared";import{VisualNavigator as x}from"../Navigator.js";import{WebPubFramePoolManager as D}from"./WebPubFramePoolManager.js";import{WebPubModules as C}from"@readium/navigator-html-injectables";import{p as g}from"../_virtual/index.js";import{getScriptMode as I}from"../helpers/scriptMode.js";import{WebPubCSS as O}from"./css/WebPubCSS.js";import{WebUserProperties as E,WebRSProperties as F}from"./css/Properties.js";import{WebPubPreferences as A}from"./preferences/WebPubPreferences.js";import{WebPubDefaults as T}from"./preferences/WebPubDefaults.js";import{WebPubSettings as _}from"./preferences/WebPubSettings.js";import{WebPubPreferencesEditor as v}from"./preferences/WebPubPreferencesEditor.js";import{Injector as W}from"../injection/Injector.js";import{createReadiumWebPubRules as M}from"../injection/webpubInjectables.js";import{NavigatorProtector as R,NAVIGATOR_SUSPICIOUS_ACTIVITY_EVENT as b}from"../protection/NavigatorProtector.js";import{KeyboardPeripherals as H,NAVIGATOR_KEYBOARD_PERIPHERAL_EVENT as L}from"../peripherals/KeyboardPeripherals.js";import{supportsDecorationStyle as N,decorationsEqual as z,resolveDecorationForWire as m}from"@readium/decorator";const V=h=>({frameLoaded:h.frameLoaded||(()=>{}),positionChanged:h.positionChanged||(()=>{}),timelineItemChanged:h.timelineItemChanged||(()=>{}),tap:h.tap||(()=>!1),click:h.click||(()=>!1),zoom:h.zoom||(()=>{}),scroll:h.scroll||(()=>{}),customEvent:h.customEvent||(()=>{}),handleLocator:h.handleLocator||(()=>!1),textSelected:h.textSelected||(()=>{}),contentProtection:h.contentProtection||(()=>{}),contextMenu:h.contextMenu||(()=>{}),peripheral:h.peripheral||(()=>{})});function j(h,e){return h.length===e.length&&h.every((t,i)=>t===e[i])}class B extends x{constructor(e,t,i,r=void 0,o={preferences:{},defaults:{}}){super(),this.currentIndex=0,this._destroyed=!1,this._visibleFragmentIds=[],this._notifiedVisibleFragmentIds=[],this._preferencesEditor=null,this._injector=null,this._isNavigating=!1,this._navigatorProtector=null,this._keyboardPeripheralsManager=null,this._suspiciousActivityListener=null,this._keyboardPeripheralListener=null,this._decorations=new Map,this._decorationObservers=new Map,this._decorationHoveredDecorations=new Map,this._decorationActivationState=new Map,this._decorationHoverState=new Map,this._decorationActivationConsumed=!1,this.webViewport={readingOrder:[],progressions:new Map,positions:null},this.pub=t,this.container=e,this.listeners=V(i),this._preferences=new A(o.preferences),this._defaults=new T(o.defaults),this._settings=new _(this._preferences,this._defaults,this.hasDisplayTransformability),this._css=new O({rsProperties:new F({experiments:this._settings.experiments||null}),userProperties:new E({zoom:this._settings.zoom})});const s=M(t.readingOrder.items),l=o.injectables||{rules:[],allowedDomains:[]};if(this._injector=new W({rules:[...s,...l.rules],allowedDomains:l.allowedDomains}),this._contentProtection=o.contentProtection||{},this._decoratorConfig=o.decoratorConfig||{},this._decorationResizeSelectors=new Set(this._decoratorConfig.resizeWatchSelectors??[]),this._keyboardPeripherals=this.mergeKeyboardPeripherals(this._contentProtection,o.keyboardPeripherals||[]),(this._contentProtection.disableContextMenu||this._contentProtection.checkAutomation||this._contentProtection.checkIFrameEmbedding||this._contentProtection.monitorDevTools||this._contentProtection.protectPrinting?.disable)&&(this._navigatorProtector=new R(this._contentProtection),this._suspiciousActivityListener=a=>{const{type:n,...c}=a.detail;n==="context_menu"?this.listeners.contextMenu(c):this.listeners.contentProtection(n,c)},window.addEventListener(b,this._suspiciousActivityListener)),this._keyboardPeripherals.length>0&&(this._keyboardPeripheralsManager=new H({keyboardPeripherals:this._keyboardPeripherals}),this._keyboardPeripheralListener=a=>{const n=a.detail;this.listeners.peripheral(n)},window.addEventListener(L,this._keyboardPeripheralListener)),r&&typeof r.copyWithLocations=="function"){this.currentLocation=r;const a=this.pub.readingOrder.findIndexWithHref(r.href);a>=0&&(this.currentIndex=a)}else this.currentLocation=this.createCurrentLocator()}async load(){if(await this.updateCSS(!1),this._destroyed)return;const e=this.compileCSSProperties(this._css);this.framePool=new D(this.container,e,this._injector,this._contentProtection,this._keyboardPeripherals,t=>this.pub.timeline.segmentsForHref(t).flatMap(i=>i.references).map(i=>{const r=i.indexOf("#");return r>=0?i.slice(r+1):""}).filter(Boolean),[...this._decorationResizeSelectors]),!this._destroyed&&(this.currentLocation=this.completeLocator(this.currentLocation),await this.apply())}get settings(){return Object.freeze({...this._settings})}get preferencesEditor(){return this._preferencesEditor===null&&(this._preferencesEditor=new v(this._preferences,this.settings,this.pub.metadata)),this._preferencesEditor}async submitPreferences(e){this._preferences=this._preferences.merging(e),await this.applyPreferences()}async applyPreferences(){this._settings=new _(this._preferences,this._defaults,this.hasDisplayTransformability),this._preferencesEditor!==null&&(this._preferencesEditor=new v(this._preferences,this.settings,this.pub.metadata)),await this.updateCSS(!0)}async updateCSS(e){this._css.update(this._settings),e&&await this.commitCSS(this._css)}compileCSSProperties(e){const t={};for(const[i,r]of Object.entries(e.rsProperties.toCSSProperties()))t[i]=r;for(const[i,r]of Object.entries(e.userProperties.toCSSProperties()))t[i]=r;return t}async commitCSS(e){const t=this.compileCSSProperties(e);this.framePool.setCSSProperties(t)}get _cframes(){return this.framePool.currentFrames}get hasDisplayTransformability(){return this.pub.metadata?.accessibility?.feature?.some(e=>e.value===w.DISPLAY_TRANSFORMABILITY.value)??!1}eventListener(e,t){switch(e){case"_pong":this.listeners.frameLoaded(this.framePool.currentFrames[0].iframe.contentWindow),this.listeners.positionChanged(this.currentLocation),this._notifyTimelineChange(this.currentLocation),this._reapplyDecorationsToCurrentFrame();break;case"first_visible_locator":const i=f.deserialize(t);if(!i)break;this.currentLocation=new f({href:this.currentLocation.href,type:this.currentLocation.type,title:this.currentLocation.title,locations:i?.locations,text:i?.text}),this.listeners.positionChanged(this.currentLocation),this._notifyTimelineChange(this.currentLocation);break;case"text_selected":{const a=t;a.locator=new f({href:this.currentLocation.href,type:this.currentLocation.type,text:new P({highlight:a.text})}),this.listeners.textSelected(a);break}case"decoration_activated":{this._handleDecorationActivated(t)&&(this._decorationActivationConsumed=!0);break}case"decoration_pointer_enter":this._handleDecorationPointerEnter(t);break;case"decoration_pointer_leave":this._handleDecorationPointerLeave(t);break;case"click":case"tap":if(this._decorationActivationConsumed){this._decorationActivationConsumed=!1;break}const r=t;if(r.interactiveElement){const a=new DOMParser().parseFromString(r.interactiveElement,"text/html").body.children[0];if(a.nodeType===a.ELEMENT_NODE&&a.nodeName==="A"&&a.hasAttribute("href")){const n=a.attributes.getNamedItem("href")?.value;if(n.startsWith("#"))this.go(this.currentLocation.copyWithLocations({fragments:[n.substring(1)]}),!1,()=>{});else if(n.startsWith("mailto:")||n.startsWith("tel:"))this.listeners.handleLocator(new u({href:n}).locator);else try{let c;if(n.startsWith("http://")||n.startsWith("https://"))c=n;else if(this.currentLocation.href.startsWith("http://")||this.currentLocation.href.startsWith("https://")){const p=new URL(this.currentLocation.href);c=new URL(n,p).href}else c=g.join(g.dirname(this.currentLocation.href),n);const d=this.pub.readingOrder.findWithHref(c);d?this.goLink(d,!1,()=>{}):(console.warn(`Internal link not found in readingOrder: ${c}`),this.listeners.handleLocator(new u({href:n}).locator))}catch(c){console.warn(`Couldn't resolve internal link for ${n}: ${c}`),this.listeners.handleLocator(new u({href:n}).locator)}}else console.log("Clicked on",a)}else if(e==="click"?this.listeners.click(r):this.listeners.tap(r))break;break;case"scroll":this.listeners.scroll(t);break;case"zoom":this.listeners.zoom(t);break;case"progress":this.syncLocation(t);break;case"content_protection":const o=t;this.listeners.contentProtection(o.type,o);break;case"context_menu":this.listeners.contextMenu(t);break;case"keyboard_peripherals":const s=t,l={...s,interactiveElement:void 0};s.interactiveElement&&(l.interactiveElement=new DOMParser().parseFromString(s.interactiveElement,"text/html").body.children[0]),this.listeners.peripheral(l);break;case"log":console.log(this.framePool.currentFrames[0]?.source?.split("/")[3],...t);break;default:this.listeners.customEvent(e,t);break}}determineModules(){const e=C.slice(),t=I(this.pub.metadata);return t==="cjk-vertical"||t==="mongolian-vertical"?e.map(i=>i==="webpub_snapper"?"cjk_vertical_snapper":i):e}attachListener(){this.framePool.currentFrames[0]?.msg&&(this.framePool.currentFrames[0].msg.listener=(e,t)=>{this.eventListener(e,t)}),this._reapplyDecorationsToCurrentFrame()}async apply(){if(await this.framePool.update(this.pub,this.currentLocation,this.determineModules()),this.attachListener(),this.pub.readingOrder.findIndexWithHref(this.currentLocation.href)<0)throw Error("Link for "+this.currentLocation.href+" not found!")}async destroy(){this._destroyed=!0,this._suspiciousActivityListener&&window.removeEventListener(b,this._suspiciousActivityListener),this._keyboardPeripheralListener&&window.removeEventListener(L,this._keyboardPeripheralListener),this._navigatorProtector?.destroy(),this._keyboardPeripheralsManager?.destroy(),await this.framePool?.destroy(),this._decorations.clear(),this._decorationObservers.clear(),this._decorationHoveredDecorations.clear(),this._decorationActivationState.clear(),this._decorationHoverState.clear(),this._decorationResizeSelectors.clear()}supportsDecorationStyle(e){return N(e,this._decoratorConfig.decorationTemplates)}registerDecorationObserver(e,t){this._decorationObservers.has(e)||this._decorationObservers.set(e,new Set),this._decorationObservers.get(e).add(t),t.onDecorationActivated&&(this._decorationActivationState.set(e,!0),this._sendDecorationActivatable(e,!0)),(t.onDecorationPointerEnter||t.onDecorationPointerLeave)&&(this._decorationHoverState.set(e,!0),this._sendDecorationHoverable(e,!0))}unregisterDecorationObserver(e){this._decorationObservers.forEach((t,i)=>{if(!t.has(e))return;t.delete(e);const r=[...t].some(s=>s.onDecorationActivated);this._decorationActivationState.has(i)&&!r&&(this._decorationActivationState.delete(i),this._sendDecorationActivatable(i,!1));const o=[...t].some(s=>s.onDecorationPointerEnter||s.onDecorationPointerLeave);this._decorationHoverState.has(i)&&!o&&(this._decorationHoverState.delete(i),this._sendDecorationHoverable(i,!1))})}_sendDecorationActivatable(e,t){const i=this.framePool?.currentFrames[0];i?.msg&&i.msg.send("decoration_activatable",{group:e,activatable:t})}_sendDecorationHoverable(e,t){const i=this.framePool?.currentFrames[0];i?.msg&&i.msg.send("decoration_hoverable",{group:e,hoverable:t})}addDecorationResizeTarget(e){this._decorationResizeSelectors.add(e),this.framePool?.addResizeTarget(e)}removeDecorationResizeTarget(e){this._decorationResizeSelectors.delete(e),this.framePool?.removeResizeTarget(e)}applyDecorations(e,t){const i=this._decorations.get(t)??[],r=new Map(i.map(d=>[d.id,d])),o=new Map(e.map(d=>[d.id,d])),s=[],l=[],a=[];for(const[d,p]of r)o.has(d)?z(p,o.get(d))||a.push(o.get(d)):s.push(d);for(const[d,p]of o)r.has(d)||l.push(p);this._decorations.set(t,e),this._sendDecorationOps(t,s,l,a,i);const n=this._decorationActivationState.get(t);n!==void 0&&this._sendDecorationActivatable(t,n);const c=this._decorationHoverState.get(t);c!==void 0&&this._sendDecorationHoverable(t,c)}_sendDecorationOps(e,t,i,r,o){const s=this.framePool?.currentFrames[0];if(!s?.msg)return;const l=this.currentLocation.href,a=new Map(o.map(n=>[n.id,n]));for(const n of t){const c=a.get(n);!c||c.locator.href!==l||s.msg.send("decorate",{group:e,action:"remove",decoration:{id:n}})}for(const n of i)n.locator.href===l&&s.msg.send("decorate",{group:e,action:"add",decoration:m(n,this._decoratorConfig.decorationTemplates)});for(const n of r)n.locator.href===l&&s.msg.send("decorate",{group:e,action:"update",decoration:m(n,this._decoratorConfig.decorationTemplates)})}_reapplyDecorationsToCurrentFrame(){const e=this.framePool?.currentFrames[0];if(!e?.msg)return;const t=this.currentLocation.href;for(const[i,r]of this._decorations){const o=r.filter(s=>s.locator.href===t);if(o.length!==0){e.msg.send("decorate",{group:i,action:"clear"});for(const s of o)e.msg.send("decorate",{group:i,action:"add",decoration:m(s,this._decoratorConfig.decorationTemplates)})}}for(const[i,r]of this._decorationActivationState)e.msg.send("decoration_activatable",{group:i,activatable:r});for(const[i,r]of this._decorationHoverState)e.msg.send("decoration_hoverable",{group:i,hoverable:r})}_handleDecorationActivated(e){const t=this._decorationObservers.get(e.group);if(!t||t.size===0)return!1;const i=(this._decorations.get(e.group)??[]).find(s=>s.id===e.decorationId);if(!i)return!1;const r={decoration:i,group:e.group,rect:e.rect,point:e.point};let o=!1;for(const s of t)s.onDecorationActivated?.(r)&&(o=!0);return o}_handleDecorationPointerEnter(e){const t=this._decorationObservers.get(e.group);if(!t||t.size===0)return;const i=(this._decorations.get(e.group)??[]).find(o=>o.id===e.decorationId);if(!i)return;this._decorationHoveredDecorations.set(e.group,i);const r={decoration:i,group:e.group,rect:e.rect,point:e.point};for(const o of t)o.onDecorationPointerEnter?.(r)}_handleDecorationPointerLeave(e){const t=this._decorationObservers.get(e.group);if(!t||t.size===0)return;const i=(this._decorations.get(e.group)??[]).find(o=>o.id===e.decorationId)??this._decorationHoveredDecorations.get(e.group);if(this._decorationHoveredDecorations.delete(e.group),!i)return;const r={decoration:i,group:e.group,rect:e.rect,point:e.point};for(const o of t)o.onDecorationPointerLeave?.(r)}async changeResource(e){if(e===0)return!1;const t=this.pub.readingOrder.findIndexWithHref(this.currentLocation.href),i=Math.max(0,Math.min(this.pub.readingOrder.items.length-1,t+e));return i===t?!1:(this.currentIndex=i,this.currentLocation=this.createCurrentLocator(),await this.apply(),!0)}updateViewport(e){this.webViewport.readingOrder=[],this.webViewport.progressions.clear(),this.webViewport.positions=null,this.currentLocation&&(this.webViewport.readingOrder.push(this.currentLocation.href),this.webViewport.progressions.set(this.currentLocation.href,e),this.currentLocation.locations?.position!==void 0&&(this.webViewport.positions=[this.currentLocation.locations.position]))}async syncLocation(e){const t=e;this.currentLocation&&(this.currentLocation=this.currentLocation.copyWithLocations({progression:t.start})),this.updateViewport(t),this.listeners.positionChanged(this.currentLocation);const i=t.fragmentId?this.currentLocation.copyWithLocations({fragments:[`#${t.fragmentId}`]}):this.currentLocation;this._visibleFragmentIds=t.visibleFragmentIds??[],this._notifyTimelineChange(i),await this.framePool.update(this.pub,this.currentLocation,this.determineModules())}goBackward(e,t){if(this._isNavigating){t(!1);return}this._isNavigating=!0,this.changeResource(-1).then(i=>{this._isNavigating=!1,t(i)})}goForward(e,t){if(this._isNavigating){t(!1);return}this._isNavigating=!0,this.changeResource(1).then(i=>{this._isNavigating=!1,t(i)})}get currentLocator(){return this.currentLocation}get viewport(){return this.webViewport}get isScrollStart(){const e=this.viewport.readingOrder[0];return this.viewport.progressions.get(e)?.start===0}get isScrollEnd(){const e=this.viewport.readingOrder[this.viewport.readingOrder.length-1];return this.viewport.progressions.get(e)?.end===1}get canGoBackward(){const e=this.pub.readingOrder.items[0]?.href;return!(this.viewport.progressions.has(e)&&this.viewport.progressions.get(e)?.start===0)}get canGoForward(){const e=this.pub.readingOrder.items[this.pub.readingOrder.items.length-1]?.href;return!(this.viewport.progressions.has(e)&&this.viewport.progressions.get(e)?.end===1)}get readingProgression(){return this.pub.metadata.effectiveReadingProgression}get publication(){return this.pub}get timeline(){const e=this.pub.timeline;return this._wrappedTimeline||(this._wrappedTimeline=new Proxy(e,{get:(t,i)=>{if(i!=="navigableFrom"){const r=Reflect.get(t,i,t);return typeof r=="function"?r.bind(t):r}return r=>{const o=this._visibleFragmentIds;let s=(o.length?t.locate(this.currentLocation.copyWithLocations({fragments:[`#${o[0]}`]})):void 0)??r;const l=(o.length?t.locate(this.currentLocation.copyWithLocations({fragments:[`#${o[o.length-1]}`]})):void 0)??r;if(this.isScrollStart){const c=t.ancestors(s).find(d=>d.references.includes(this.currentLocation.href));c&&(s=c)}const a=t.navigableFrom(s).previous,n=t.navigableFrom(l).next;return{previous:a,next:n}}}})),this._wrappedTimeline}async loadLocator(e,t){let i=!1,r=y(e.locations);if(e.text?.highlight?i=await new Promise((a,n)=>{this.framePool.currentFrames[0].msg.send("go_text",r?[e.text?.serialize(),r]:e.text?.serialize(),c=>a(c))}):r&&(i=await new Promise((a,n)=>{this.framePool.currentFrames[0].msg.send("go_text",["",r],c=>a(c))})),i){t(i);return}const o=S(e.locations);if(o&&(i=await new Promise((a,n)=>{this.framePool.currentFrames[0].msg.send("go_id",o,c=>a(c))})),i){t(i);return}const s=e?.locations?.progression;s&&s>0?i=await new Promise((a,n)=>{this.framePool.currentFrames[0].msg.send("go_progression",s,c=>a(c))}):i=!0,t(i)}completeLocator(e){if(!e.href){const t=this.pub.readingOrder.items;let i=!1;if(typeof e.locations.position=="number"){const r=t[e.locations.position-1];r&&(e=(this.pub.manifest.locatorFromLink(r)||new f({href:r.href,type:r.type||"text/html"})).copyWithLocations(e.locations),i=!0)}if(!i&&typeof e.locations?.totalProgression=="number"){const r=e.locations.totalProgression,o=Math.min(t.length-1,Math.max(0,Math.round(r*(t.length-1)))),s=t[o];s&&(e=(this.pub.manifest.locatorFromLink(s)||new f({href:s.href,type:s.type||"text/html"})).copyWithLocations(e.locations))}}return e}go(e,t,i){e=this.completeLocator(e);const r=e.href.split("#")[0];if(!this.pub.readingOrder.findWithHref(r))return i(this.listeners.handleLocator(e));const s=this.pub.readingOrder.findIndexWithHref(r);if(s>=0&&(this.currentIndex=s),this._isNavigating){i(!1);return}this._isNavigating=!0,this.currentLocation=this.createCurrentLocator(),this.apply().then(()=>this.loadLocator(e,l=>{this._isNavigating=!1,i(l)})).then(()=>{this.attachListener()})}goLink(e,t,i){return this.go(e.locator,t,i)}createCurrentLocator(){const t=this.pub.readingOrder.items[this.currentIndex];if(!t)throw new Error("No current resource available");const r=this.currentLocation&&this.currentLocation.href===t.href&&this.currentLocation.locations.progression?this.currentLocation.locations.progression:0;return this.pub.manifest.locatorFromLink(t)||new f({href:t.href,type:t.type||"text/html",locations:new k({fragments:[],progression:r,position:this.currentIndex+1})})}_notifyTimelineChange(e){const t=this.timeline.locate(e),i=!j(this._visibleFragmentIds,this._notifiedVisibleFragmentIds);(t!==this._currentTimelineItem||i)&&(this._currentTimelineItem=t,this._notifiedVisibleFragmentIds=this._visibleFragmentIds,this.listeners.timelineItemChanged(t))}}const ce=B;export{ce as ExperimentalWebPubNavigator,B as WebPubNavigator};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@readium/navigator",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Next generation SDK for publications in Web Apps",
|
|
6
6
|
"author": "readium",
|
|
@@ -44,10 +44,10 @@
|
|
|
44
44
|
"node": ">=18"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@readium/decorator": "1.2.
|
|
48
|
-
"@readium/
|
|
49
|
-
"@readium/
|
|
50
|
-
"@readium/
|
|
47
|
+
"@readium/decorator": "1.2.4",
|
|
48
|
+
"@readium/shared": "2.6.0",
|
|
49
|
+
"@readium/helpers": "1.1.1",
|
|
50
|
+
"@readium/navigator-html-injectables": "2.8.4"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@readium/css": "^2.0.5",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Link, Locator, LocatorLocations, Publication, Timeline, TimelineItem, getTime as locatorTime } from "@readium/shared";
|
|
2
2
|
import { MediaNavigator, IContentProtectionConfig, IKeyboardPeripheralsConfig, KeyboardPeripheralEventData } from "../Navigator.ts";
|
|
3
3
|
import { Configurable } from "../preferences/Configurable.ts";
|
|
4
|
-
import { WebAudioEngine, PlaybackState } from "./engine/index.ts";
|
|
4
|
+
import { WebAudioEngine, PlaybackState, AudioMseLoaderFactory } from "./engine/index.ts";
|
|
5
5
|
import {
|
|
6
6
|
AudioPreferences,
|
|
7
7
|
AudioDefaults,
|
|
@@ -69,6 +69,26 @@ export interface AudioNavigatorConfiguration {
|
|
|
69
69
|
defaults: IAudioDefaults;
|
|
70
70
|
contentProtection?: IAudioContentProtectionConfig;
|
|
71
71
|
keyboardPeripherals?: IKeyboardPeripheralsConfig;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Called with the persistent playback element before
|
|
75
|
+
* the first src is assigned, so the host can prepare it. Use for MSE/EME
|
|
76
|
+
* setups. When it returns a promise, loading of
|
|
77
|
+
* the initial track (and prefetching of adjacent ones) is deferred until
|
|
78
|
+
* the promise settles; a rejection is forwarded to the error listener and
|
|
79
|
+
* loading proceeds anyway.
|
|
80
|
+
*/
|
|
81
|
+
mediaElementSetup?: (element: HTMLMediaElement) => void | Promise<void>;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* When provided, media bytes reach the playback element through Media
|
|
85
|
+
* Source Extensions instead of direct `src` assignment: the engine
|
|
86
|
+
* creates one loader per track via this factory and the loader owns all
|
|
87
|
+
* fetching. Supply one for e.g. EME encrypted audio, since browsers have
|
|
88
|
+
* poor support for encrypted audio directly through `src`. The container
|
|
89
|
+
* handling (WebM, fMP4, …) is the loader implementation's concern.
|
|
90
|
+
*/
|
|
91
|
+
mseLoaderFactory?: AudioMseLoaderFactory;
|
|
72
92
|
}
|
|
73
93
|
|
|
74
94
|
export class AudioNavigator extends MediaNavigator implements Configurable<AudioSettings, AudioPreferences> {
|
|
@@ -94,6 +114,8 @@ export class AudioNavigator extends MediaNavigator implements Configurable<Audio
|
|
|
94
114
|
/** True while a track transition is in progress; suppresses spurious mid-navigation events. */
|
|
95
115
|
private _isNavigating: boolean = false;
|
|
96
116
|
private _isStalled: boolean = false;
|
|
117
|
+
/** Set by destroy(); stops the deferred initial load from touching a dead instance. */
|
|
118
|
+
private _destroyed: boolean = false;
|
|
97
119
|
private _stalledWatchdog: ReturnType<typeof setInterval> | null = null;
|
|
98
120
|
private _stalledCheckTime: number = 0;
|
|
99
121
|
|
|
@@ -131,9 +153,19 @@ export class AudioNavigator extends MediaNavigator implements Configurable<Audio
|
|
|
131
153
|
}
|
|
132
154
|
|
|
133
155
|
const initialHref = this.currentLocation.href.split("#")[0];
|
|
134
|
-
|
|
156
|
+
let trackIndex = this.hrefToTrackIndex(initialHref);
|
|
135
157
|
if (trackIndex === -1) {
|
|
136
|
-
|
|
158
|
+
// Progression-only locators (e.g. restored from an OPDS progression)
|
|
159
|
+
// Resolve totalProgression against the
|
|
160
|
+
// cumulative track durations instead of failing outright.
|
|
161
|
+
const totalProgression = this.currentLocation.locations?.totalProgression;
|
|
162
|
+
if (totalProgression !== undefined) {
|
|
163
|
+
const resolved = this.locatorFromTotalProgression(totalProgression);
|
|
164
|
+
this.currentLocation = resolved.locator;
|
|
165
|
+
trackIndex = resolved.trackIndex;
|
|
166
|
+
} else {
|
|
167
|
+
throw new Error(`AudioNavigator: initial href "${ initialHref }" not found in reading order`);
|
|
168
|
+
}
|
|
137
169
|
}
|
|
138
170
|
const initialTime = locatorTime(this.currentLocation.locations) || 0;
|
|
139
171
|
|
|
@@ -145,7 +177,8 @@ export class AudioNavigator extends MediaNavigator implements Configurable<Audio
|
|
|
145
177
|
} as PlaybackState,
|
|
146
178
|
playWhenReady: false,
|
|
147
179
|
index: trackIndex
|
|
148
|
-
}
|
|
180
|
+
},
|
|
181
|
+
mseLoaderFactory: configuration.mseLoaderFactory,
|
|
149
182
|
});
|
|
150
183
|
|
|
151
184
|
this.pool = new AudioPoolManager(audioEngine, publication, configuration.contentProtection);
|
|
@@ -188,6 +221,29 @@ export class AudioNavigator extends MediaNavigator implements Configurable<Audio
|
|
|
188
221
|
this.setupEventListeners();
|
|
189
222
|
|
|
190
223
|
this._isNavigating = true;
|
|
224
|
+
|
|
225
|
+
const startInitialLoad = () => this.startInitialLoad(trackIndex, initialTime);
|
|
226
|
+
if (configuration.mediaElementSetup) {
|
|
227
|
+
// Defer the initial load until the host has prepared the element
|
|
228
|
+
// (e.g. attached MediaKeys for EME) so no media data is fetched or
|
|
229
|
+
// decoded before protection is in place.
|
|
230
|
+
if (this._destroyed) return;
|
|
231
|
+
Promise.resolve()
|
|
232
|
+
.then(() => configuration.mediaElementSetup!(this.pool.audioEngine.getMediaElement()))
|
|
233
|
+
.catch((error) => { this.listeners.error(error, this.currentLocator); })
|
|
234
|
+
.then(startInitialLoad);
|
|
235
|
+
} else {
|
|
236
|
+
startInitialLoad();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** Sets the initial track on the primary element and seeks to the starting position. */
|
|
241
|
+
private startInitialLoad(trackIndex: number, initialTime: number): void {
|
|
242
|
+
// The navigator may have been destroyed while an async
|
|
243
|
+
// mediaElementSetup was pending (React StrictMode does this in dev) —
|
|
244
|
+
// don't start loading media on the orphaned element.
|
|
245
|
+
if (this._destroyed) return;
|
|
246
|
+
|
|
191
247
|
this.pool.setCurrentAudio(trackIndex, "forward");
|
|
192
248
|
|
|
193
249
|
// applyPreferences() must come after setCurrentAudio() so that the src
|
|
@@ -289,6 +345,47 @@ export class AudioNavigator extends MediaNavigator implements Configurable<Audio
|
|
|
289
345
|
return this.hrefToTrackIndex(this.currentLocation.href);
|
|
290
346
|
}
|
|
291
347
|
|
|
348
|
+
/**
|
|
349
|
+
* Builds a locator for an overall publication progression (0–1) by walking
|
|
350
|
+
* the cumulative track durations. Falls back to the start of the first
|
|
351
|
+
* track when the reading order carries no duration metadata.
|
|
352
|
+
*/
|
|
353
|
+
private locatorFromTotalProgression(totalProgression: number): { locator: Locator, trackIndex: number } {
|
|
354
|
+
const items = this.pub.readingOrder.items;
|
|
355
|
+
const durations = items.map(link => link.duration ?? 0);
|
|
356
|
+
const totalDuration = durations.reduce((sum, d) => sum + d, 0);
|
|
357
|
+
|
|
358
|
+
let trackIndex = 0;
|
|
359
|
+
let time = 0;
|
|
360
|
+
if (totalDuration > 0) {
|
|
361
|
+
let remaining = Math.min(Math.max(totalProgression, 0), 1) * totalDuration;
|
|
362
|
+
for (let i = 0; i < durations.length; i++) {
|
|
363
|
+
trackIndex = i;
|
|
364
|
+
if (remaining <= durations[i] || i === durations.length - 1) {
|
|
365
|
+
time = remaining;
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
remaining -= durations[i];
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const link = items[trackIndex];
|
|
373
|
+
return {
|
|
374
|
+
trackIndex,
|
|
375
|
+
locator: new Locator({
|
|
376
|
+
href: link.href,
|
|
377
|
+
type: link.type || "", // Should have a mimetype
|
|
378
|
+
title: link.title,
|
|
379
|
+
locations: new LocatorLocations({
|
|
380
|
+
position: trackIndex + 1,
|
|
381
|
+
progression: durations[trackIndex] > 0 ? time / durations[trackIndex] : 0,
|
|
382
|
+
totalProgression: Math.min(Math.max(totalProgression, 0), 1),
|
|
383
|
+
fragments: [`t=${ time }`]
|
|
384
|
+
})
|
|
385
|
+
})
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
292
389
|
get currentLocator(): Locator {
|
|
293
390
|
return this.currentLocation;
|
|
294
391
|
}
|
|
@@ -384,7 +481,11 @@ export class AudioNavigator extends MediaNavigator implements Configurable<Audio
|
|
|
384
481
|
fragments: [`t=${this.duration}`]
|
|
385
482
|
}));
|
|
386
483
|
this.listeners.trackEnded(this.currentLocator);
|
|
387
|
-
if (!this.canGoForward)
|
|
484
|
+
if (!this.canGoForward) {
|
|
485
|
+
// Set final progress in audiobook
|
|
486
|
+
this.listeners.positionChanged(this.currentLocator);
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
388
489
|
await this.nextTrack();
|
|
389
490
|
if (this._settings.autoPlay) this.play();
|
|
390
491
|
});
|
|
@@ -698,6 +799,7 @@ export class AudioNavigator extends MediaNavigator implements Configurable<Audio
|
|
|
698
799
|
}
|
|
699
800
|
|
|
700
801
|
destroy(): void {
|
|
802
|
+
this._destroyed = true;
|
|
701
803
|
this.stopPositionPolling();
|
|
702
804
|
this._stopStalledWatchdog();
|
|
703
805
|
this.destroyMediaSession();
|