@readium/navigator 2.9.1 → 2.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import{Layout as d,Profile as E,Link as _,ReadingProgression as g,Locator as v,LocatorText as D,getCssSelector as O,getHtmlId as I}from"@readium/shared";import{VisualNavigator as F}from"../Navigator.js";import{FramePoolManager as A}from"./frame/FramePoolManager.js";import{FXLFramePoolManager as M}from"./fxl/FXLFramePoolManager.js";import{ModuleLibrary as T,FXLModules as H,ReflowableModules as R}from"@readium/navigator-html-injectables";import{p as y}from"../_virtual/index.js";import{FrameManager as V}from"./frame/FrameManager.js";import{EpubPreferences as N}from"./preferences/EpubPreferences.js";import{EpubDefaults as W}from"./preferences/EpubDefaults.js";import{EpubPreferencesEditor as b}from"./preferences/EpubPreferencesEditor.js";import{ReadiumCSS as j}from"./css/ReadiumCSS.js";import{UserProperties as z,RSProperties as B}from"./css/Properties.js";import{getContentWidth as K}from"../helpers/dimensions.js";import{Injector as $}from"../injection/Injector.js";import{createReadiumEpubRules as G}from"../injection/epubInjectables.js";import{NavigatorProtector as X,NAVIGATOR_SUSPICIOUS_ACTIVITY_EVENT as x}from"../protection/NavigatorProtector.js";import{KeyboardPeripherals as J,NAVIGATOR_KEYBOARD_PERIPHERAL_EVENT as C}from"../peripherals/KeyboardPeripherals.js";import{getScriptMode as w}from"../helpers/scriptMode.js";import{EpubSettings as S}from"./preferences/EpubSettings.js";import{LineLengths as U}from"../helpers/lineLength.js";import{supportsDecorationStyle as q,decorationsEqual as Y,resolveDecorationForWire as P}from"@readium/decorator";const Q=p=>({frameLoaded:p.frameLoaded||(()=>{}),positionChanged:p.positionChanged||(()=>{}),timelineItemChanged:p.timelineItemChanged||(()=>{}),tap:p.tap||(()=>!1),click:p.click||(()=>!1),zoom:p.zoom||(()=>{}),miscPointer:p.miscPointer||(()=>{}),scroll:p.scroll||(()=>{}),customEvent:p.customEvent||(()=>{}),handleLocator:p.handleLocator||(()=>!1),textSelected:p.textSelected||(()=>{}),contentProtection:p.contentProtection||(()=>{}),contextMenu:p.contextMenu||(()=>{}),peripheral:p.peripheral||(()=>{})});function Z(p,e){return p.length===e.length&&p.every((t,i)=>t===e[i])}class k extends F{constructor(e,t,i,s=[],r=void 0,o={preferences:{},defaults:{}}){super(),this._visibleFragmentIds=[],this._notifiedVisibleFragmentIds=[],this._timelineAugmented=!1,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.reflowViewport={readingOrder:[],progressions:new Map,positions:null},this.pub=t,this.container=e,this.listeners=Q(i),this.currentLocation=r,s.length&&(this.positions=s),this._preferences=new N(o.preferences),this._defaults=new W(o.defaults),this._settings=new S(this._preferences,this._defaults);const h=w(t.metadata),f=h==="cjk-horizontal",a=h==="cjk-vertical",c=a||h==="mongolian-vertical",l=f||a;this._css=new j({rsProperties:new B({noVerticalPagination:c||void 0}),userProperties:new z({}),lineLengths:new U({optimalChars:this._settings.optimalLineLength,minChars:this._settings.minimalLineLength,maxChars:this._settings.maximalLineLength,padding:this._settings.scroll?(this._settings.scrollPaddingLeft||0)+(this._settings.scrollPaddingRight||0):(this._settings.pageGutter||0)*2,fontFace:this._settings.fontFamily,letterSpacing:this._settings.letterSpacing,wordSpacing:this._settings.wordSpacing,isCJK:l}),container:e,constraint:this._settings.constraint,isCJKVertical:c}),this._layout=k.determineLayout(t,!!this._settings.scroll),this.currentProgression=t.metadata.effectiveReadingProgression,this._injectablesConfig=o.injectables||{rules:[],allowedDomains:[]},this._readiumRulesPromise=G(t.metadata,t.readingOrder.items),this._contentProtection=o.contentProtection||{},this._decoratorConfig=o.decoratorConfig||{},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 X(this._contentProtection),this._suspiciousActivityListener=u=>{const{type:m,...L}=u.detail;m==="context_menu"?this.listeners.contextMenu(L):this.listeners.contentProtection(m,L)},window.addEventListener(x,this._suspiciousActivityListener)),this._keyboardPeripherals.length>0&&(this._keyboardPeripheralsManager=new J({keyboardPeripherals:this._keyboardPeripherals}),this._keyboardPeripheralListener=u=>{const m=u.detail;this.listeners.peripheral(m)},window.addEventListener(C,this._keyboardPeripheralListener)),this.resizeObserver=new ResizeObserver(()=>this.ownerWindow.requestAnimationFrame(async()=>await this.resizeHandler())),this.resizeObserver.observe(this.container.parentElement||document.documentElement)}static determineLayout(e,t){const i=e.metadata.effectiveLayout;if(i===d.fixed)return d.fixed;if(e.metadata.otherMetadata&&"http://openmangaformat.org/schema/1.0#version"in e.metadata.otherMetadata)return d.fixed;if(e.metadata?.conformsTo?.includes(E.DIVINA))return d.fixed;if(i===d.scrolled)return d.scrolled;const s=w(e.metadata);return s==="cjk-vertical"||s==="mongolian-vertical"?d.scrolled:i===d.reflowable&&t?d.scrolled:d.reflowable}async load(){if(this.positions?.length||(this.positions=await this.pub.positionsFromManifest()),!this._injector){const e=await this._readiumRulesPromise;this._injector=new $({rules:[...e,...this._injectablesConfig.rules],allowedDomains:this._injectablesConfig.allowedDomains})}if(this._layout===d.fixed)this.framePool=new M(this.container,this.positions,this.pub,this._injector,this._contentProtection,this._keyboardPeripherals),this.framePool.listener=(e,t)=>{this.eventListener(e,t)};else{await this.updateCSS(!1);const e=this.compileCSSProperties(this._css);this.framePool=new A(this.container,this.positions,e,this._injector,this._contentProtection,this._keyboardPeripherals,t=>this.pub.timeline.segmentsForHref(t).flatMap(i=>i.references).map(i=>{const s=i.indexOf("#");return s>=0?i.slice(s+1):""}).filter(Boolean))}this.currentLocation===void 0&&(this.currentLocation=this.positions[0]),await this.resizeHandler(),await this.apply()}get settings(){if(this._layout===d.fixed)return Object.freeze({...this._settings});{const e=this._css.userProperties.colCount||this._css.rsProperties.colCount||this._settings.columnCount;return Object.freeze({...this._settings,columnCount:e})}}get preferencesEditor(){return this._preferencesEditor===null&&(this._preferencesEditor=new b(this._preferences,this.settings,this.pub.metadata)),this._preferencesEditor}async submitPreferences(e){this._preferences=this._preferences.merging(e),await this.applyPreferences()}async applyPreferences(){const e=this._settings;this._settings=new S(this._preferences,this._defaults),this._preferencesEditor!==null&&(this._preferencesEditor=new b(this._preferences,this.settings,this.pub.metadata)),this._layout===d.fixed?this.handleFXLPrefs(e,this._settings):await this.updateCSS(!0)}handleFXLPrefs(e,t){e.columnCount!==t.columnCount&&this.framePool.setPerPage(t.columnCount)}async updateCSS(e){this._css.update(this._settings),e&&await this.commitCSS(this._css)}compileCSSProperties(e){const t={};for(const[i,s]of Object.entries(e.rsProperties.toCSSProperties()))t[i]=s;for(const[i,s]of Object.entries(e.userProperties.toCSSProperties()))t[i]=s;return t}async commitCSS(e){if(!this.framePool)return;const t=this.compileCSSProperties(e);this.framePool.setCSSProperties(t),this._css.userProperties.view==="paged"&&this._layout===d.scrolled?await this.setLayout(d.reflowable):this._css.userProperties.view==="scroll"&&this._layout===d.reflowable&&await this.setLayout(d.scrolled),this._css.setContainerWidth()}async resizeHandler(){const e=this.container.parentElement||document.documentElement;if(this._layout===d.fixed){if(this.container.style.width=`${K(e)-this._settings.constraint}px`,!this.framePool)return;this.framePool.resizeHandler()}else{const t=this._css.userProperties.colCount,i=this._css.userProperties.lineLength;this._css.resizeHandler(),(this._css.userProperties.view!=="scroll"&&t!==this._css.userProperties.colCount||i!==this._css.userProperties.lineLength)&&await this.commitCSS(this._css)}}get layout(){return this._layout}get ownerWindow(){return this.container.ownerDocument.defaultView||window}get _cframes(){return(this.framePool?.currentFrames??[]).filter(e=>!(e instanceof V&&e.isDestroyed))}get pool(){return this.framePool}eventListener(e,t,i){switch(e){case"_pong":if(this.listeners.frameLoaded(this._cframes[0].iframe.contentWindow),this.listeners.positionChanged(this.currentLocation),this._notifyTimelineChange(this.currentLocation),i){const n=this._cframes.filter(l=>!!l).indexOf(i),c=n>=0?this.viewport.readingOrder[n]:void 0;c&&this._reapplyDecorationsToFrame(i,c)}else this._reapplyDecorationsToCurrentFrames();break;case"first_visible_locator":const s=v.deserialize(t);if(!s)break;this.currentLocation=new v({href:this.currentLocation.href,type:this.currentLocation.type,title:this.currentLocation.title,locations:s?.locations,text:s?.text}),this.listeners.positionChanged(this.currentLocation),this._notifyTimelineChange(this.currentLocation);break;case"text_selected":{const a=t;if(i){const c=this._cframes.filter(u=>!!u).indexOf(i),l=c>=0?this.viewport.readingOrder[c]:void 0;if(l){const u=this.pub.readingOrder.findWithHref(l);a.locator=new v({href:l,type:u.type||"application/xhtml+xml",text:new D({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("http://")||n.startsWith("https://")||n.startsWith("mailto:")||n.startsWith("tel:"))this.listeners.handleLocator(new _({href:n}).locator);else try{this.goLink(new _({href:y.join(y.dirname(this.currentLocation.href),n)}),!1,()=>{})}catch(c){console.warn(`Couldn't go to link for ${n}: ${c}`),this.listeners.handleLocator(new _({href:n}).locator)}}else console.log("Clicked on",a)}else{if(this._layout===d.fixed&&this.framePool.doNotDisturb&&(r.doNotDisturb=!0),this._layout===d.fixed&&(this.currentProgression===g.rtl||this.currentProgression===g.ltr)&&this.framePool.currentFrames.length>1){const c=this.framePool.currentFrames;r.targetFrameSrc===c[this.currentProgression===g.rtl?0:1]?.source&&(r.x+=(c[this.currentProgression===g.rtl?1:0]?.iframe.contentWindow?.innerWidth??0)*window.devicePixelRatio)}if(e==="click"?this.listeners.click(r):this.listeners.tap(r))break;const n=(this._cframes.length===2?this._cframes[0].window.innerWidth+this._cframes[1].window.innerWidth:this._cframes[0].window.innerWidth)*window.devicePixelRatio/4;r.x>=n&&r.x<=n*3&&this.listeners.miscPointer(1),r.x<n?this.goLeft(!1,()=>{}):r.x>n*3&&this.goRight(!1,()=>{})}break;case"tap_more":this.listeners.miscPointer(t);break;case"no_more":this.changeResource(1);break;case"no_less":this.changeResource(-1);break;case"swipe":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 h=t,f={...h,interactiveElement:void 0};h.interactiveElement&&(f.interactiveElement=new DOMParser().parseFromString(h.interactiveElement,"text/html").body.children[0]),this.listeners.peripheral(f);break;case"log":console.log(this._cframes[0]?.source?.split("/")[3],...t);break;default:this.listeners.customEvent(e,t);break}}determineModules(){let e=Array.from(T.keys());if(this._layout===d.fixed)return e.filter(s=>H.includes(s));e=e.filter(s=>R.includes(s));const t=w(this.pub.metadata);if(t==="cjk-vertical"||t==="mongolian-vertical")return e.filter(s=>s!=="column_snapper"&&s!=="scroll_snapper");const i=e;return this._layout===d.scrolled?e=i.filter(s=>s!=="column_snapper"&&s!=="cjk_vertical_snapper"):e=i.filter(s=>s!=="scroll_snapper"&&s!=="cjk_vertical_snapper"),e}attachListener(){const e=this._cframes.filter(t=>!!t);if(e.length===0)throw Error("no cframe to attach listener to");e.forEach(t=>{t.msg&&(t.msg.listener=(i,s)=>{this.eventListener(i,s,t)})}),this._reapplyDecorationsToCurrentFrames()}async apply(){if(await this.framePool.update(this.pub,this.currentLocator,this.determineModules()),this.attachListener(),this.pub.readingOrder.findIndexWithHref(this.currentLocation.href)<0)throw Error("Link for "+this.currentLocation.href+" not found!")}supportsDecorationStyle(e){return q(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._sendDecorationActivationToFrames(e,!0)),(t.onDecorationPointerEnter||t.onDecorationPointerLeave)&&(this._decorationHoverState.set(e,!0),this._sendDecorationHoverToFrames(e,!0))}unregisterDecorationObserver(e){this._decorationObservers.forEach((t,i)=>{if(!t.has(e))return;t.delete(e);const s=[...t].some(o=>o.onDecorationActivated);this._decorationActivationState.has(i)&&!s&&(this._decorationActivationState.delete(i),this._sendDecorationActivationToFrames(i,!1));const r=[...t].some(o=>o.onDecorationPointerEnter||o.onDecorationPointerLeave);this._decorationHoverState.has(i)&&!r&&(this._decorationHoverState.delete(i),this._sendDecorationHoverToFrames(i,!1))})}_sendDecorationActivationToFrames(e,t){this._cframes.filter(s=>!!s).forEach(s=>{s.msg&&s.msg.send("decoration_activatable",{group:e,activatable:t})})}_sendDecorationHoverToFrames(e,t){this._cframes.filter(s=>!!s).forEach(s=>{s.msg&&s.msg.send("decoration_hoverable",{group:e,hoverable:t})})}applyDecorations(e,t){const i=this._decorations.get(t)??[],s=new Map(i.map(c=>[c.id,c])),r=new Map(e.map(c=>[c.id,c])),o=[],h=[],f=[];for(const[c,l]of s)r.has(c)?Y(l,r.get(c))||f.push(r.get(c)):o.push(c);for(const[c,l]of r)s.has(c)||h.push(l);this._decorations.set(t,e),this._sendDecorationOps(t,o,h,f,i);const a=this._decorationActivationState.get(t);a!==void 0&&this._sendDecorationActivationToFrames(t,a);const n=this._decorationHoverState.get(t);n!==void 0&&this._sendDecorationHoverToFrames(t,n)}_sendDecorationOps(e,t,i,s,r){const o=this._cframes.filter(a=>!!a),h=new Map(r.map(a=>[a.id,a])),f=this.viewport.readingOrder;o.forEach((a,n)=>{if(!a.msg)return;const c=f[n];if(c){for(const l of t){const u=h.get(l);!u||u.locator.href!==c||a.msg.send("decorate",{group:e,action:"remove",decoration:{id:l}})}for(const l of i)l.locator.href===c&&a.msg.send("decorate",{group:e,action:"add",decoration:P(l,this._decoratorConfig.decorationTemplates)});for(const l of s)l.locator.href===c&&a.msg.send("decorate",{group:e,action:"update",decoration:P(l,this._decoratorConfig.decorationTemplates)})}})}_reapplyDecorationsToFrame(e,t){if(e.msg){for(const[i,s]of this._decorations){const r=s.filter(o=>o.locator.href===t);if(r.length!==0){e.msg.send("decorate",{group:i,action:"clear"});for(const o of r)e.msg.send("decorate",{group:i,action:"add",decoration:P(o,this._decoratorConfig.decorationTemplates)})}}for(const[i,s]of this._decorationActivationState)e.msg.send("decoration_activatable",{group:i,activatable:s});for(const[i,s]of this._decorationHoverState)e.msg.send("decoration_hoverable",{group:i,hoverable:s})}}_reapplyDecorationsToCurrentFrames(){const e=this._cframes.filter(i=>!!i),t=this.viewport.readingOrder;e.forEach((i,s)=>{const r=t[s];r&&this._reapplyDecorationsToFrame(i,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(o=>o.id===e.decorationId);if(!i)return!1;const s={decoration:i,group:e.group,rect:e.rect,point:e.point};let r=!1;for(const o of t)o.onDecorationActivated?.(s)&&(r=!0);return r}_handleDecorationPointerEnter(e){const t=this._decorationObservers.get(e.group);if(!t||t.size===0)return;const i=(this._decorations.get(e.group)??[]).find(r=>r.id===e.decorationId);if(!i)return;this._decorationHoveredDecorations.set(e.group,i);const s={decoration:i,group:e.group,rect:e.rect,point:e.point};for(const r of t)r.onDecorationPointerEnter?.(s)}_handleDecorationPointerLeave(e){const t=this._decorationObservers.get(e.group);if(!t||t.size===0)return;const i=(this._decorations.get(e.group)??[]).find(r=>r.id===e.decorationId)??this._decorationHoveredDecorations.get(e.group);if(this._decorationHoveredDecorations.delete(e.group),!i)return;const s={decoration:i,group:e.group,rect:e.rect,point:e.point};for(const r of t)r.onDecorationPointerLeave?.(s)}async destroy(){this._suspiciousActivityListener&&window.removeEventListener(x,this._suspiciousActivityListener),this._keyboardPeripheralListener&&window.removeEventListener(C,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()}async changeResource(e){if(e===0)return!1;if(this._layout===d.fixed){const s=this.framePool,r=s.viewport.positions[0];if(e===1){if(!s.next(s.perPage))return!1}else if(e===-1){if(!s.prev(s.perPage))return!1}else throw Error("Invalid relative value for FXL");const o=s.viewport.positions[0];if(r>o){for(let h=this.positions.length-1;h>=0;h--)if(this.positions[h].href===this.pub.readingOrder.items[o-1].href){this.currentLocation=this.positions[h].copyWithLocations({progression:.999999999999});break}}else if(r<o){for(let h=0;h<this.positions.length;h++)if(this.positions[h].href===this.pub.readingOrder.items[o-1].href){this.currentLocation=this.positions[h];break}}return await this.apply(),this.listeners.positionChanged(this.currentLocation),this._notifyTimelineChange(this.currentLocation),!0}const t=this.pub.readingOrder.findIndexWithHref(this.currentLocation.href),i=Math.max(0,Math.min(this.pub.readingOrder.items.length-1,t+e));if(i===t)return this._cframes[0]?.msg?.send("shake",void 0,async s=>{}),!1;if(t>i){for(let s=this.positions.length-1;s>=0;s--)if(this.positions[s].href===this.pub.readingOrder.items[i].href){this.currentLocation=this.positions[s].copyWithLocations({progression:.999999999999});break}}else for(let s=0;s<this.positions.length;s++)if(this.positions[s].href===this.pub.readingOrder.items[i].href){this.currentLocation=this.positions[s];break}return await this.apply(),!0}findLastPositionInProgressionRange(e,t){const i=e.findLastIndex(s=>{const r=s.locations.progression;return!!(r&&r>t.start&&r<=t.end)});return i!==-1?e[i]:void 0}findNearestPositions(e){const t=this.positions.filter(o=>o.href===this.currentLocation.href);let i=this.currentLocation,s;const r=t.findLastIndex(o=>(o.locations.progression??0)<=e.start);if(r!==-1){i=t[r];const o=t.slice(r+1);s=this.findLastPositionInProgressionRange(o,e)}return{first:i,last:s}}updateViewport(e){this.reflowViewport.readingOrder=[],this.reflowViewport.progressions.clear(),this.reflowViewport.positions=null,this.currentLocation&&(this.reflowViewport.readingOrder.push(this.currentLocation.href),this.reflowViewport.progressions.set(this.currentLocation.href,e),this.currentLocation.locations?.position!==void 0&&(this.reflowViewport.positions=[this.currentLocation.locations.position],this.lastLocationInView?.locations?.position!==void 0&&this.reflowViewport.positions.push(this.lastLocationInView.locations.position)))}async syncLocation(e){const t=e,i=this.findNearestPositions(t);this.currentLocation=i.first.copyWithLocations({progression:t.start}),this.lastLocationInView=i.last,this.updateViewport(t),this.listeners.positionChanged(this.currentLocation);const s=t.fragmentId?this.currentLocation.copyWithLocations({fragments:[`#${t.fragmentId}`]}):this.currentLocation;this._visibleFragmentIds=t.visibleFragmentIds??[],this._notifyTimelineChange(s),await this.framePool.update(this.pub,this.currentLocation,this.determineModules())}goBackward(e,t){if(this._isNavigating){t(!1);return}this._isNavigating=!0,this._layout===d.fixed?this.changeResource(-1).then(i=>{this._isNavigating=!1,t(i)}):this._cframes[0]?.msg?.send("go_prev",void 0,async i=>{if(i)this._isNavigating=!1,t(!0);else{const s=await this.changeResource(-1);this._isNavigating=!1,t(s)}})}goForward(e,t){if(this._isNavigating){t(!1);return}this._isNavigating=!0,this._layout===d.fixed?this.changeResource(1).then(i=>{this._isNavigating=!1,t(i)}):this._cframes[0]?.msg?.send("go_next",void 0,async i=>{if(i)this._isNavigating=!1,t(!0);else{const s=await this.changeResource(1);this._isNavigating=!1,t(s)}})}get currentLocator(){return this.currentLocation}get viewport(){return this._layout===d.fixed?this.framePool?this.framePool.viewport:{readingOrder:[],progressions:new Map,positions:null}:this.reflowViewport}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.currentProgression}async setLayout(e){this._layout!==e&&(this._layout=e,await this.framePool.update(this.pub,this.currentLocator,this.determineModules(),!0),this.attachListener())}get publication(){return this.pub}get timeline(){const e=this.pub.timeline;if(!this._timelineAugmented&&this.positions?.length){const t=this.positions,i=new Set;e.augment((s,r)=>{if(s.children?.length)return{};const o=r.href.indexOf("#"),h=o>=0?r.href.slice(0,o):r.href,f=o>=0?r.href.slice(o+1):void 0,a=t.filter(l=>l.href===h);if(!a.length)return{};const n=f?a.find(l=>l.locations.fragments[0]===f):void 0;let c;return n?c=n:i.has(h)?c=void 0:(i.add(h),c=a.reduce((l,u)=>(u.locations.position??1/0)<(l.locations.position??1/0)?u:l)),c?{position:c.locations.position,scroll:n?.locations.progression}:{}}),this._timelineAugmented=!0}return this._wrappedTimeline||(this._wrappedTimeline=new Proxy(e,{get:(t,i)=>{if(i!=="navigableFrom"){const s=Reflect.get(t,i,t);return typeof s=="function"?s.bind(t):s}return s=>{const r=this._visibleFragmentIds;let o=(r.length?t.locate(this.currentLocation.copyWithLocations({fragments:[`#${r[0]}`]})):void 0)??s;const h=(r.length?t.locate(this.currentLocation.copyWithLocations({fragments:[`#${r[r.length-1]}`]})):void 0)??s;if(this.isScrollStart){const n=t.ancestors(o).find(c=>c.references.includes(this.currentLocation.href));n&&(o=n)}const f=t.navigableFrom(o).previous,a=t.navigableFrom(h).next;return{previous:f,next:a}}}})),this._wrappedTimeline}async loadLocator(e,t){let i=!1,s=O(e.locations);if(e.text?.highlight?i=await new Promise((f,a)=>{this._cframes[0].msg.send("go_text",s?[e.text?.serialize(),s]:e.text?.serialize(),n=>f(n))}):s&&(i=await new Promise((f,a)=>{this._cframes[0].msg.send("go_text",["",s],n=>f(n))})),i){t(i);return}const r=I(e.locations);if(r&&(i=await new Promise((f,a)=>{this._cframes[0].msg.send("go_id",r,n=>f(n))})),i){t(i);return}const o=e?.locations?.progression;o&&o>0?i=await new Promise((f,a)=>{this._cframes[0].msg.send("go_progression",o,n=>f(n))}):i=!0,t(i)}go(e,t,i){const s=e.href.split("#")[0];let r=this.pub.readingOrder.findWithHref(s);if(!r)return i(this.listeners.handleLocator(e));if(this._isNavigating){i(!1);return}this._isNavigating=!0,this.currentLocation=this.positions.find(o=>o.href===r.href),this.apply().then(()=>this.loadLocator(e,o=>{this._isNavigating=!1,i(o)})).then(()=>{this.attachListener()})}goLink(e,t,i){return this.go(e.locator,t,i)}_notifyTimelineChange(e){const t=this.timeline.locate(e),i=!Z(this._visibleFragmentIds,this._notifiedVisibleFragmentIds);(t!==this._currentTimelineItem||i)&&(this._currentTimelineItem=t,this._notifiedVisibleFragmentIds=this._visibleFragmentIds,this.listeners.timelineItemChanged(t))}}export{k as EpubNavigator};
1
+ import{Layout as d,Profile as E,Link as _,ReadingProgression as g,Locator as v,LocatorText as D,getCssSelector as O,getHtmlId as I}from"@readium/shared";import{VisualNavigator as F}from"../Navigator.js";import{FramePoolManager as T}from"./frame/FramePoolManager.js";import{FXLFramePoolManager as A}from"./fxl/FXLFramePoolManager.js";import{ModuleLibrary as R,FXLModules as M,ReflowableModules as H}from"@readium/navigator-html-injectables";import{p as y}from"../_virtual/index.js";import{FrameManager as z}from"./frame/FrameManager.js";import{EpubPreferences as V}from"./preferences/EpubPreferences.js";import{EpubDefaults as N}from"./preferences/EpubDefaults.js";import{EpubPreferencesEditor as b}from"./preferences/EpubPreferencesEditor.js";import{ReadiumCSS as W}from"./css/ReadiumCSS.js";import{UserProperties as j,RSProperties as B}from"./css/Properties.js";import{getContentWidth as K}from"../helpers/dimensions.js";import{Injector as $}from"../injection/Injector.js";import{createReadiumEpubRules as G}from"../injection/epubInjectables.js";import{NavigatorProtector as X,NAVIGATOR_SUSPICIOUS_ACTIVITY_EVENT as S}from"../protection/NavigatorProtector.js";import{KeyboardPeripherals as J,NAVIGATOR_KEYBOARD_PERIPHERAL_EVENT as x}from"../peripherals/KeyboardPeripherals.js";import{getScriptMode as w}from"../helpers/scriptMode.js";import{EpubSettings as C}from"./preferences/EpubSettings.js";import{LineLengths as U}from"../helpers/lineLength.js";import{supportsDecorationStyle as q,decorationsEqual as Y,resolveDecorationForWire as P}from"@readium/decorator";const Q=p=>({frameLoaded:p.frameLoaded||(()=>{}),positionChanged:p.positionChanged||(()=>{}),timelineItemChanged:p.timelineItemChanged||(()=>{}),tap:p.tap||(()=>!1),click:p.click||(()=>!1),zoom:p.zoom||(()=>{}),miscPointer:p.miscPointer||(()=>{}),scroll:p.scroll||(()=>{}),customEvent:p.customEvent||(()=>{}),handleLocator:p.handleLocator||(()=>!1),textSelected:p.textSelected||(()=>{}),contentProtection:p.contentProtection||(()=>{}),contextMenu:p.contextMenu||(()=>{}),peripheral:p.peripheral||(()=>{})});function Z(p,e){return p.length===e.length&&p.every((t,i)=>t===e[i])}class k extends F{constructor(e,t,i,s=[],r=void 0,o={preferences:{},defaults:{}}){super(),this._visibleFragmentIds=[],this._notifiedVisibleFragmentIds=[],this._timelineAugmented=!1,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.reflowViewport={readingOrder:[],progressions:new Map,positions:null},this.pub=t,this.container=e,this.listeners=Q(i),this.currentLocation=r,s.length&&(this.positions=s),this._preferences=new V(o.preferences),this._defaults=new N(o.defaults),this._settings=new C(this._preferences,this._defaults);const h=w(t.metadata),f=h==="cjk-horizontal",a=h==="cjk-vertical",c=a||h==="mongolian-vertical",l=f||a;this._css=new W({rsProperties:new B({noVerticalPagination:c||void 0}),userProperties:new j({}),lineLengths:new U({optimalChars:this._settings.optimalLineLength,minChars:this._settings.minimalLineLength,maxChars:this._settings.maximalLineLength,padding:this._settings.scroll?(this._settings.scrollPaddingLeft||0)+(this._settings.scrollPaddingRight||0):(this._settings.pageGutter||0)*2,fontFace:this._settings.fontFamily,letterSpacing:this._settings.letterSpacing,wordSpacing:this._settings.wordSpacing,isCJK:l}),container:e,constraint:this._settings.constraint,isCJKVertical:c}),this._layout=k.determineLayout(t,!!this._settings.scroll),this.currentProgression=t.metadata.effectiveReadingProgression,this._injectablesConfig=o.injectables||{rules:[],allowedDomains:[]},this._readiumRulesPromise=G(t.metadata,t.readingOrder.items),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 X(this._contentProtection),this._suspiciousActivityListener=u=>{const{type:m,...L}=u.detail;m==="context_menu"?this.listeners.contextMenu(L):this.listeners.contentProtection(m,L)},window.addEventListener(S,this._suspiciousActivityListener)),this._keyboardPeripherals.length>0&&(this._keyboardPeripheralsManager=new J({keyboardPeripherals:this._keyboardPeripherals}),this._keyboardPeripheralListener=u=>{const m=u.detail;this.listeners.peripheral(m)},window.addEventListener(x,this._keyboardPeripheralListener)),this.resizeObserver=new ResizeObserver(()=>this.ownerWindow.requestAnimationFrame(async()=>await this.resizeHandler())),this.resizeObserver.observe(this.container.parentElement||document.documentElement)}static determineLayout(e,t){const i=e.metadata.effectiveLayout;if(i===d.fixed)return d.fixed;if(e.metadata.otherMetadata&&"http://openmangaformat.org/schema/1.0#version"in e.metadata.otherMetadata)return d.fixed;if(e.metadata?.conformsTo?.includes(E.DIVINA))return d.fixed;if(i===d.scrolled)return d.scrolled;const s=w(e.metadata);return s==="cjk-vertical"||s==="mongolian-vertical"?d.scrolled:i===d.reflowable&&t?d.scrolled:d.reflowable}async load(){if(this.positions?.length||(this.positions=await this.pub.positionsFromManifest()),!this._injector){const e=await this._readiumRulesPromise;this._injector=new $({rules:[...e,...this._injectablesConfig.rules],allowedDomains:this._injectablesConfig.allowedDomains})}if(this._layout===d.fixed)this.framePool=new A(this.container,this.positions,this.pub,this._injector,this._contentProtection,this._keyboardPeripherals,[...this._decorationResizeSelectors]),this.framePool.listener=(e,t)=>{this.eventListener(e,t)};else{await this.updateCSS(!1);const e=this.compileCSSProperties(this._css);this.framePool=new T(this.container,this.positions,e,this._injector,this._contentProtection,this._keyboardPeripherals,t=>this.pub.timeline.segmentsForHref(t).flatMap(i=>i.references).map(i=>{const s=i.indexOf("#");return s>=0?i.slice(s+1):""}).filter(Boolean),[...this._decorationResizeSelectors])}this.currentLocation===void 0&&(this.currentLocation=this.positions[0]),await this.resizeHandler(),await this.apply()}get settings(){if(this._layout===d.fixed)return Object.freeze({...this._settings});{const e=this._css.userProperties.colCount||this._css.rsProperties.colCount||this._settings.columnCount;return Object.freeze({...this._settings,columnCount:e})}}get preferencesEditor(){return this._preferencesEditor===null&&(this._preferencesEditor=new b(this._preferences,this.settings,this.pub.metadata)),this._preferencesEditor}async submitPreferences(e){this._preferences=this._preferences.merging(e),await this.applyPreferences()}async applyPreferences(){const e=this._settings;this._settings=new C(this._preferences,this._defaults),this._preferencesEditor!==null&&(this._preferencesEditor=new b(this._preferences,this.settings,this.pub.metadata)),this._layout===d.fixed?this.handleFXLPrefs(e,this._settings):await this.updateCSS(!0)}handleFXLPrefs(e,t){e.columnCount!==t.columnCount&&this.framePool.setPerPage(t.columnCount)}async updateCSS(e){this._css.update(this._settings),e&&await this.commitCSS(this._css)}compileCSSProperties(e){const t={};for(const[i,s]of Object.entries(e.rsProperties.toCSSProperties()))t[i]=s;for(const[i,s]of Object.entries(e.userProperties.toCSSProperties()))t[i]=s;return t}async commitCSS(e){if(!this.framePool)return;const t=this.compileCSSProperties(e);this.framePool.setCSSProperties(t),this._css.userProperties.view==="paged"&&this._layout===d.scrolled?await this.setLayout(d.reflowable):this._css.userProperties.view==="scroll"&&this._layout===d.reflowable&&await this.setLayout(d.scrolled),this._css.setContainerWidth()}async resizeHandler(){const e=this.container.parentElement||document.documentElement;if(this._layout===d.fixed){if(this.container.style.width=`${K(e)-this._settings.constraint}px`,!this.framePool)return;this.framePool.resizeHandler()}else{const t=this._css.userProperties.colCount,i=this._css.userProperties.lineLength;this._css.resizeHandler(),(this._css.userProperties.view!=="scroll"&&t!==this._css.userProperties.colCount||i!==this._css.userProperties.lineLength)&&await this.commitCSS(this._css)}}get layout(){return this._layout}get ownerWindow(){return this.container.ownerDocument.defaultView||window}get _cframes(){return(this.framePool?.currentFrames??[]).filter(e=>!(e instanceof z&&e.isDestroyed))}get pool(){return this.framePool}eventListener(e,t,i){switch(e){case"_pong":if(this.listeners.frameLoaded(this._cframes[0].iframe.contentWindow),this.listeners.positionChanged(this.currentLocation),this._notifyTimelineChange(this.currentLocation),i){const n=this._cframes.filter(l=>!!l).indexOf(i),c=n>=0?this.viewport.readingOrder[n]:void 0;c&&this._reapplyDecorationsToFrame(i,c)}else this._reapplyDecorationsToCurrentFrames();break;case"first_visible_locator":const s=v.deserialize(t);if(!s)break;this.currentLocation=new v({href:this.currentLocation.href,type:this.currentLocation.type,title:this.currentLocation.title,locations:s?.locations,text:s?.text}),this.listeners.positionChanged(this.currentLocation),this._notifyTimelineChange(this.currentLocation);break;case"text_selected":{const a=t;if(i){const c=this._cframes.filter(u=>!!u).indexOf(i),l=c>=0?this.viewport.readingOrder[c]:void 0;if(l){const u=this.pub.readingOrder.findWithHref(l);a.locator=new v({href:l,type:u.type||"application/xhtml+xml",text:new D({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("http://")||n.startsWith("https://")||n.startsWith("mailto:")||n.startsWith("tel:"))this.listeners.handleLocator(new _({href:n}).locator);else try{this.goLink(new _({href:y.join(y.dirname(this.currentLocation.href),n)}),!1,()=>{})}catch(c){console.warn(`Couldn't go to link for ${n}: ${c}`),this.listeners.handleLocator(new _({href:n}).locator)}}else console.log("Clicked on",a)}else{if(this._layout===d.fixed&&this.framePool.doNotDisturb&&(r.doNotDisturb=!0),this._layout===d.fixed&&(this.currentProgression===g.rtl||this.currentProgression===g.ltr)&&this.framePool.currentFrames.length>1){const c=this.framePool.currentFrames;r.targetFrameSrc===c[this.currentProgression===g.rtl?0:1]?.source&&(r.x+=(c[this.currentProgression===g.rtl?1:0]?.iframe.contentWindow?.innerWidth??0)*window.devicePixelRatio)}if(e==="click"?this.listeners.click(r):this.listeners.tap(r))break;const n=(this._cframes.length===2?this._cframes[0].window.innerWidth+this._cframes[1].window.innerWidth:this._cframes[0].window.innerWidth)*window.devicePixelRatio/4;r.x>=n&&r.x<=n*3&&this.listeners.miscPointer(1),r.x<n?this.goLeft(!1,()=>{}):r.x>n*3&&this.goRight(!1,()=>{})}break;case"tap_more":this.listeners.miscPointer(t);break;case"no_more":this.changeResource(1);break;case"no_less":this.changeResource(-1);break;case"swipe":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 h=t,f={...h,interactiveElement:void 0};h.interactiveElement&&(f.interactiveElement=new DOMParser().parseFromString(h.interactiveElement,"text/html").body.children[0]),this.listeners.peripheral(f);break;case"log":console.log(this._cframes[0]?.source?.split("/")[3],...t);break;default:this.listeners.customEvent(e,t);break}}determineModules(){let e=Array.from(R.keys());if(this._layout===d.fixed)return e.filter(s=>M.includes(s));e=e.filter(s=>H.includes(s));const t=w(this.pub.metadata);if(t==="cjk-vertical"||t==="mongolian-vertical")return e.filter(s=>s!=="column_snapper"&&s!=="scroll_snapper");const i=e;return this._layout===d.scrolled?e=i.filter(s=>s!=="column_snapper"&&s!=="cjk_vertical_snapper"):e=i.filter(s=>s!=="scroll_snapper"&&s!=="cjk_vertical_snapper"),e}attachListener(){const e=this._cframes.filter(t=>!!t);if(e.length===0)throw Error("no cframe to attach listener to");e.forEach(t=>{t.msg&&(t.msg.listener=(i,s)=>{this.eventListener(i,s,t)})}),this._reapplyDecorationsToCurrentFrames()}async apply(){if(await this.framePool.update(this.pub,this.currentLocator,this.determineModules()),this.attachListener(),this.pub.readingOrder.findIndexWithHref(this.currentLocation.href)<0)throw Error("Link for "+this.currentLocation.href+" not found!")}supportsDecorationStyle(e){return q(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._sendDecorationActivationToFrames(e,!0)),(t.onDecorationPointerEnter||t.onDecorationPointerLeave)&&(this._decorationHoverState.set(e,!0),this._sendDecorationHoverToFrames(e,!0))}unregisterDecorationObserver(e){this._decorationObservers.forEach((t,i)=>{if(!t.has(e))return;t.delete(e);const s=[...t].some(o=>o.onDecorationActivated);this._decorationActivationState.has(i)&&!s&&(this._decorationActivationState.delete(i),this._sendDecorationActivationToFrames(i,!1));const r=[...t].some(o=>o.onDecorationPointerEnter||o.onDecorationPointerLeave);this._decorationHoverState.has(i)&&!r&&(this._decorationHoverState.delete(i),this._sendDecorationHoverToFrames(i,!1))})}_sendDecorationActivationToFrames(e,t){this._cframes.filter(s=>!!s).forEach(s=>{s.msg&&s.msg.send("decoration_activatable",{group:e,activatable:t})})}_sendDecorationHoverToFrames(e,t){this._cframes.filter(s=>!!s).forEach(s=>{s.msg&&s.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)??[],s=new Map(i.map(c=>[c.id,c])),r=new Map(e.map(c=>[c.id,c])),o=[],h=[],f=[];for(const[c,l]of s)r.has(c)?Y(l,r.get(c))||f.push(r.get(c)):o.push(c);for(const[c,l]of r)s.has(c)||h.push(l);this._decorations.set(t,e),this._sendDecorationOps(t,o,h,f,i);const a=this._decorationActivationState.get(t);a!==void 0&&this._sendDecorationActivationToFrames(t,a);const n=this._decorationHoverState.get(t);n!==void 0&&this._sendDecorationHoverToFrames(t,n)}_sendDecorationOps(e,t,i,s,r){const o=this._cframes.filter(a=>!!a),h=new Map(r.map(a=>[a.id,a])),f=this.viewport.readingOrder;o.forEach((a,n)=>{if(!a.msg)return;const c=f[n];if(c){for(const l of t){const u=h.get(l);!u||u.locator.href!==c||a.msg.send("decorate",{group:e,action:"remove",decoration:{id:l}})}for(const l of i)l.locator.href===c&&a.msg.send("decorate",{group:e,action:"add",decoration:P(l,this._decoratorConfig.decorationTemplates)});for(const l of s)l.locator.href===c&&a.msg.send("decorate",{group:e,action:"update",decoration:P(l,this._decoratorConfig.decorationTemplates)})}})}_reapplyDecorationsToFrame(e,t){if(e.msg){for(const[i,s]of this._decorations){const r=s.filter(o=>o.locator.href===t);if(r.length!==0){e.msg.send("decorate",{group:i,action:"clear"});for(const o of r)e.msg.send("decorate",{group:i,action:"add",decoration:P(o,this._decoratorConfig.decorationTemplates)})}}for(const[i,s]of this._decorationActivationState)e.msg.send("decoration_activatable",{group:i,activatable:s});for(const[i,s]of this._decorationHoverState)e.msg.send("decoration_hoverable",{group:i,hoverable:s})}}_reapplyDecorationsToCurrentFrames(){const e=this._cframes.filter(i=>!!i),t=this.viewport.readingOrder;e.forEach((i,s)=>{const r=t[s];r&&this._reapplyDecorationsToFrame(i,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(o=>o.id===e.decorationId);if(!i)return!1;const s={decoration:i,group:e.group,rect:e.rect,point:e.point};let r=!1;for(const o of t)o.onDecorationActivated?.(s)&&(r=!0);return r}_handleDecorationPointerEnter(e){const t=this._decorationObservers.get(e.group);if(!t||t.size===0)return;const i=(this._decorations.get(e.group)??[]).find(r=>r.id===e.decorationId);if(!i)return;this._decorationHoveredDecorations.set(e.group,i);const s={decoration:i,group:e.group,rect:e.rect,point:e.point};for(const r of t)r.onDecorationPointerEnter?.(s)}_handleDecorationPointerLeave(e){const t=this._decorationObservers.get(e.group);if(!t||t.size===0)return;const i=(this._decorations.get(e.group)??[]).find(r=>r.id===e.decorationId)??this._decorationHoveredDecorations.get(e.group);if(this._decorationHoveredDecorations.delete(e.group),!i)return;const s={decoration:i,group:e.group,rect:e.rect,point:e.point};for(const r of t)r.onDecorationPointerLeave?.(s)}async destroy(){this._suspiciousActivityListener&&window.removeEventListener(S,this._suspiciousActivityListener),this._keyboardPeripheralListener&&window.removeEventListener(x,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()}async changeResource(e){if(e===0)return!1;if(this._layout===d.fixed){const s=this.framePool,r=s.viewport.positions[0];if(e===1){if(!s.next(s.perPage))return!1}else if(e===-1){if(!s.prev(s.perPage))return!1}else throw Error("Invalid relative value for FXL");const o=s.viewport.positions[0];if(r>o){for(let h=this.positions.length-1;h>=0;h--)if(this.positions[h].href===this.pub.readingOrder.items[o-1].href){this.currentLocation=this.positions[h].copyWithLocations({progression:.999999999999});break}}else if(r<o){for(let h=0;h<this.positions.length;h++)if(this.positions[h].href===this.pub.readingOrder.items[o-1].href){this.currentLocation=this.positions[h];break}}return await this.apply(),this.listeners.positionChanged(this.currentLocation),this._notifyTimelineChange(this.currentLocation),!0}const t=this.pub.readingOrder.findIndexWithHref(this.currentLocation.href),i=Math.max(0,Math.min(this.pub.readingOrder.items.length-1,t+e));if(i===t)return this._cframes[0]?.msg?.send("shake",void 0,async s=>{}),!1;if(t>i){for(let s=this.positions.length-1;s>=0;s--)if(this.positions[s].href===this.pub.readingOrder.items[i].href){this.currentLocation=this.positions[s].copyWithLocations({progression:.999999999999});break}}else for(let s=0;s<this.positions.length;s++)if(this.positions[s].href===this.pub.readingOrder.items[i].href){this.currentLocation=this.positions[s];break}return await this.apply(),!0}findLastPositionInProgressionRange(e,t){const i=e.findLastIndex(s=>{const r=s.locations.progression;return!!(r&&r>t.start&&r<=t.end)});return i!==-1?e[i]:void 0}findNearestPositions(e){const t=this.positions.filter(o=>o.href===this.currentLocation.href);let i=this.currentLocation,s;const r=t.findLastIndex(o=>(o.locations.progression??0)<=e.start);if(r!==-1){i=t[r];const o=t.slice(r+1);s=this.findLastPositionInProgressionRange(o,e)}return{first:i,last:s}}updateViewport(e){this.reflowViewport.readingOrder=[],this.reflowViewport.progressions.clear(),this.reflowViewport.positions=null,this.currentLocation&&(this.reflowViewport.readingOrder.push(this.currentLocation.href),this.reflowViewport.progressions.set(this.currentLocation.href,e),this.currentLocation.locations?.position!==void 0&&(this.reflowViewport.positions=[this.currentLocation.locations.position],this.lastLocationInView?.locations?.position!==void 0&&this.reflowViewport.positions.push(this.lastLocationInView.locations.position)))}async syncLocation(e){const t=e,i=this.findNearestPositions(t);this.currentLocation=i.first.copyWithLocations({progression:t.start}),this.lastLocationInView=i.last,this.updateViewport(t),this.listeners.positionChanged(this.currentLocation);const s=t.fragmentId?this.currentLocation.copyWithLocations({fragments:[`#${t.fragmentId}`]}):this.currentLocation;this._visibleFragmentIds=t.visibleFragmentIds??[],this._notifyTimelineChange(s),await this.framePool.update(this.pub,this.currentLocation,this.determineModules())}goBackward(e,t){if(this._isNavigating){t(!1);return}this._isNavigating=!0,this._layout===d.fixed?this.changeResource(-1).then(i=>{this._isNavigating=!1,t(i)}):this._cframes[0]?.msg?.send("go_prev",void 0,async i=>{if(i)this._isNavigating=!1,t(!0);else{const s=await this.changeResource(-1);this._isNavigating=!1,t(s)}})}goForward(e,t){if(this._isNavigating){t(!1);return}this._isNavigating=!0,this._layout===d.fixed?this.changeResource(1).then(i=>{this._isNavigating=!1,t(i)}):this._cframes[0]?.msg?.send("go_next",void 0,async i=>{if(i)this._isNavigating=!1,t(!0);else{const s=await this.changeResource(1);this._isNavigating=!1,t(s)}})}get currentLocator(){return this.currentLocation}get viewport(){return this._layout===d.fixed?this.framePool?this.framePool.viewport:{readingOrder:[],progressions:new Map,positions:null}:this.reflowViewport}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.currentProgression}async setLayout(e){this._layout!==e&&(this._layout=e,await this.framePool.update(this.pub,this.currentLocator,this.determineModules(),!0),this.attachListener())}get publication(){return this.pub}get timeline(){const e=this.pub.timeline;if(!this._timelineAugmented&&this.positions?.length){const t=this.positions,i=new Set;e.augment((s,r)=>{if(s.children?.length)return{};const o=r.href.indexOf("#"),h=o>=0?r.href.slice(0,o):r.href,f=o>=0?r.href.slice(o+1):void 0,a=t.filter(l=>l.href===h);if(!a.length)return{};const n=f?a.find(l=>l.locations.fragments[0]===f):void 0;let c;return n?c=n:i.has(h)?c=void 0:(i.add(h),c=a.reduce((l,u)=>(u.locations.position??1/0)<(l.locations.position??1/0)?u:l)),c?{position:c.locations.position,scroll:n?.locations.progression}:{}}),this._timelineAugmented=!0}return this._wrappedTimeline||(this._wrappedTimeline=new Proxy(e,{get:(t,i)=>{if(i!=="navigableFrom"){const s=Reflect.get(t,i,t);return typeof s=="function"?s.bind(t):s}return s=>{const r=this._visibleFragmentIds;let o=(r.length?t.locate(this.currentLocation.copyWithLocations({fragments:[`#${r[0]}`]})):void 0)??s;const h=(r.length?t.locate(this.currentLocation.copyWithLocations({fragments:[`#${r[r.length-1]}`]})):void 0)??s;if(this.isScrollStart){const n=t.ancestors(o).find(c=>c.references.includes(this.currentLocation.href));n&&(o=n)}const f=t.navigableFrom(o).previous,a=t.navigableFrom(h).next;return{previous:f,next:a}}}})),this._wrappedTimeline}async loadLocator(e,t){let i=!1,s=O(e.locations);if(e.text?.highlight?i=await new Promise((f,a)=>{this._cframes[0].msg.send("go_text",s?[e.text?.serialize(),s]:e.text?.serialize(),n=>f(n))}):s&&(i=await new Promise((f,a)=>{this._cframes[0].msg.send("go_text",["",s],n=>f(n))})),i){t(i);return}const r=I(e.locations);if(r&&(i=await new Promise((f,a)=>{this._cframes[0].msg.send("go_id",r,n=>f(n))})),i){t(i);return}const o=e?.locations?.progression;o&&o>0?i=await new Promise((f,a)=>{this._cframes[0].msg.send("go_progression",o,n=>f(n))}):i=!0,t(i)}go(e,t,i){const s=e.href.split("#")[0];let r=this.pub.readingOrder.findWithHref(s);if(!r)return i(this.listeners.handleLocator(e));if(this._isNavigating){i(!1);return}this._isNavigating=!0,this.currentLocation=this.positions.find(o=>o.href===r.href),this.apply().then(()=>this.loadLocator(e,o=>{this._isNavigating=!1,i(o)})).then(()=>{this.attachListener()})}goLink(e,t,i){return this.go(e.locator,t,i)}_notifyTimelineChange(e){const t=this.timeline.locate(e),i=!Z(this._visibleFragmentIds,this._notifiedVisibleFragmentIds);(t!==this._currentTimelineItem||i)&&(this._currentTimelineItem=t,this._notifiedVisibleFragmentIds=this._visibleFragmentIds,this.listeners.timelineItemChanged(t))}}export{k as EpubNavigator};
@@ -1 +1 @@
1
- import{Loader as o}from"@readium/navigator-html-injectables";import{FrameComms as n}from"./FrameComms.js";import{sML as h}from"@readium/helpers";import{KeyboardConditionBridge as m}from"../../peripherals/KeyboardConditionBridge.js";class y{constructor(t,i={},r=[],e=[]){this.timelineFragmentIds=e,this.hidden=!0,this.destroyed=!1,this.currModules=[],this.frame=document.createElement("iframe"),this.frame.sandbox.value="allow-same-origin allow-scripts",this.frame.classList.add("readium-navigator-iframe"),this.frame.style.visibility="hidden",this.frame.style.setProperty("aria-hidden","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.source=t,this.contentProtectionConfig={...i},this.keyboardPeripheralsConfig=[...r]}async load(t){return new Promise((i,r)=>{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{r(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 m(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)}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.style.setProperty("aria-hidden","true"),this.frame.style.opacity="0",this.frame.style.pointerEvents="none",this.hidden=!0,this.frame.blur(),this.frame.parentElement)return this.comms===void 0||!this.comms.ready?void 0:new Promise((t,i)=>{this.comms?.send("unfocus",void 0,r=>{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,r)=>{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.style.removeProperty("aria-hidden"),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?.resume():this.comms=new n(this.frame.contentWindow,this.source)),this.comms?.send("update_properties",t),this.hidden&&this.comms?.halt())}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 isDestroyed(){return this.destroyed}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 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{y as FrameManager};
1
+ import{Loader as o}from"@readium/navigator-html-injectables";import{FrameComms as n}from"./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.sandbox.value="allow-same-origin allow-scripts",this.frame.classList.add("readium-navigator-iframe"),this.frame.style.visibility="hidden",this.frame.style.setProperty("aria-hidden","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.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.style.setProperty("aria-hidden","true"),this.frame.style.opacity="0",this.frame.style.pointerEvents="none",this.hidden=!0,this.frame.blur(),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.style.removeProperty("aria-hidden"),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?.resume():this.comms=new n(this.frame.contentWindow,this.source)),this.comms?.send("update_properties",t),this.hidden&&this.comms?.halt())}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 isDestroyed(){return this.destroyed}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 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{y as FrameManager};
@@ -1 +1 @@
1
- import y from"./FrameBlobBuilder.js";import{FrameManager as P}from"./FrameManager.js";const b=5,m=3;class B{constructor(e,r,s,n,o,i,l){this.pool=new Map,this.blobs=new Map,this.inprogress=new Map,this.pendingUpdates=new Map,this.injector=null,this.container=e,this.positions=r,this.currentCssProperties=s,this.injector=n??null,this.contentProtectionConfig=o||{},this.keyboardPeripheralsConfig=i||[],this.getFragmentIds=l??(()=>[])}async destroy(){let e=this.inprogress.values(),r=e.next();const s=[];for(;r.value;)s.push(r.value),r=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(i=>{this.injector?.releaseBlobUrl?.(i),URL.revokeObjectURL(i)}),this.injector?.dispose(),this.container.childNodes.forEach(i=>{(i.nodeType===Node.ELEMENT_NODE||i.nodeType===Node.TEXT_NODE)&&i.remove()})}async update(e,r,s,n=!1){let o=this.positions.findIndex(p=>p.locations.position===r.locations.position);if(o<0)throw Error(`Locator not found in position list: ${r.locations.position} > ${this.positions.reduce((p,d)=>d.locations.position||0>p?d.locations.position||0:p,0)}`);const i=this.positions[o].href;this.inprogress.has(i)&&await this.inprogress.get(i);const l=new Promise(async(p,d)=>{const u=[],f=[];this.positions.forEach((t,c)=>{(c>o+b||c<o-b)&&(u.includes(t.href)||u.push(t.href)),c<o+m&&c>o-m&&(f.includes(t.href)||f.push(t.href))}),u.forEach(async t=>{f.includes(t)||this.pool.has(t)&&(await this.pool.get(t)?.destroy(),this.pool.delete(t),this.pendingUpdates.has(t)&&this.pendingUpdates.set(t,{inPool:!1}))}),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 w=async t=>{if(n&&(this.blobs.forEach(a=>{this.injector?.releaseBlobUrl?.(a),URL.revokeObjectURL(a)}),this.blobs.clear(),this.pendingUpdates.clear()),this.pendingUpdates.has(t)&&this.pendingUpdates.get(t)?.inPool===!1){const a=this.blobs.get(t);a&&(this.injector?.releaseBlobUrl?.(a),URL.revokeObjectURL(a),this.blobs.delete(t),this.pendingUpdates.delete(t))}if(this.pool.has(t)){const a=this.pool.get(t);if(!this.blobs.has(t))await a.destroy(),this.pool.delete(t),this.pendingUpdates.delete(t);else{await a.load(s);return}}const c=e.readingOrder.findWithHref(t);if(!c)return;if(!this.blobs.has(t)){const U=await new y(e,this.currentBaseURL||"",c,{cssProperties:this.currentCssProperties,injector:this.injector}).build();this.blobs.set(t,U)}const g=new P(this.blobs.get(t),this.contentProtectionConfig,this.keyboardPeripheralsConfig,this.getFragmentIds(t));t!==i&&await g.hide(),this.container.appendChild(g.iframe),await g.load(s),this.pool.set(t,g)};try{await Promise.all(f.map(t=>w(t)))}catch(t){d(t)}const h=this.pool.get(i);if((h?.source!==this._currentFrame?.source||n)&&(await this._currentFrame?.hide(),h&&await h.load(s),h&&await h.show(r.locations.progression),this._currentFrame=h,h)){const t=this.container.ownerDocument.activeElement;t&&t.tagName==="IFRAME"&&t!==h.iframe&&h.iframe.focus({preventScroll:!0})}p()});this.inprogress.set(i,l),await l,this.inprogress.delete(i)}setCSSProperties(e){if(!((s,n)=>{const o=Object.keys(s),i=Object.keys(n);if(o.length!==i.length)return!1;for(const l of o)if(s[l]!==n[l])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(r=>{if(!r)return;const s=r.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{B as FramePoolManager};
1
+ import y from"./FrameBlobBuilder.js";import{FrameManager as R}from"./FrameManager.js";const b=5,m=3;class B{constructor(e,i,s,n,r,o,c,l=[]){this.pool=new Map,this.blobs=new Map,this.inprogress=new Map,this.pendingUpdates=new Map,this.injector=null,this.container=e,this.positions=i,this.currentCssProperties=s,this.injector=n??null,this.contentProtectionConfig=r||{},this.keyboardPeripheralsConfig=o||[],this.getFragmentIds=c??(()=>[]),this.resizeWatchSelectors=[...l]}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(),r=n.next();for(;r.value;)await r.value.destroy(),r=n.next();this.pool.clear(),this.blobs.forEach(o=>{this.injector?.releaseBlobUrl?.(o),URL.revokeObjectURL(o)}),this.injector?.dispose(),this.container.childNodes.forEach(o=>{(o.nodeType===Node.ELEMENT_NODE||o.nodeType===Node.TEXT_NODE)&&o.remove()})}async update(e,i,s,n=!1){let r=this.positions.findIndex(l=>l.locations.position===i.locations.position);if(r<0)throw Error(`Locator not found in position list: ${i.locations.position} > ${this.positions.reduce((l,d)=>d.locations.position||0>l?d.locations.position||0:l,0)}`);const o=this.positions[r].href;this.inprogress.has(o)&&await this.inprogress.get(o);const c=new Promise(async(l,d)=>{const u=[],f=[];this.positions.forEach((t,p)=>{(p>r+b||p<r-b)&&(u.includes(t.href)||u.push(t.href)),p<r+m&&p>r-m&&(f.includes(t.href)||f.push(t.href))}),u.forEach(async t=>{f.includes(t)||this.pool.has(t)&&(await this.pool.get(t)?.destroy(),this.pool.delete(t),this.pendingUpdates.has(t)&&this.pendingUpdates.set(t,{inPool:!1}))}),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 w=async t=>{if(n&&(this.blobs.forEach(a=>{this.injector?.releaseBlobUrl?.(a),URL.revokeObjectURL(a)}),this.blobs.clear(),this.pendingUpdates.clear()),this.pendingUpdates.has(t)&&this.pendingUpdates.get(t)?.inPool===!1){const a=this.blobs.get(t);a&&(this.injector?.releaseBlobUrl?.(a),URL.revokeObjectURL(a),this.blobs.delete(t),this.pendingUpdates.delete(t))}if(this.pool.has(t)){const a=this.pool.get(t);if(!this.blobs.has(t))await a.destroy(),this.pool.delete(t),this.pendingUpdates.delete(t);else{await a.load(s);return}}const p=e.readingOrder.findWithHref(t);if(!p)return;if(!this.blobs.has(t)){const U=await new y(e,this.currentBaseURL||"",p,{cssProperties:this.currentCssProperties,injector:this.injector}).build();this.blobs.set(t,U)}const g=new R(this.blobs.get(t),this.contentProtectionConfig,this.keyboardPeripheralsConfig,this.getFragmentIds(t),this.resizeWatchSelectors);t!==o&&await g.hide(),this.container.appendChild(g.iframe),await g.load(s),this.pool.set(t,g)};try{await Promise.all(f.map(t=>w(t)))}catch(t){d(t)}const h=this.pool.get(o);if((h?.source!==this._currentFrame?.source||n)&&(await this._currentFrame?.hide(),h&&await h.load(s),h&&await h.show(i.locations.progression),this._currentFrame=h,h)){const t=this.container.ownerDocument.activeElement;t&&t.tagName==="IFRAME"&&t!==h.iframe&&h.iframe.focus({preventScroll:!0})}l()});this.inprogress.set(o,c),await c,this.inprogress.delete(o)}setCSSProperties(e){if(!((s,n)=>{const r=Object.keys(s),o=Object.keys(n);if(r.length!==o.length)return!1;for(const c of r)if(s[c]!==n[c])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{B as FramePoolManager};
@@ -1 +1 @@
1
- import{Loader as n}from"@readium/navigator-html-injectables";import{ReadingProgression as a,Page as h}from"@readium/shared";import{FrameComms as d}from"../frame/FrameComms.js";import{KeyboardConditionBridge as l}from"../../peripherals/KeyboardConditionBridge.js";class u{constructor(t,e,r,s={},i=[]){this.currModules=[],this.cachedPage=void 0,this.peripherals=t,this.debugHref=r,this.contentProtectionConfig={...s},this.keyboardPeripheralsConfig=[...i],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.style.setProperty("aria-hidden","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=r,this.source="about:blank",this.wrapper=document.createElement("div"),this.wrapper.style.position="relative",this.wrapper.style.float=this.wrapper.style.cssFloat=e===a.rtl?"right":"left",this.wrapper.appendChild(this.frame)}async load(t,e){return 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((r,s)=>{if(this.loader&&this.loaded){const i=this.frame.contentWindow;if([...this.currModules].sort().join("|")===[...t].sort().join("|")){try{r(i),this.loadPromise=void 0}catch{}return}this.comms?.halt(),this.loader.destroy(),this.loader=new n(i,t),this.currModules=t,this.comms=void 0;try{r(i),this.loadPromise=void 0}catch{}return}this.frame.addEventListener("load",()=>{const i=this.frame.contentWindow;this.loader=new n(i,t),this.currModules=t,this.peripherals.observe(this.wrapper),this.peripherals.observe(i);try{r(i)}catch{}},{once:!0}),this.frame.addEventListener("error",i=>{try{s(i.error),this.loadPromise=void 0}catch{}},{once:!0}),this.frame.style.removeProperty("display"),this.frame.contentWindow.location.replace(this.source)}),this.loadPromise)}loadPageSize(){const t=this.frame.contentWindow,e=t.document.head.querySelector("meta[name=viewport]");if(e){const r=/(\w+) *= *([^\s,]+)/g;let s,i=0,o=0;for(;s=r.exec(e.content);)s[1]==="width"?i=Number.parseFloat(s[2]):s[1]==="height"&&(o=Number.parseFloat(s[2]));if(i>0&&o>0)return{width:i,height:o}}return{width:t.document.body.scrollWidth,height:t.document.body.scrollHeight}}update(t){if(!this.loaded)return;const e=this.loadPageSize();this.frame.style.height=`${e.height}px`,this.frame.style.width=`${e.width}px`;const r=Math.min(this.wrapper.clientWidth/e.width,this.wrapper.clientHeight/e.height);this.frame.style.transform=`scale(${r})`;const s=this.frame.getBoundingClientRect(),i=this.wrapper.clientHeight-s.height;if(this.frame.style.top=`${i/2}px`,t===h.left){const o=this.wrapper.clientWidth-s.width;this.frame.style.left=`${o}px`}else if(t===h.center){const o=this.wrapper.clientWidth-s.width;this.frame.style.left=`${o/2}px`}else this.frame.style.left="0px";this.frame.style.removeProperty("visibility"),this.frame.style.removeProperty("aria-hidden"),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.style.setProperty("aria-hidden","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",r=>{try{this.showPromise=void 0,e(r.error)}catch{}},{once:!0}),this.source="about:blank",this.frame.contentWindow.location.replace("about:blank"),this.frame.style.display="none"})}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,r=>{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)}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 d(this.frame.contentWindow,this.source),this.showPromise=new Promise((e,r)=>{this.comms.send("focus",void 0,s=>{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){const e=`${t}%`;this.wrapper.style.width!==e&&(this.wrapper.style.width=e)}set height(t){const e=`${t}px`;this.wrapper.style.height!==e&&(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{u as FXLFrameManager};
1
+ import{Loader as h}from"@readium/navigator-html-injectables";import{ReadingProgression as a,Page as n}from"@readium/shared";import{FrameComms as c}from"../frame/FrameComms.js";import{KeyboardConditionBridge as d}from"../../peripherals/KeyboardConditionBridge.js";class u{constructor(t,e,s,r={},i=[],o=[]){this.pendingUnwatchSelectors=[],this.currModules=[],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.style.setProperty("aria-hidden","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.float=this.wrapper.style.cssFloat=e===a.rtl?"right":"left",this.wrapper.appendChild(this.frame)}async load(t,e){return 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 h(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 h(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(){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{width:i,height:o}}return{width:t.document.body.scrollWidth,height:t.document.body.scrollHeight}}update(t){if(!this.loaded)return;const e=this.loadPageSize();this.frame.style.height=`${e.height}px`,this.frame.style.width=`${e.width}px`;const s=Math.min(this.wrapper.clientWidth/e.width,this.wrapper.clientHeight/e.height);this.frame.style.transform=`scale(${s})`;const r=this.frame.getBoundingClientRect(),i=this.wrapper.clientHeight-r.height;if(this.frame.style.top=`${i/2}px`,t===n.left){const o=this.wrapper.clientWidth-r.width;this.frame.style.left=`${o}px`}else if(t===n.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.style.removeProperty("aria-hidden"),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.style.setProperty("aria-hidden","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"})}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 d(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){const e=`${t}%`;this.wrapper.style.width!==e&&(this.wrapper.style.width=e)}set height(t){const e=`${t}px`;this.wrapper.style.height!==e&&(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{u as FXLFrameManager};
@@ -1 +1 @@
1
- import{ReadingProgression as S,Page as p}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 E}from"./FXLSpreader.js";const T=8,O=5,U=300,A=15e3,D=250,I=150,N=500;class z{constructor(e,t,r,a,n,s){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=r,this.injector=a??null,this.contentProtectionConfig=n||{},this.keyboardPeripheralsConfig=s||[],this.spreadPresentation=r.metadata.otherMetadata?.spread||y.auto,this.pub.metadata.effectiveReadingProgression!==S.rtl&&this.pub.metadata.effectiveReadingProgression!==S.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(l=>{const h=new M(this.peripherals,this.pub.metadata.effectiveReadingProgression,l.href,this.contentProtectionConfig,this.keyboardPeripheralsConfig);this.spineElement.appendChild(h.element),this.pool.set(l.href,h),h.width=100/this.length*(l.properties?.otherProperties.orientation===E.landscape||l.properties?.otherProperties.addBlank?this.perPage:1),h.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((r,a)=>{let n=this.pub.readingOrder.items.findIndex(h=>h.href===a);const s=this.pub.readingOrder.items[n];if(r.width=100/this.length*(s.properties?.otherProperties.orientation===E.landscape||s.properties?.otherProperties.addBlank?this.perPage:1),r.height=this.height,!r.loaded)return;const l=this.spreader.findByLink(s);r.update(this.spreadPosition(l,s))})},D)}updateDimensions(){this.width=this.bookElement.clientWidth,this.height=this.bookElement.clientHeight}get rtl(){return this.pub.metadata.effectiveReadingProgression===S.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 r="0";this.updateDimensions(),this.perPage>1&&(r=`${this.width/2}px`);const a={transition:e?`all ${t?I:N}ms ease-out`:"all 0ms ease-out",marginRight:this.rtl?r:"0",marginLeft:this.rtl?"0":r,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 r=`translate3d(${this.offset}px, 0, 0)`;this.spineElement.style.transform!==r&&(this.transform=r,this.updateSpineStyle(!0,t),this.deselect())})});else{const r=`translate3d(${this.offset}px, 0, 0)`;if(this.spineElement.style.transform===r)return;this.transform=r,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 r=[];for(;t.value;)r.push(t.value),t=e.next();r.length>0&&await Promise.allSettled(r),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(s=>URL.revokeObjectURL(s)),this.injector?.dispose(),this.container.childNodes.forEach(s=>{(s.nodeType===Node.ELEMENT_NODE||s.nodeType===Node.TEXT_NODE)&&s.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?p.center:e.length<2?p.center:t.href===e[0].href?this.rtl?p.right:p.left:this.rtl?p.left:p.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,r,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 s=this.makeSpread(this.currentSlide);this.perPage>1&&n++;for(const h of s)await this.waitForItem(h.href);const l=new Promise(async(h,L)=>{const g=[],c=[];this.positions.forEach((i,o)=>{(o>n+T||o<n-T)&&(g.includes(i.href)||g.push(i.href)),o<n+O&&o>n-O&&(c.includes(i.href)||c.push(i.href))}),g.forEach(async i=>{c.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 v=async i=>{const o=e.readingOrder.findIndexWithHref(i),d=e.readingOrder.items[o];if(d){if(!this.blobs.has(i)){const m=await new k(e,this.currentBaseURL||"",d,{injector:this.injector}).build(!0);this.blobs.set(i,m)}this.delayedShow.has(i)||this.delayedShow.set(i,new Promise((w,m)=>{let b=!1;const F=window.setTimeout(async()=>{this.delayedTimeout.set(i,0);const C=this.makeSpread(this.reAlign(o)),R=this.spreadPosition(C,d),P=this.pool.get(i);await P.load(r,this.blobs.get(i)),this.peripherals.isScaled||await P.show(R),this.delayedShow.delete(i),b=!0,w()},U);setTimeout(()=>{!b&&this.delayedShow.has(i)&&m(`Offscreen load timeout: ${i}`)},A),this.delayedTimeout.set(i,F)}))}};try{await Promise.all(c.map(i=>v(i)))}catch(i){L(i)}const u=[];for(const i of s){const o=this.pool.get(i.href),d=this.blobs.get(i.href);d&&(this.cancelShowing(i.href),await o.load(r,d),await o.show(this.spreadPosition(s,i)),this.previousFrames.push(o),await o.activate(),u.push(o))}for(;this.previousFrames.length>0;){const i=this.previousFrames.shift();i&&!u.includes(i)&&await i.unfocus()}this.previousFrames=u;const f=this.container.ownerDocument.activeElement;f&&f.tagName==="IFRAME"&&!u.some(i=>i.iframe===f)&&u[0]?.iframe.focus({preventScroll:!0}),h()});for(const h of s)this.inprogress.set(h.href,l);await l;for(const h of s)this.inprogress.delete(h.href)}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 r=t.realSize;e.x=Math.min(e.x,r.x),e.y=Math.min(e.y,r.y),e.width+=r.width,e.height=Math.max(e.height,r.height),e.top=Math.min(e.top,r.top),e.right=Math.min(e.right,r.right),e.bottom=Math.min(e.bottom,r.bottom),e.left=Math.min(e.left,r.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(r=>{e.readingOrder.push(r.href),e.progressions.set(r.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{z as FXLFramePoolManager};
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 +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 m}from"../peripherals/KeyboardConditionBridge.js";class y{constructor(t,i={},r=[],e=[]){this.timelineFragmentIds=e,this.hidden=!0,this.destroyed=!1,this.currModules=[],this.frame=document.createElement("iframe"),this.frame.classList.add("readium-navigator-iframe"),this.frame.style.visibility="hidden",this.frame.style.setProperty("aria-hidden","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=[...r]}async load(t=[]){return new Promise((i,r)=>{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{r(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 m(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)}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.style.setProperty("aria-hidden","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,r=>{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,r)=>{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.style.removeProperty("aria-hidden"),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?.resume():this.comms=new n(this.frame.contentWindow,this.source)),this.comms?.send("update_properties",t),this.hidden&&this.comms?.halt())}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
+ 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.style.setProperty("aria-hidden","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.style.setProperty("aria-hidden","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.style.removeProperty("aria-hidden"),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?.resume():this.comms=new n(this.frame.contentWindow,this.source)),this.comms?.send("update_properties",t),this.hidden&&this.comms?.halt())}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 L{constructor(t,o,s,n={},r=[],i){this.pool=new Map,this.blobs=new Map,this.inprogress=new Map,this.pendingUpdates=new Map,this.injector=null,this.container=t,this.currentCssProperties=o,this.injector=s,this.contentProtectionConfig=n,this.keyboardPeripheralsConfig=[...r],this.getFragmentIds=i??(()=>[])}async destroy(){let t=this.inprogress.values(),o=t.next();const s=[];for(;o.value;)s.push(o.value),o=t.next();s.length>0&&await Promise.allSettled(s),this.inprogress.clear();let n=this.pool.values(),r=n.next();for(;r.value;)await r.value.destroy(),r=n.next();this.pool.clear(),this.blobs.forEach(i=>{this.injector?.releaseBlobUrl?.(i),URL.revokeObjectURL(i)}),this.blobs.clear(),this.injector?.dispose(),this.container.childNodes.forEach(i=>{(i.nodeType===Node.ELEMENT_NODE||i.nodeType===Node.TEXT_NODE)&&i.remove()})}async update(t,o,s){const n=t.readingOrder.items;let r=n.findIndex(f=>f.href===o.href);if(r<0)throw Error(`Locator not found in reading order: ${o.href}`);const i=n[r].href;this.inprogress.has(i)&&await this.inprogress.get(i);const c=new Promise(async(f,u)=>{const g=[],d=[];t.readingOrder.items.forEach((e,h)=>{h!==r&&h!==r-1&&h!==r+1&&(g.includes(e.href)||g.push(e.href)),h===r&&(d.includes(e.href)||d.push(e.href))}),g.forEach(async e=>{d.includes(e)||this.pool.has(e)&&(await this.pool.get(e)?.destroy(),this.pool.delete(e))}),this.currentBaseURL!==void 0&&t.baseURL!==this.currentBaseURL&&(this.blobs.forEach(e=>{this.injector?.releaseBlobUrl?.(e),URL.revokeObjectURL(e)}),this.blobs.clear()),this.currentBaseURL=t.baseURL;const b=async e=>{if(this.pendingUpdates.has(e)&&this.pendingUpdates.get(e)?.inPool===!1){const l=this.blobs.get(e);l&&(this.injector?.releaseBlobUrl?.(l),URL.revokeObjectURL(l),this.blobs.delete(e),this.pendingUpdates.delete(e))}if(this.pool.has(e)){const l=this.pool.get(e);if(!this.blobs.has(e))await l.destroy(),this.pool.delete(e),this.pendingUpdates.delete(e);else{await l.load(s);return}}const h=t.readingOrder.findWithHref(e);if(!h)return;if(!this.blobs.has(e)){const m=await new w(t,this.currentBaseURL||"",h,{cssProperties:this.currentCssProperties,injector:this.injector}).build();this.blobs.set(e,m)}const p=new y(this.blobs.get(e),this.contentProtectionConfig,this.keyboardPeripheralsConfig,this.getFragmentIds(e));e!==i&&await p.hide(),this.container.appendChild(p.iframe),await p.load(s),this.pool.set(e,p)};try{await Promise.all(d.map(e=>b(e)))}catch(e){u(e)}const a=this.pool.get(i);if(a?.source!==this._currentFrame?.source&&(await this._currentFrame?.hide(),a&&await a.load(s),a&&await a.show(o.locations.progression),this._currentFrame=a,a)){const e=this.container.ownerDocument.activeElement;e&&e.tagName==="IFRAME"&&e!==a.iframe&&a.iframe.focus({preventScroll:!0})}f()});this.inprogress.set(i,c),await c,this.inprogress.delete(i)}setCSSProperties(t){if(!((s,n)=>{const r=Object.keys(s),i=Object.keys(n);if(r.length!==i.length)return!1;for(const c of r)if(s[c]!==n[c])return!1;return!0})(this.currentCssProperties||{},t)){this.currentCssProperties=t,this.pool.forEach(s=>{s.setCSSProperties(t)});for(const s of this.blobs.keys())this.pendingUpdates.set(s,{inPool:this.pool.has(s)})}}get currentFrames(){return[this._currentFrame]}get currentBounds(){const t={x:0,y:0,width:0,height:0,top:0,right:0,bottom:0,left:0,toJSON(){return this}};return this.currentFrames.forEach(o=>{if(!o)return;const s=o.realSize;t.x=Math.min(t.x,s.x),t.y=Math.min(t.y,s.y),t.width+=s.width,t.height=Math.max(t.height,s.height),t.top=Math.min(t.top,s.top),t.right=Math.min(t.right,s.right),t.bottom=Math.min(t.bottom,s.bottom),t.left=Math.min(t.left,s.left)}),t}}export{L as WebPubFramePoolManager};
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,c)=>{c!==o&&c!==o-1&&c!==o+1&&(g.includes(t.href)||g.push(t.href)),c===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 l=this.blobs.get(t);l&&(this.injector?.releaseBlobUrl?.(l),URL.revokeObjectURL(l),this.blobs.delete(t),this.pendingUpdates.delete(t))}if(this.pool.has(t)){const l=this.pool.get(t);if(!this.blobs.has(t))await l.destroy(),this.pool.delete(t),this.pendingUpdates.delete(t);else{await l.load(s);return}}const c=e.readingOrder.findWithHref(t);if(!c)return;if(!this.blobs.has(t)){const m=await new w(e,this.currentBaseURL||"",c,{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),await h,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 w,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 H}from"../injection/webpubInjectables.js";import{NavigatorProtector as M,NAVIGATOR_SUSPICIOUS_ACTIVITY_EVENT as b}from"../protection/NavigatorProtector.js";import{KeyboardPeripherals as N,NAVIGATOR_KEYBOARD_PERIPHERAL_EVENT as L}from"../peripherals/KeyboardPeripherals.js";import{supportsDecorationStyle as R,decorationsEqual as V,resolveDecorationForWire as m}from"@readium/decorator";const z=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=z(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=H(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._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 M(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 N({keyboardPeripherals:this._keyboardPeripherals}),this._keyboardPeripheralListener=a=>{const s=a.detail;this.listeners.peripheral(s)},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(){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)),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=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(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()}supportsDecorationStyle(e){return R(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})}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)?V(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 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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@readium/navigator",
3
- "version": "2.9.1",
3
+ "version": "2.10.1",
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.1.1",
48
- "@readium/helpers": "1.1.0",
49
- "@readium/shared": "2.5.0",
50
- "@readium/navigator-html-injectables": "2.7.1"
47
+ "@readium/decorator": "1.2.1",
48
+ "@readium/shared": "2.5.1",
49
+ "@readium/navigator-html-injectables": "2.8.1",
50
+ "@readium/helpers": "1.1.0"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@readium/css": "^2.0.5",
@@ -42,4 +42,14 @@ export interface DecorableNavigator {
42
42
 
43
43
  /** Unregisters a previously registered observer from all groups. */
44
44
  unregisterDecorationObserver(observer: DecorationObserver): void;
45
+
46
+ /**
47
+ * Watches an element within the current content document for resize, so decoration
48
+ * overlays relay out when it resizes even if the resource's own document size doesn't
49
+ * change (e.g. a sidebar collapsing and the reading pane reflowing inside it).
50
+ */
51
+ addDecorationResizeTarget(selector: string): void;
52
+
53
+ /** Stops watching an element previously registered with addDecorationResizeTarget. */
54
+ removeDecorationResizeTarget(selector: string): void;
45
55
  }
@@ -114,6 +114,7 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
114
114
  private _decorationActivationState: Map<string, boolean> = new Map();
115
115
  private _decorationHoverState: Map<string, boolean> = new Map();
116
116
  private _decorationActivationConsumed = false;
117
+ private _decorationResizeSelectors: Set<string>;
117
118
 
118
119
  private reflowViewport: VisualNavigatorViewport = {
119
120
  readingOrder: [],
@@ -175,6 +176,7 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
175
176
 
176
177
  this._contentProtection = configuration.contentProtection || {};
177
178
  this._decoratorConfig = configuration.decoratorConfig || {};
179
+ this._decorationResizeSelectors = new Set(this._decoratorConfig.resizeWatchSelectors ?? []);
178
180
 
179
181
  // Merge keyboard peripherals
180
182
  this._keyboardPeripherals = this.mergeKeyboardPeripherals(
@@ -269,7 +271,8 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
269
271
  this.pub,
270
272
  this._injector,
271
273
  this._contentProtection,
272
- this._keyboardPeripherals
274
+ this._keyboardPeripherals,
275
+ [...this._decorationResizeSelectors]
273
276
  );
274
277
  this.framePool.listener = (key: CommsEventKey | ManagerEventKey, data: unknown) => {
275
278
  this.eventListener(key, data);
@@ -287,7 +290,8 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
287
290
  (href) => this.pub.timeline.segmentsForHref(href)
288
291
  .flatMap(item => item.references)
289
292
  .map(ref => { const h = ref.indexOf('#'); return h >= 0 ? ref.slice(h + 1) : ''; })
290
- .filter(Boolean)
293
+ .filter(Boolean),
294
+ [...this._decorationResizeSelectors]
291
295
  );
292
296
  }
293
297
 
@@ -725,6 +729,21 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
725
729
  });
726
730
  }
727
731
 
732
+ // Watches an element within the current content document(s) for resize, so decoration
733
+ // overlays relay out when it resizes even if the resource's own document size doesn't
734
+ // change. Not resource-scoped: forwarded to every frame, current and future, via
735
+ // framePool the same way contentProtectionConfig/keyboardPeripheralsConfig are — applied
736
+ // on every frame show(), not just replayed reactively off decoration state.
737
+ public addDecorationResizeTarget(selector: string): void {
738
+ this._decorationResizeSelectors.add(selector);
739
+ this.framePool?.addResizeTarget(selector);
740
+ }
741
+
742
+ public removeDecorationResizeTarget(selector: string): void {
743
+ this._decorationResizeSelectors.delete(selector);
744
+ this.framePool?.removeResizeTarget(selector);
745
+ }
746
+
728
747
  public applyDecorations(decorations: Decoration[], group: string): void {
729
748
  const previous = this._decorations.get(group) ?? [];
730
749
  const prevById = new Map(previous.map(d => [d.id, d]));
@@ -869,6 +888,7 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
869
888
  this._decorationHoveredDecorations.clear();
870
889
  this._decorationActivationState.clear();
871
890
  this._decorationHoverState.clear();
891
+ this._decorationResizeSelectors.clear();
872
892
  }
873
893
 
874
894
  private async changeResource(relative: number): Promise<boolean> {
@@ -15,6 +15,8 @@ export class FrameManager {
15
15
  private destroyed: boolean = false;
16
16
  private readonly contentProtectionConfig: IContentProtectionConfig;
17
17
  private readonly keyboardPeripheralsConfig: IKeyboardPeripheralsConfig;
18
+ private resizeWatchSelectors: string[];
19
+ private pendingUnwatchSelectors: string[] = [];
18
20
  private conditionBridge?: KeyboardConditionBridge;
19
21
  private currModules: ModuleName[] = [];
20
22
 
@@ -22,7 +24,8 @@ export class FrameManager {
22
24
  source: string,
23
25
  contentProtectionConfig: IContentProtectionConfig = {},
24
26
  keyboardPeripheralsConfig: IKeyboardPeripheralsConfig = [],
25
- private readonly timelineFragmentIds: string[] = []
27
+ private readonly timelineFragmentIds: string[] = [],
28
+ resizeWatchSelectors: string[] = []
26
29
  ) {
27
30
  this.frame = document.createElement("iframe");
28
31
  this.frame.sandbox.value = "allow-same-origin allow-scripts";
@@ -38,6 +41,7 @@ export class FrameManager {
38
41
  // Use the provided content protection config directly without overriding defaults
39
42
  this.contentProtectionConfig = { ...contentProtectionConfig };
40
43
  this.keyboardPeripheralsConfig = [...keyboardPeripheralsConfig];
44
+ this.resizeWatchSelectors = [...resizeWatchSelectors];
41
45
 
42
46
  }
43
47
 
@@ -99,6 +103,32 @@ export class FrameManager {
99
103
  if (this.contentProtectionConfig.protectPrinting?.disable) {
100
104
  this.comms!.send("print_protection", this.contentProtectionConfig.protectPrinting);
101
105
  }
106
+
107
+ // Send resize-watch targets
108
+ this.resizeWatchSelectors.forEach(selector => {
109
+ this.comms!.send("decoration_resize", { action: "watch", selector });
110
+ });
111
+
112
+ // Flush unwatches that couldn't be sent while comms was halted
113
+ this.pendingUnwatchSelectors.forEach(selector => {
114
+ this.comms!.send("decoration_resize", { action: "unwatch", selector });
115
+ });
116
+ this.pendingUnwatchSelectors = [];
117
+ }
118
+
119
+ addResizeTarget(selector: string): void {
120
+ if (!this.resizeWatchSelectors.includes(selector)) this.resizeWatchSelectors.push(selector);
121
+ this.pendingUnwatchSelectors = this.pendingUnwatchSelectors.filter(s => s !== selector);
122
+ if (this.comms?.ready) this.comms.send("decoration_resize", { action: "watch", selector });
123
+ }
124
+
125
+ removeResizeTarget(selector: string): void {
126
+ this.resizeWatchSelectors = this.resizeWatchSelectors.filter(s => s !== selector);
127
+ if (this.comms?.ready) {
128
+ this.comms.send("decoration_resize", { action: "unwatch", selector });
129
+ } else if (!this.pendingUnwatchSelectors.includes(selector)) {
130
+ this.pendingUnwatchSelectors.push(selector);
131
+ }
102
132
  }
103
133
 
104
134
  async destroy() {
@@ -21,6 +21,7 @@ export class FramePoolManager {
21
21
  private readonly injector: Injector | null = null;
22
22
  private readonly contentProtectionConfig: IContentProtectionConfig;
23
23
  private readonly keyboardPeripheralsConfig: IKeyboardPeripheralsConfig;
24
+ private resizeWatchSelectors: string[];
24
25
  private readonly getFragmentIds: (href: string) => string[];
25
26
 
26
27
  constructor(
@@ -30,7 +31,8 @@ export class FramePoolManager {
30
31
  injector?: Injector | null,
31
32
  contentProtectionConfig?: IContentProtectionConfig,
32
33
  keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig,
33
- getFragmentIds?: (href: string) => string[]
34
+ getFragmentIds?: (href: string) => string[],
35
+ resizeWatchSelectors: string[] = []
34
36
  ) {
35
37
  this.container = container;
36
38
  this.positions = positions;
@@ -39,6 +41,20 @@ export class FramePoolManager {
39
41
  this.contentProtectionConfig = contentProtectionConfig || {};
40
42
  this.keyboardPeripheralsConfig = keyboardPeripheralsConfig || [];
41
43
  this.getFragmentIds = getFragmentIds ?? (() => []);
44
+ this.resizeWatchSelectors = [...resizeWatchSelectors];
45
+ }
46
+
47
+ addResizeTarget(selector: string): void {
48
+ if (!this.resizeWatchSelectors.includes(selector)) this.resizeWatchSelectors.push(selector);
49
+ // Forward to the whole pool, not just currentFrames: off-screen cached frames
50
+ // (preloaded within the pool's boundary window) must not keep stale state that
51
+ // resurfaces if they're shown again later without being recreated.
52
+ this.pool.forEach(f => f.addResizeTarget(selector));
53
+ }
54
+
55
+ removeResizeTarget(selector: string): void {
56
+ this.resizeWatchSelectors = this.resizeWatchSelectors.filter(s => s !== selector);
57
+ this.pool.forEach(f => f.removeResizeTarget(selector));
42
58
  }
43
59
 
44
60
  async destroy() {
@@ -174,7 +190,7 @@ export class FramePoolManager {
174
190
  }
175
191
 
176
192
  // Create <iframe>
177
- const fm = new FrameManager(this.blobs.get(href)!, this.contentProtectionConfig, this.keyboardPeripheralsConfig, this.getFragmentIds(href));
193
+ const fm = new FrameManager(this.blobs.get(href)!, this.contentProtectionConfig, this.keyboardPeripheralsConfig, this.getFragmentIds(href), this.resizeWatchSelectors);
178
194
  if(href !== newHref) await fm.hide(); // Avoid unecessary hide
179
195
  this.container.appendChild(fm.iframe);
180
196
  await fm.load(modules);
@@ -14,6 +14,8 @@ export class FXLFrameManager {
14
14
  private readonly peripherals: FXLPeripherals;
15
15
  private readonly contentProtectionConfig: IContentProtectionConfig;
16
16
  private readonly keyboardPeripheralsConfig: IKeyboardPeripheralsConfig;
17
+ private resizeWatchSelectors: string[];
18
+ private pendingUnwatchSelectors: string[] = [];
17
19
  private conditionBridge?: KeyboardConditionBridge;
18
20
  private currModules: ModuleName[] = [];
19
21
 
@@ -28,13 +30,15 @@ export class FXLFrameManager {
28
30
  direction: ReadingProgression,
29
31
  debugHref: string,
30
32
  contentProtectionConfig: IContentProtectionConfig = {},
31
- keyboardPeripheralsConfig: IKeyboardPeripheralsConfig = []
33
+ keyboardPeripheralsConfig: IKeyboardPeripheralsConfig = [],
34
+ resizeWatchSelectors: string[] = []
32
35
  ) {
33
36
  this.peripherals = peripherals;
34
37
  this.debugHref = debugHref;
35
38
  // Use the provided content protection config directly without overriding defaults
36
39
  this.contentProtectionConfig = { ...contentProtectionConfig };
37
40
  this.keyboardPeripheralsConfig = [...keyboardPeripheralsConfig];
41
+ this.resizeWatchSelectors = [...resizeWatchSelectors];
38
42
  this.frame = document.createElement("iframe");
39
43
  this.frame.sandbox.value = "allow-same-origin allow-scripts";
40
44
  this.frame.classList.add("readium-navigator-iframe");
@@ -236,6 +240,32 @@ export class FXLFrameManager {
236
240
  if (this.contentProtectionConfig.protectPrinting?.disable) {
237
241
  this.comms!.send("print_protection", this.contentProtectionConfig.protectPrinting);
238
242
  }
243
+
244
+ // Send resize-watch targets
245
+ this.resizeWatchSelectors.forEach(selector => {
246
+ this.comms!.send("decoration_resize", { action: "watch", selector });
247
+ });
248
+
249
+ // Flush unwatches that couldn't be sent while comms was halted
250
+ this.pendingUnwatchSelectors.forEach(selector => {
251
+ this.comms!.send("decoration_resize", { action: "unwatch", selector });
252
+ });
253
+ this.pendingUnwatchSelectors = [];
254
+ }
255
+
256
+ addResizeTarget(selector: string): void {
257
+ if (!this.resizeWatchSelectors.includes(selector)) this.resizeWatchSelectors.push(selector);
258
+ this.pendingUnwatchSelectors = this.pendingUnwatchSelectors.filter(s => s !== selector);
259
+ if (this.comms?.ready) this.comms.send("decoration_resize", { action: "watch", selector });
260
+ }
261
+
262
+ removeResizeTarget(selector: string): void {
263
+ this.resizeWatchSelectors = this.resizeWatchSelectors.filter(s => s !== selector);
264
+ if (this.comms?.ready) {
265
+ this.comms.send("decoration_resize", { action: "unwatch", selector });
266
+ } else if (!this.pendingUnwatchSelectors.includes(selector)) {
267
+ this.pendingUnwatchSelectors.push(selector);
268
+ }
239
269
  }
240
270
 
241
271
  private cachedPage: Page | undefined = undefined;
@@ -30,6 +30,7 @@ export class FXLFramePoolManager {
30
30
  private readonly injector: Injector | null = null;
31
31
  private readonly contentProtectionConfig: IContentProtectionConfig;
32
32
  private readonly keyboardPeripheralsConfig: IKeyboardPeripheralsConfig;
33
+ private resizeWatchSelectors: string[];
33
34
 
34
35
  // NEW
35
36
  private readonly bookElement: HTMLDivElement;
@@ -55,6 +56,7 @@ export class FXLFramePoolManager {
55
56
  injector?: Injector | null,
56
57
  contentProtectionConfig?: IContentProtectionConfig,
57
58
  keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig,
59
+ resizeWatchSelectors: string[] = [],
58
60
  ) {
59
61
  this.container = container;
60
62
  this.positions = positions;
@@ -62,6 +64,7 @@ export class FXLFramePoolManager {
62
64
  this.injector = injector ?? null;
63
65
  this.contentProtectionConfig = contentProtectionConfig || {};
64
66
  this.keyboardPeripheralsConfig = keyboardPeripheralsConfig || [];
67
+ this.resizeWatchSelectors = [...resizeWatchSelectors];
65
68
  this.spreadPresentation = pub.metadata.otherMetadata?.spread || Spread.auto;
66
69
 
67
70
  if(this.pub.metadata.effectiveReadingProgression !== ReadingProgression.rtl && this.pub.metadata.effectiveReadingProgression !== ReadingProgression.ltr)
@@ -88,7 +91,7 @@ export class FXLFramePoolManager {
88
91
 
89
92
  this.pub.readingOrder.items.forEach((link) => {
90
93
  // Create <iframe>
91
- const fm = new FXLFrameManager(this.peripherals, this.pub.metadata.effectiveReadingProgression, link.href, this.contentProtectionConfig, this.keyboardPeripheralsConfig);
94
+ const fm = new FXLFrameManager(this.peripherals, this.pub.metadata.effectiveReadingProgression, link.href, this.contentProtectionConfig, this.keyboardPeripheralsConfig, this.resizeWatchSelectors);
92
95
  this.spineElement.appendChild(fm.element);
93
96
 
94
97
  // this.pages.push(fm);
@@ -595,6 +598,16 @@ export class FXLFramePoolManager {
595
598
  }
596
599
  }
597
600
 
601
+ addResizeTarget(selector: string): void {
602
+ if (!this.resizeWatchSelectors.includes(selector)) this.resizeWatchSelectors.push(selector);
603
+ this.pool.forEach(f => f.addResizeTarget(selector));
604
+ }
605
+
606
+ removeResizeTarget(selector: string): void {
607
+ this.resizeWatchSelectors = this.resizeWatchSelectors.filter(s => s !== selector);
608
+ this.pool.forEach(f => f.removeResizeTarget(selector));
609
+ }
610
+
598
611
  get currentFrames(): (FXLFrameManager | undefined)[] {
599
612
  if(this.perPage < 2) {
600
613
  const link = this.pub.readingOrder.items[this.currentSlide];
@@ -14,6 +14,8 @@ export class WebPubFrameManager {
14
14
  private destroyed: boolean = false;
15
15
  private readonly contentProtectionConfig: IContentProtectionConfig;
16
16
  private readonly keyboardPeripheralsConfig: IKeyboardPeripheralsConfig;
17
+ private resizeWatchSelectors: string[];
18
+ private pendingUnwatchSelectors: string[] = [];
17
19
  private conditionBridge?: KeyboardConditionBridge;
18
20
  private currModules: ModuleName[] = [];
19
21
 
@@ -21,7 +23,8 @@ export class WebPubFrameManager {
21
23
  source: string,
22
24
  contentProtectionConfig: IContentProtectionConfig = {},
23
25
  keyboardPeripheralsConfig: IKeyboardPeripheralsConfig = [],
24
- private readonly timelineFragmentIds: string[] = []
26
+ private readonly timelineFragmentIds: string[] = [],
27
+ resizeWatchSelectors: string[] = []
25
28
  ) {
26
29
  this.frame = document.createElement("iframe");
27
30
  this.frame.classList.add("readium-navigator-iframe");
@@ -38,6 +41,7 @@ export class WebPubFrameManager {
38
41
  // Use the provided content protection config directly without overriding defaults
39
42
  this.contentProtectionConfig = { ...contentProtectionConfig };
40
43
  this.keyboardPeripheralsConfig = [...keyboardPeripheralsConfig];
44
+ this.resizeWatchSelectors = [...resizeWatchSelectors];
41
45
  }
42
46
 
43
47
  async load(modules: ModuleName[] = []): Promise<Window> {
@@ -97,6 +101,32 @@ export class WebPubFrameManager {
97
101
  if (this.contentProtectionConfig.protectPrinting?.disable) {
98
102
  this.comms!.send("print_protection", this.contentProtectionConfig.protectPrinting);
99
103
  }
104
+
105
+ // Send resize-watch targets
106
+ this.resizeWatchSelectors.forEach(selector => {
107
+ this.comms!.send("decoration_resize", { action: "watch", selector });
108
+ });
109
+
110
+ // Flush unwatches that couldn't be sent while comms was halted
111
+ this.pendingUnwatchSelectors.forEach(selector => {
112
+ this.comms!.send("decoration_resize", { action: "unwatch", selector });
113
+ });
114
+ this.pendingUnwatchSelectors = [];
115
+ }
116
+
117
+ addResizeTarget(selector: string): void {
118
+ if (!this.resizeWatchSelectors.includes(selector)) this.resizeWatchSelectors.push(selector);
119
+ this.pendingUnwatchSelectors = this.pendingUnwatchSelectors.filter(s => s !== selector);
120
+ if (this.comms?.ready) this.comms.send("decoration_resize", { action: "watch", selector });
121
+ }
122
+
123
+ removeResizeTarget(selector: string): void {
124
+ this.resizeWatchSelectors = this.resizeWatchSelectors.filter(s => s !== selector);
125
+ if (this.comms?.ready) {
126
+ this.comms.send("decoration_resize", { action: "unwatch", selector });
127
+ } else if (!this.pendingUnwatchSelectors.includes(selector)) {
128
+ this.pendingUnwatchSelectors.push(selector);
129
+ }
100
130
  }
101
131
 
102
132
  async destroy() {
@@ -17,6 +17,7 @@ export class WebPubFramePoolManager {
17
17
  private readonly injector?: Injector | null = null;
18
18
  private readonly contentProtectionConfig: IContentProtectionConfig;
19
19
  private readonly keyboardPeripheralsConfig: IKeyboardPeripheralsConfig;
20
+ private resizeWatchSelectors: string[];
20
21
  private readonly getFragmentIds: (href: string) => string[];
21
22
 
22
23
  constructor(
@@ -25,7 +26,8 @@ export class WebPubFramePoolManager {
25
26
  injector?: Injector | null,
26
27
  contentProtectionConfig: IContentProtectionConfig = {},
27
28
  keyboardPeripheralsConfig: IKeyboardPeripheralsConfig = [],
28
- getFragmentIds?: (href: string) => string[]
29
+ getFragmentIds?: (href: string) => string[],
30
+ resizeWatchSelectors: string[] = []
29
31
  ) {
30
32
  this.container = container;
31
33
  this.currentCssProperties = cssProperties;
@@ -33,6 +35,20 @@ export class WebPubFramePoolManager {
33
35
  this.contentProtectionConfig = contentProtectionConfig;
34
36
  this.keyboardPeripheralsConfig = [...keyboardPeripheralsConfig];
35
37
  this.getFragmentIds = getFragmentIds ?? (() => []);
38
+ this.resizeWatchSelectors = [...resizeWatchSelectors];
39
+ }
40
+
41
+ addResizeTarget(selector: string): void {
42
+ if (!this.resizeWatchSelectors.includes(selector)) this.resizeWatchSelectors.push(selector);
43
+ // Forward to the whole pool, not just currentFrames: off-screen cached frames
44
+ // (preloaded within the pool's boundary window) must not keep stale state that
45
+ // resurfaces if they're shown again later without being recreated.
46
+ this.pool.forEach(f => f.addResizeTarget(selector));
47
+ }
48
+
49
+ removeResizeTarget(selector: string): void {
50
+ this.resizeWatchSelectors = this.resizeWatchSelectors.filter(s => s !== selector);
51
+ this.pool.forEach(f => f.removeResizeTarget(selector));
36
52
  }
37
53
 
38
54
  async destroy() {
@@ -157,7 +173,7 @@ export class WebPubFramePoolManager {
157
173
  this.blobs.set(href, blobURL);
158
174
  }
159
175
 
160
- const fm = new WebPubFrameManager(this.blobs.get(href)!, this.contentProtectionConfig, this.keyboardPeripheralsConfig, this.getFragmentIds(href));
176
+ const fm = new WebPubFrameManager(this.blobs.get(href)!, this.contentProtectionConfig, this.keyboardPeripheralsConfig, this.getFragmentIds(href), this.resizeWatchSelectors);
161
177
  if(href !== newHref) await fm.hide();
162
178
  this.container.appendChild(fm.iframe);
163
179
  await fm.load(modules);
@@ -100,6 +100,7 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
100
100
  private _decorationActivationState: Map<string, boolean> = new Map();
101
101
  private _decorationHoverState: Map<string, boolean> = new Map();
102
102
  private _decorationActivationConsumed = false;
103
+ private _decorationResizeSelectors: Set<string>;
103
104
 
104
105
  private webViewport: VisualNavigatorViewport = {
105
106
  readingOrder: [],
@@ -134,6 +135,7 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
134
135
  // Initialize content protection with provided config or default values
135
136
  this._contentProtection = configuration.contentProtection || {};
136
137
  this._decoratorConfig = configuration.decoratorConfig || {};
138
+ this._decorationResizeSelectors = new Set(this._decoratorConfig.resizeWatchSelectors ?? []);
137
139
 
138
140
  // Merge keyboard peripherals
139
141
  this._keyboardPeripherals = this.mergeKeyboardPeripherals(
@@ -200,7 +202,8 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
200
202
  (href) => this.pub.timeline.segmentsForHref(href)
201
203
  .flatMap(item => item.references)
202
204
  .map(ref => { const h = ref.indexOf('#'); return h >= 0 ? ref.slice(h + 1) : ''; })
203
- .filter(Boolean)
205
+ .filter(Boolean),
206
+ [...this._decorationResizeSelectors]
204
207
  );
205
208
 
206
209
  await this.apply();
@@ -467,6 +470,7 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
467
470
  this._decorationHoveredDecorations.clear();
468
471
  this._decorationActivationState.clear();
469
472
  this._decorationHoverState.clear();
473
+ this._decorationResizeSelectors.clear();
470
474
  }
471
475
 
472
476
  // DecorableNavigator
@@ -520,6 +524,21 @@ export class WebPubNavigator extends VisualNavigator implements Configurable<Web
520
524
  if (frame?.msg) frame.msg.send("decoration_hoverable", { group, hoverable });
521
525
  }
522
526
 
527
+ // Watches an element within the current content document for resize, so decoration
528
+ // overlays relay out when it resizes even if the resource's own document size doesn't
529
+ // change. Forwarded to framePool the same way contentProtectionConfig/
530
+ // keyboardPeripheralsConfig are — applied on every frame show(), not replayed
531
+ // reactively off decoration state.
532
+ public addDecorationResizeTarget(selector: string): void {
533
+ this._decorationResizeSelectors.add(selector);
534
+ this.framePool?.addResizeTarget(selector);
535
+ }
536
+
537
+ public removeDecorationResizeTarget(selector: string): void {
538
+ this._decorationResizeSelectors.delete(selector);
539
+ this.framePool?.removeResizeTarget(selector);
540
+ }
541
+
523
542
  public applyDecorations(decorations: Decoration[], group: string): void {
524
543
  const previous = this._decorations.get(group) ?? [];
525
544
  const prevById = new Map(previous.map(d => [d.id, d]));
@@ -18,4 +18,12 @@ export interface DecorableNavigator {
18
18
  registerDecorationObserver(group: string, observer: DecorationObserver): void;
19
19
  /** Unregisters a previously registered observer from all groups. */
20
20
  unregisterDecorationObserver(observer: DecorationObserver): void;
21
+ /**
22
+ * Watches an element within the current content document for resize, so decoration
23
+ * overlays relay out when it resizes even if the resource's own document size doesn't
24
+ * change (e.g. a sidebar collapsing and the reading pane reflowing inside it).
25
+ */
26
+ addDecorationResizeTarget(selector: string): void;
27
+ /** Stops watching an element previously registered with addDecorationResizeTarget. */
28
+ removeDecorationResizeTarget(selector: string): void;
21
29
  }
@@ -75,6 +75,7 @@ export declare class EpubNavigator extends VisualNavigator implements Configurab
75
75
  private _decorationActivationState;
76
76
  private _decorationHoverState;
77
77
  private _decorationActivationConsumed;
78
+ private _decorationResizeSelectors;
78
79
  private reflowViewport;
79
80
  constructor(container: HTMLElement, pub: Publication, listeners: EpubNavigatorListeners, positions?: Locator[], initialPosition?: Locator | undefined, configuration?: EpubNavigatorConfiguration);
80
81
  static determineLayout(pub: Publication, scroll?: boolean): Layout;
@@ -114,6 +115,8 @@ export declare class EpubNavigator extends VisualNavigator implements Configurab
114
115
  unregisterDecorationObserver(observer: DecorationObserver): void;
115
116
  private _sendDecorationActivationToFrames;
116
117
  private _sendDecorationHoverToFrames;
118
+ addDecorationResizeTarget(selector: string): void;
119
+ removeDecorationResizeTarget(selector: string): void;
117
120
  applyDecorations(decorations: Decoration[], group: string): void;
118
121
  private _sendDecorationOps;
119
122
  private _reapplyDecorationsToFrame;
@@ -11,11 +11,15 @@ export declare class FrameManager {
11
11
  private destroyed;
12
12
  private readonly contentProtectionConfig;
13
13
  private readonly keyboardPeripheralsConfig;
14
+ private resizeWatchSelectors;
15
+ private pendingUnwatchSelectors;
14
16
  private conditionBridge?;
15
17
  private currModules;
16
- constructor(source: string, contentProtectionConfig?: IContentProtectionConfig, keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig, timelineFragmentIds?: string[]);
18
+ constructor(source: string, contentProtectionConfig?: IContentProtectionConfig, keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig, timelineFragmentIds?: string[], resizeWatchSelectors?: string[]);
17
19
  load(modules: ModuleName[]): Promise<Window>;
18
20
  private applyContentProtection;
21
+ addResizeTarget(selector: string): void;
22
+ removeResizeTarget(selector: string): void;
19
23
  destroy(): Promise<void>;
20
24
  hide(): Promise<void>;
21
25
  show(atProgress?: number): Promise<void>;
@@ -16,10 +16,13 @@ export declare class FramePoolManager {
16
16
  private readonly injector;
17
17
  private readonly contentProtectionConfig;
18
18
  private readonly keyboardPeripheralsConfig;
19
+ private resizeWatchSelectors;
19
20
  private readonly getFragmentIds;
20
21
  constructor(container: HTMLElement, positions: Locator[], cssProperties?: {
21
22
  [key: string]: string;
22
- }, injector?: Injector | null, contentProtectionConfig?: IContentProtectionConfig, keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig, getFragmentIds?: (href: string) => string[]);
23
+ }, injector?: Injector | null, contentProtectionConfig?: IContentProtectionConfig, keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig, getFragmentIds?: (href: string) => string[], resizeWatchSelectors?: string[]);
24
+ addResizeTarget(selector: string): void;
25
+ removeResizeTarget(selector: string): void;
23
26
  destroy(): Promise<void>;
24
27
  update(pub: Publication, locator: Locator, modules: ModuleName[], force?: boolean): Promise<void>;
25
28
  setCSSProperties(properties: {
@@ -11,13 +11,15 @@ export declare class FXLFrameManager {
11
11
  private readonly peripherals;
12
12
  private readonly contentProtectionConfig;
13
13
  private readonly keyboardPeripheralsConfig;
14
+ private resizeWatchSelectors;
15
+ private pendingUnwatchSelectors;
14
16
  private conditionBridge?;
15
17
  private currModules;
16
18
  wrapper: HTMLDivElement;
17
19
  debugHref: string;
18
20
  private loadPromise;
19
21
  private showPromise;
20
- constructor(peripherals: FXLPeripherals, direction: ReadingProgression, debugHref: string, contentProtectionConfig?: IContentProtectionConfig, keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig);
22
+ constructor(peripherals: FXLPeripherals, direction: ReadingProgression, debugHref: string, contentProtectionConfig?: IContentProtectionConfig, keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig, resizeWatchSelectors?: string[]);
21
23
  load(modules: ModuleName[], source: string): Promise<Window>;
22
24
  loadPageSize(): {
23
25
  width: number;
@@ -29,6 +31,8 @@ export declare class FXLFrameManager {
29
31
  deselect(): void;
30
32
  unfocus(): Promise<void>;
31
33
  private applyContentProtection;
34
+ addResizeTarget(selector: string): void;
35
+ removeResizeTarget(selector: string): void;
32
36
  private cachedPage;
33
37
  show(page: Page): Promise<void>;
34
38
  activate(): Promise<void>;
@@ -18,6 +18,7 @@ export declare class FXLFramePoolManager {
18
18
  private readonly injector;
19
19
  private readonly contentProtectionConfig;
20
20
  private readonly keyboardPeripheralsConfig;
21
+ private resizeWatchSelectors;
21
22
  private readonly bookElement;
22
23
  readonly spineElement: HTMLDivElement;
23
24
  private readonly pub;
@@ -32,7 +33,7 @@ export declare class FXLFramePoolManager {
32
33
  private containerHeightCached;
33
34
  private resizeTimeout;
34
35
  readonly peripherals: FXLPeripherals;
35
- constructor(container: HTMLElement, positions: Locator[], pub: Publication, injector?: Injector | null, contentProtectionConfig?: IContentProtectionConfig, keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig);
36
+ constructor(container: HTMLElement, positions: Locator[], pub: Publication, injector?: Injector | null, contentProtectionConfig?: IContentProtectionConfig, keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig, resizeWatchSelectors?: string[]);
36
37
  private _listener;
37
38
  set listener(listener: FrameCommsListener);
38
39
  get listener(): FrameCommsListener;
@@ -89,6 +90,8 @@ export declare class FXLFramePoolManager {
89
90
  waitForItem(href: string): Promise<void>;
90
91
  cancelShowing(href: string): Promise<void>;
91
92
  update(pub: Publication, locator: Locator, modules: ModuleName[], _force?: boolean): Promise<void>;
93
+ addResizeTarget(selector: string): void;
94
+ removeResizeTarget(selector: string): void;
92
95
  get currentFrames(): (FXLFrameManager | undefined)[];
93
96
  get currentBounds(): DOMRect;
94
97
  get viewport(): VisualNavigatorViewport;
@@ -11,11 +11,15 @@ export declare class WebPubFrameManager {
11
11
  private destroyed;
12
12
  private readonly contentProtectionConfig;
13
13
  private readonly keyboardPeripheralsConfig;
14
+ private resizeWatchSelectors;
15
+ private pendingUnwatchSelectors;
14
16
  private conditionBridge?;
15
17
  private currModules;
16
- constructor(source: string, contentProtectionConfig?: IContentProtectionConfig, keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig, timelineFragmentIds?: string[]);
18
+ constructor(source: string, contentProtectionConfig?: IContentProtectionConfig, keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig, timelineFragmentIds?: string[], resizeWatchSelectors?: string[]);
17
19
  load(modules?: ModuleName[]): Promise<Window>;
18
20
  private applyContentProtection;
21
+ addResizeTarget(selector: string): void;
22
+ removeResizeTarget(selector: string): void;
19
23
  destroy(): Promise<void>;
20
24
  hide(): Promise<void>;
21
25
  show(atProgress?: number): Promise<void>;
@@ -15,10 +15,13 @@ export declare class WebPubFramePoolManager {
15
15
  private readonly injector?;
16
16
  private readonly contentProtectionConfig;
17
17
  private readonly keyboardPeripheralsConfig;
18
+ private resizeWatchSelectors;
18
19
  private readonly getFragmentIds;
19
20
  constructor(container: HTMLElement, cssProperties?: {
20
21
  [key: string]: string;
21
- }, injector?: Injector | null, contentProtectionConfig?: IContentProtectionConfig, keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig, getFragmentIds?: (href: string) => string[]);
22
+ }, injector?: Injector | null, contentProtectionConfig?: IContentProtectionConfig, keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig, getFragmentIds?: (href: string) => string[], resizeWatchSelectors?: string[]);
23
+ addResizeTarget(selector: string): void;
24
+ removeResizeTarget(selector: string): void;
22
25
  destroy(): Promise<void>;
23
26
  update(pub: Publication, locator: Locator, modules: ModuleName[]): Promise<void>;
24
27
  setCSSProperties(properties: {
@@ -65,6 +65,7 @@ export declare class WebPubNavigator extends VisualNavigator implements Configur
65
65
  private _decorationActivationState;
66
66
  private _decorationHoverState;
67
67
  private _decorationActivationConsumed;
68
+ private _decorationResizeSelectors;
68
69
  private webViewport;
69
70
  constructor(container: HTMLElement, pub: Publication, listeners: WebPubNavigatorListeners, initialPosition?: Locator | undefined, configuration?: WebPubNavigatorConfiguration);
70
71
  load(): Promise<void>;
@@ -91,6 +92,8 @@ export declare class WebPubNavigator extends VisualNavigator implements Configur
91
92
  unregisterDecorationObserver(observer: DecorationObserver): void;
92
93
  private _sendDecorationActivatable;
93
94
  private _sendDecorationHoverable;
95
+ addDecorationResizeTarget(selector: string): void;
96
+ removeDecorationResizeTarget(selector: string): void;
94
97
  applyDecorations(decorations: Decoration[], group: string): void;
95
98
  private _sendDecorationOps;
96
99
  private _reapplyDecorationsToCurrentFrame;