@readium/navigator-html-injectables 2.6.1 → 2.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import{Module as l}from"../Module.js";const i=class i extends l{constructor(){super(...arguments),this.mediaPlayingCount=0,this.allAnimations=new Set}wndOnErr(e){this.comms?.send("error",{message:e.message,filename:e.filename,lineno:e.lineno,colno:e.colno})}unblock(e){for(e._readium_blockEvents=!1;e._readium_blockedEvents?.length>0;){const t=e._readium_blockedEvents.shift();switch(t[0]){case 0:Reflect.apply(t[1],t[2],t[3]);break;case 1:const a=t[1],n=t[2];e.removeEventListener(a.type,e._readium_eventBlocker,!0);const s=new Event(a.type,{bubbles:a.bubbles,cancelable:a.cancelable});n?n.dispatchEvent(s):e.dispatchEvent(s);break}}}onMediaPlayEvent(){this.mediaPlayingCount++,this.comms?.send("media_play",this.mediaPlayingCount)}onMediaPauseEvent(){this.mediaPlayingCount>0&&this.mediaPlayingCount--,this.comms?.send("media_pause",this.mediaPlayingCount)}pauseAllMedia(e){const t=e.document.querySelectorAll("audio,video");for(let a=0;a<t.length;a++)t[a].pause()}mount(e,t){this.comms=t,e.addEventListener("error",this.wndOnErr,!1),Reflect.defineProperty(e.navigator,"epubReadingSystem",{value:{name:"readium-ts-toolkit",version:"2.6.1",hasFeature:(n,s="")=>{switch(n){case"dom-manipulation":return!0;case"layout-changes":return!0;case"touch-events":return!0;case"mouse-events":return!0;case"keyboard-events":return!0;case"spine-scripting":return!0;case"embedded-web-content":return!0;default:return!1}}},writable:!1}),"getAnimations"in e.document&&e.document.getAnimations().forEach(n=>{n.cancel(),this.allAnimations.add(n)}),t.register("activate",i.moduleName,(n,s)=>{this.allAnimations.forEach(r=>{r.cancel(),r.play()}),s(!0)}),t.register("unfocus",i.moduleName,(n,s)=>{this.pauseAllMedia(e),this.allAnimations.forEach(r=>r.pause()),s(!0)});const a=e.document.querySelectorAll("audio,video");for(let n=0;n<a.length;n++){const s=a[n];s.addEventListener("play",this.onMediaPlayEvent,{passive:!0}),s.addEventListener("pause",this.onMediaPauseEvent,{passive:!0})}return t.log("Setup Mounted"),!0}unmount(e,t){return e.removeEventListener("error",this.wndOnErr),e.removeEventListener("play",this.onMediaPlayEvent),e.removeEventListener("pause",this.onMediaPauseEvent),this.allAnimations.forEach(a=>a.cancel()),this.allAnimations.clear(),t.log("Setup Unmounted"),!0}};i.moduleName="setup";let o=i;export{o as Setup};
1
+ import{Module as l}from"../Module.js";const i=class i extends l{constructor(){super(...arguments),this.mediaPlayingCount=0,this.allAnimations=new Set}wndOnErr(e){this.comms?.send("error",{message:e.message,filename:e.filename,lineno:e.lineno,colno:e.colno})}unblock(e){for(e._readium_blockEvents=!1;e._readium_blockedEvents?.length>0;){const t=e._readium_blockedEvents.shift();switch(t[0]){case 0:Reflect.apply(t[1],t[2],t[3]);break;case 1:const a=t[1],n=t[2];e.removeEventListener(a.type,e._readium_eventBlocker,!0);const s=new Event(a.type,{bubbles:a.bubbles,cancelable:a.cancelable});n?n.dispatchEvent(s):e.dispatchEvent(s);break}}}onMediaPlayEvent(){this.mediaPlayingCount++,this.comms?.send("media_play",this.mediaPlayingCount)}onMediaPauseEvent(){this.mediaPlayingCount>0&&this.mediaPlayingCount--,this.comms?.send("media_pause",this.mediaPlayingCount)}pauseAllMedia(e){const t=e.document.querySelectorAll("audio,video");for(let a=0;a<t.length;a++)t[a].pause()}mount(e,t){this.comms=t,e.addEventListener("error",this.wndOnErr,!1),Reflect.defineProperty(e.navigator,"epubReadingSystem",{value:{name:"readium-ts-toolkit",version:"2.6.2",hasFeature:(n,s="")=>{switch(n){case"dom-manipulation":return!0;case"layout-changes":return!0;case"touch-events":return!0;case"mouse-events":return!0;case"keyboard-events":return!0;case"spine-scripting":return!0;case"embedded-web-content":return!0;default:return!1}}},writable:!1}),"getAnimations"in e.document&&e.document.getAnimations().forEach(n=>{n.cancel(),this.allAnimations.add(n)}),t.register("activate",i.moduleName,(n,s)=>{this.allAnimations.forEach(r=>{r.cancel(),r.play()}),s(!0)}),t.register("unfocus",i.moduleName,(n,s)=>{this.pauseAllMedia(e),this.allAnimations.forEach(r=>r.pause()),s(!0)});const a=e.document.querySelectorAll("audio,video");for(let n=0;n<a.length;n++){const s=a[n];s.addEventListener("play",this.onMediaPlayEvent,{passive:!0}),s.addEventListener("pause",this.onMediaPauseEvent,{passive:!0})}return t.log("Setup Mounted"),!0}unmount(e,t){return e.removeEventListener("error",this.wndOnErr),e.removeEventListener("play",this.onMediaPlayEvent),e.removeEventListener("pause",this.onMediaPauseEvent),this.allAnimations.forEach(a=>a.cancel()),this.allAnimations.clear(),t.log("Setup Unmounted"),!0}};i.moduleName="setup";let o=i;export{o as Setup};
@@ -1,4 +1,4 @@
1
- import{LocatorText as u,Locator as m,LocatorLocations as g}from"@readium/shared";import{deselect as a,findFirstVisibleLocator as f}from"../../helpers/dom.js";import{Snapper as p}from"./Snapper.js";import{rangeFromLocator as b}from"../../helpers/locator.js";import{isVerticalLR as L,forceWebkitRecalc as S}from"../../helpers/document.js";import{PatternAnalyzer as v}from"../../protection/PatternAnalyzer.js";import{SCROLL_PROTECTION_CONFIG as y}from"../../protection/config.js";const h="readium-cjk-vertical-snapper-style",l=class l extends p{constructor(){super(...arguments),this.patternAnalyzer=null,this.lastScrollTime=0,this.isScrollProtectionEnabled=!1,this.initialScrollHandled=!1,this.isScrolling=!1,this.lastScrollLeft=0,this.isResizing=!1,this.resizeDebounce=null,this.verticalLR=!1,this.handleScroll=i=>{if(this.comms.ready&&!this.isResizing){if(!this.initialScrollHandled){this.lastScrollLeft=Math.abs(this.doc().scrollLeft),this.initialScrollHandled=!0,this.reportProgress();return}this.isScrolling||(this.isScrolling=!0,this.wnd.requestAnimationFrame(()=>{this.reportProgress();const r=Math.abs(this.doc().scrollLeft),o=r-this.lastScrollLeft;if(this.lastScrollLeft=r,this.isScrollProtectionEnabled&&Math.abs(o)>5){const e=Date.now(),t=e-(this.lastScrollTime||e);if(this.patternAnalyzer&&this.patternAnalyzer.analyze(o>0?"down":"up",Math.abs(o),t)){const n=i.target&&"tagName"in i.target?{tagName:i.target.tagName}:null;this.comms?.send("content_protection",{type:"suspicious_scrolling",timestamp:Date.now(),scrollDelta:o,scrollDirection:o>0?"left":"right",targetElement:n})}this.lastScrollTime=e}this.comms.send("scroll",o),this.isScrolling=!1}))}}}doc(){return this.wnd.document.scrollingElement}scrollable(){return Math.max(0,this.doc().scrollWidth-this.wnd.innerWidth)}hasScrolledPast(i){const r=i.getBoundingClientRect();return this.verticalLR?r.right<=0:r.left>=this.wnd.innerWidth}reportProgress(i){if(!this.comms.ready)return;const r=this.doc().scrollWidth,o=this.wnd.innerWidth,e=Math.max(1,r-o),t=Math.abs(this.doc().scrollLeft),s=Math.max(0,Math.min(1,t/e)),n=Math.max(0,Math.min(1,(t+o)/r));this.comms.send("progress",{start:s,end:n,fragmentId:i!==void 0?i:this.currentTimelineFragment()})}enableScrollProtection(){this.patternAnalyzer||(this.patternAnalyzer=new v(y),this.isScrollProtectionEnabled=!0,this.comms?.log("Scroll protection enabled"))}mount(i,r){this.wnd=i,this.comms=r,this.setupTimelineObserver(),this.initialScrollHandled=!1,this.lastScrollLeft=0,this.isResizing=!1,this.verticalLR=L(i),this.resizeDebounce&&(this.wnd.clearTimeout(this.resizeDebounce),this.resizeDebounce=null),i.navigator.epubReadingSystem&&(i.navigator.epubReadingSystem.layoutStyle="scrolling");const o=i.document.createElement("style");return o.dataset.readium="true",o.id=h,o.textContent=`
1
+ import{LocatorText as m,Locator as g,LocatorLocations as f}from"@readium/shared";import{deselect as a,findFirstVisibleLocator as p}from"../../helpers/dom.js";import{Snapper as h}from"./Snapper.js";import{rangeFromLocator as b}from"../../helpers/locator.js";import{isVerticalLR as L,forceWebkitRecalc as S}from"../../helpers/document.js";import{PatternAnalyzer as v}from"../../protection/PatternAnalyzer.js";import{SCROLL_PROTECTION_CONFIG as y}from"../../protection/config.js";const d="readium-cjk-vertical-snapper-style",l=class l extends h{constructor(){super(...arguments),this.patternAnalyzer=null,this.lastScrollTime=0,this.isScrollProtectionEnabled=!1,this.initialScrollHandled=!1,this.isScrolling=!1,this.lastScrollLeft=0,this.isResizing=!1,this.resizeDebounce=null,this.verticalLR=!1,this.handleScroll=i=>{if(this.comms.ready&&!this.isResizing){if(!this.initialScrollHandled){this.lastScrollLeft=Math.abs(this.doc().scrollLeft),this.initialScrollHandled=!0,this.reportProgress();return}this.isScrolling||(this.isScrolling=!0,this.wnd.requestAnimationFrame(()=>{this.reportProgress();const r=Math.abs(this.doc().scrollLeft),s=r-this.lastScrollLeft;if(this.lastScrollLeft=r,this.isScrollProtectionEnabled&&Math.abs(s)>5){const e=Date.now(),t=e-(this.lastScrollTime||e);if(this.patternAnalyzer&&this.patternAnalyzer.analyze(s>0?"down":"up",Math.abs(s),t)){const n=i.target&&"tagName"in i.target?{tagName:i.target.tagName}:null;this.comms?.send("content_protection",{type:"suspicious_scrolling",timestamp:Date.now(),scrollDelta:s,scrollDirection:s>0?"left":"right",targetElement:n})}this.lastScrollTime=e}this.comms.send("scroll",s),this.isScrolling=!1}))}}}doc(){return this.wnd.document.scrollingElement}scrollable(){return Math.max(0,this.doc().scrollWidth-this.wnd.innerWidth)}hasScrolledPast(i){const r=i.getBoundingClientRect(),s=this.wnd.innerWidth/2,e=this.wnd.innerWidth*h.CENTER_TOLERANCE;return this.verticalLR?r.right<=s+e:r.left>=s-e}reportProgress(i){if(!this.comms.ready)return;const r=this.doc().scrollWidth,s=this.wnd.innerWidth,e=Math.max(1,r-s),t=Math.abs(this.doc().scrollLeft),o=Math.max(0,Math.min(1,t/e)),n=Math.max(0,Math.min(1,(t+s)/r));this.comms.send("progress",{start:o,end:n,fragmentId:i!==void 0?i:this.currentTimelineFragment()})}enableScrollProtection(){this.patternAnalyzer||(this.patternAnalyzer=new v(y),this.isScrollProtectionEnabled=!0,this.comms?.log("Scroll protection enabled"))}mount(i,r){this.wnd=i,this.comms=r,this.setupTimelineObserver("horizontal"),this.initialScrollHandled=!1,this.lastScrollLeft=0,this.isResizing=!1,this.verticalLR=L(i),this.resizeDebounce&&(this.wnd.clearTimeout(this.resizeDebounce),this.resizeDebounce=null),i.navigator.epubReadingSystem&&(i.navigator.epubReadingSystem.layoutStyle="scrolling");const s=i.document.createElement("style");return s.dataset.readium="true",s.id=d,s.textContent=`
2
2
  * {
3
3
  scrollbar-width: none;
4
4
  }
@@ -9,4 +9,4 @@ import{LocatorText as u,Locator as m,LocatorLocations as g}from"@readium/shared"
9
9
  overflow-x: auto !important;
10
10
  overflow-y: hidden !important;
11
11
  }
12
- `,i.document.head.appendChild(o),this.resizeObserver=new ResizeObserver(()=>{this.resizeDebounce&&this.wnd.clearTimeout(this.resizeDebounce),this.isResizing=!0,this.resizeDebounce=this.wnd.setTimeout(()=>{this.isResizing=!1,this.resizeDebounce=null,this.reportProgress()},50)}),this.resizeObserver.observe(i.document.body),i.addEventListener("scroll",this.handleScroll,{passive:!0}),r.register("force_webkit_recalc",l.moduleName,()=>{S(this.wnd);const e=this.doc().scrollLeft;this.verticalLR?this.doc().scrollLeft=e>1?e-1:e+1:this.doc().scrollLeft=e<-1?e+1:e-1,this.doc().scrollLeft=e}),r.register("go_progression",l.moduleName,(e,t)=>{const s=e;if(s<0||s>1){r.send("error",{message:"go_progression must be given a position from 0.0 to 1.0"}),t(!1);return}this.wnd.requestAnimationFrame(()=>{const n=this.scrollable()*s;this.doc().scrollLeft=this.verticalLR?n:-n,this.reportProgress(this.fragmentFromGeometry()),a(this.wnd),t(!0)})}),r.register("go_id",l.moduleName,(e,t)=>{const s=i.document.getElementById(e);if(!s){t(!1);return}this.wnd.requestAnimationFrame(()=>{this.doc().scrollLeft+=s.getBoundingClientRect().left-i.innerWidth/2;const n=e;this.reportProgress(this.timelineEntries.has(n)?n:this.nearestPrecedingTimelineEntry(s)),a(this.wnd),t(!0)})}),r.register("go_text",l.moduleName,(e,t)=>{let s;Array.isArray(e)&&(e.length>1&&(s=e[1]),e=e[0]);const n=u.deserialize(e),c=b(this.wnd.document,new m({href:i.location.href,type:"text/html",text:n,locations:s?new g({otherLocations:new Map([["cssSelector",s]])}):void 0}));if(!c){t(!1);return}this.wnd.requestAnimationFrame(()=>{this.doc().scrollLeft+=c.getBoundingClientRect().left-i.innerWidth/2,this.reportProgress(this.nearestPrecedingTimelineEntry(c.startContainer)),a(this.wnd),t(!0)})}),r.register("go_start",l.moduleName,(e,t)=>{if(this.doc().scrollLeft===0)return t(!1);this.doc().scrollLeft=0,this.reportProgress(this.firstFragmentIfReached()),t(!0)}),r.register("go_end",l.moduleName,(e,t)=>{if(Math.abs(this.doc().scrollLeft)===this.scrollable())return t(!1);this.doc().scrollLeft=this.verticalLR?this.scrollable():-this.scrollable(),this.reportProgress(this.sortedFragmentIds[this.sortedFragmentIds.length-1]),t(!0)}),r.register(["go_next","go_prev"],l.moduleName,(e,t)=>t(!1)),r.register("unfocus",l.moduleName,(e,t)=>{a(this.wnd),t(!0)}),r.register("scroll_protection",l.moduleName,(e,t)=>{this.enableScrollProtection(),t(!0)}),r.register("focus",l.moduleName,(e,t)=>{this.reportProgress(),t(!0)}),r.register("first_visible_locator",l.moduleName,(e,t)=>{const s=f(i,!0);this.comms.send("first_visible_locator",s.serialize()),t(!0)}),r.register("timeline_entries",l.moduleName,(e,t)=>{this.updateTimelineEntries(Array.isArray(e)?e:[],i),t(!0)}),r.log("CJKVerticalSnapper Mounted"),!0}unmount(i,r){return r.unregisterAll(l.moduleName),this.resizeObserver.disconnect(),this.handleScroll&&i.removeEventListener("scroll",this.handleScroll),i.document.getElementById(h)?.remove(),this.patternAnalyzer&&(this.patternAnalyzer.clear(),this.patternAnalyzer=null,this.isScrollProtectionEnabled=!1),this.timelineObserver?.disconnect(),this.timelineObserver=null,this.visibleFragmentIds.clear(),this.timelineEntries.clear(),r.log("CJKVerticalSnapper Unmounted"),!0}};l.moduleName="cjk_vertical_snapper";let d=l;export{d as CJKVerticalSnapper};
12
+ `,i.document.head.appendChild(s),this.resizeObserver=new ResizeObserver(()=>{this.resizeDebounce&&this.wnd.clearTimeout(this.resizeDebounce),this.isResizing=!0,this.resizeDebounce=this.wnd.setTimeout(()=>{this.isResizing=!1,this.resizeDebounce=null,this.reportProgress()},50)}),this.resizeObserver.observe(i.document.body),i.addEventListener("scroll",this.handleScroll,{passive:!0}),r.register("force_webkit_recalc",l.moduleName,()=>{S(this.wnd);const e=this.doc().scrollLeft;this.verticalLR?this.doc().scrollLeft=e>1?e-1:e+1:this.doc().scrollLeft=e<-1?e+1:e-1,this.doc().scrollLeft=e}),r.register("go_progression",l.moduleName,(e,t)=>{const o=e;if(o<0||o>1){r.send("error",{message:"go_progression must be given a position from 0.0 to 1.0"}),t(!1);return}this.wnd.requestAnimationFrame(()=>{const n=this.scrollable()*o;this.doc().scrollLeft=this.verticalLR?n:-n,this.reportProgress(this.fragmentFromGeometry()),a(this.wnd),t(!0)})}),r.register("go_id",l.moduleName,(e,t)=>{const o=i.document.getElementById(e);if(!o){t(!1);return}this.wnd.requestAnimationFrame(()=>{this.doc().scrollLeft+=o.getBoundingClientRect().left-i.innerWidth/2;const n=e;this.reportProgress(this.timelineEntries.has(n)?n:this.nearestPrecedingTimelineEntry(o)),a(this.wnd),t(!0)})}),r.register("go_text",l.moduleName,(e,t)=>{let o;Array.isArray(e)&&(e.length>1&&(o=e[1]),e=e[0]);const n=m.deserialize(e),c=b(this.wnd.document,new g({href:i.location.href,type:"text/html",text:n,locations:o?new f({otherLocations:new Map([["cssSelector",o]])}):void 0}));if(!c){t(!1);return}this.wnd.requestAnimationFrame(()=>{this.doc().scrollLeft+=c.getBoundingClientRect().left-i.innerWidth/2,this.reportProgress(this.nearestPrecedingTimelineEntry(c.startContainer)),a(this.wnd),t(!0)})}),r.register("go_start",l.moduleName,(e,t)=>{if(this.doc().scrollLeft===0)return t(!1);this.doc().scrollLeft=0,this.reportProgress(this.firstFragmentIfReached()),t(!0)}),r.register("go_end",l.moduleName,(e,t)=>{if(Math.abs(this.doc().scrollLeft)===this.scrollable())return t(!1);this.doc().scrollLeft=this.verticalLR?this.scrollable():-this.scrollable(),this.reportProgress(this.sortedFragmentIds[this.sortedFragmentIds.length-1]),t(!0)}),r.register(["go_next","go_prev"],l.moduleName,(e,t)=>t(!1)),r.register("unfocus",l.moduleName,(e,t)=>{a(this.wnd),t(!0)}),r.register("scroll_protection",l.moduleName,(e,t)=>{this.enableScrollProtection(),t(!0)}),r.register("focus",l.moduleName,(e,t)=>{this.reportProgress(),t(!0)}),r.register("first_visible_locator",l.moduleName,(e,t)=>{const o=p(i,!0);this.comms.send("first_visible_locator",o.serialize()),t(!0)}),r.register("timeline_entries",l.moduleName,(e,t)=>{this.updateTimelineEntries(Array.isArray(e)?e:[],i),t(!0)}),r.log("CJKVerticalSnapper Mounted"),!0}unmount(i,r){return r.unregisterAll(l.moduleName),this.resizeObserver.disconnect(),this.handleScroll&&i.removeEventListener("scroll",this.handleScroll),i.document.getElementById(d)?.remove(),this.patternAnalyzer&&(this.patternAnalyzer.clear(),this.patternAnalyzer=null,this.isScrollProtectionEnabled=!1),this.timelineObserver?.disconnect(),this.timelineObserver=null,this.visibleFragmentIds.clear(),this.timelineEntries.clear(),r.log("CJKVerticalSnapper Unmounted"),!0}};l.moduleName="cjk_vertical_snapper";let u=l;export{u as CJKVerticalSnapper};
@@ -1,4 +1,4 @@
1
- import{LocatorText as u,Locator as m,LocatorLocations as g}from"@readium/shared";import{deselect as a,findFirstVisibleLocator as p}from"../../helpers/dom.js";import{Snapper as f}from"./Snapper.js";import{rangeFromLocator as S}from"../../helpers/locator.js";import{forceWebkitRecalc as b}from"../../helpers/document.js";import{PatternAnalyzer as T}from"../../protection/PatternAnalyzer.js";import{SCROLL_PROTECTION_CONFIG as y}from"../../protection/config.js";const h="readium-scroll-snapper-style",o=class o extends f{constructor(){super(...arguments),this.patternAnalyzer=null,this.lastScrollTime=0,this.isScrollProtectionEnabled=!1,this.initialScrollHandled=!1,this.isScrolling=!1,this.lastScrollTop=0,this.isResizing=!1,this.resizeDebounce=null,this.handleScroll=r=>{if(this.comms.ready&&!this.isResizing){if(!this.initialScrollHandled){this.lastScrollTop=this.doc().scrollTop,this.initialScrollHandled=!0,this.reportProgress();return}this.isScrolling||(this.isScrolling=!0,this.wnd.requestAnimationFrame(()=>{this.reportProgress();const s=this.doc().scrollTop,l=s-this.lastScrollTop;if(this.lastScrollTop=s,this.isScrollProtectionEnabled&&Math.abs(l)>5){const e=Date.now(),t=e-(this.lastScrollTime||e);if(this.patternAnalyzer&&this.patternAnalyzer.analyze(l>0?"down":"up",Math.abs(l),t)){const n=r.target&&"tagName"in r.target?{tagName:r.target.tagName}:null;this.comms?.send("content_protection",{type:"suspicious_scrolling",timestamp:Date.now(),scrollDelta:l,scrollDirection:l>0?"down":"up",targetElement:n})}this.lastScrollTime=e}this.comms.send("scroll",l),this.isScrolling=!1}))}}}doc(){return this.wnd.document.scrollingElement}hasScrolledPast(r){return r.getBoundingClientRect().bottom<=0}reportProgress(r){if(!this.comms.ready)return;const s=Math.ceil(this.doc().scrollTop),l=this.doc().scrollHeight,e=this.wnd.innerHeight,t=Math.max(0,Math.min(1,s/l)),i=Math.max(0,Math.min(1,(s+e)/l));this.comms.send("progress",{start:t,end:i,fragmentId:r!==void 0?r:this.currentTimelineFragment()})}enableScrollProtection(){this.patternAnalyzer||(this.patternAnalyzer=new T(y),this.isScrollProtectionEnabled=!0,this.comms?.log("Scroll protection enabled"))}mount(r,s){this.wnd=r,this.comms=s,this.setupTimelineObserver(),this.initialScrollHandled=!1,this.lastScrollTop=0,this.isResizing=!1,this.resizeDebounce&&(this.wnd.clearTimeout(this.resizeDebounce),this.resizeDebounce=null),r.navigator.epubReadingSystem&&(r.navigator.epubReadingSystem.layoutStyle="scrolling");const l=r.document.createElement("style");return l.dataset.readium="true",l.id=h,l.textContent=`
1
+ import{LocatorText as m,Locator as g,LocatorLocations as p}from"@readium/shared";import{deselect as c,findFirstVisibleLocator as f}from"../../helpers/dom.js";import{Snapper as h}from"./Snapper.js";import{rangeFromLocator as S}from"../../helpers/locator.js";import{forceWebkitRecalc as b}from"../../helpers/document.js";import{PatternAnalyzer as T}from"../../protection/PatternAnalyzer.js";import{SCROLL_PROTECTION_CONFIG as y}from"../../protection/config.js";const d="readium-scroll-snapper-style",o=class o extends h{constructor(){super(...arguments),this.patternAnalyzer=null,this.lastScrollTime=0,this.isScrollProtectionEnabled=!1,this.initialScrollHandled=!1,this.isScrolling=!1,this.lastScrollTop=0,this.isResizing=!1,this.resizeDebounce=null,this.handleScroll=r=>{if(this.comms.ready&&!this.isResizing){if(!this.initialScrollHandled){this.lastScrollTop=this.doc().scrollTop,this.initialScrollHandled=!0,this.reportProgress();return}this.isScrolling||(this.isScrolling=!0,this.wnd.requestAnimationFrame(()=>{this.reportProgress();const s=this.doc().scrollTop,l=s-this.lastScrollTop;if(this.lastScrollTop=s,this.isScrollProtectionEnabled&&Math.abs(l)>5){const e=Date.now(),t=e-(this.lastScrollTime||e);if(this.patternAnalyzer&&this.patternAnalyzer.analyze(l>0?"down":"up",Math.abs(l),t)){const n=r.target&&"tagName"in r.target?{tagName:r.target.tagName}:null;this.comms?.send("content_protection",{type:"suspicious_scrolling",timestamp:Date.now(),scrollDelta:l,scrollDirection:l>0?"down":"up",targetElement:n})}this.lastScrollTime=e}this.comms.send("scroll",l),this.isScrolling=!1}))}}}doc(){return this.wnd.document.scrollingElement}hasScrolledPast(r){const s=this.wnd.innerHeight/2;return r.getBoundingClientRect().top<=s+this.wnd.innerHeight*h.CENTER_TOLERANCE}reportProgress(r){if(!this.comms.ready)return;const s=Math.ceil(this.doc().scrollTop),l=this.doc().scrollHeight,e=this.wnd.innerHeight,t=Math.max(0,Math.min(1,s/l)),i=Math.max(0,Math.min(1,(s+e)/l));this.comms.send("progress",{start:t,end:i,fragmentId:r!==void 0?r:this.currentTimelineFragment()})}enableScrollProtection(){this.patternAnalyzer||(this.patternAnalyzer=new T(y),this.isScrollProtectionEnabled=!0,this.comms?.log("Scroll protection enabled"))}mount(r,s){this.wnd=r,this.comms=s,this.setupTimelineObserver(),this.initialScrollHandled=!1,this.lastScrollTop=0,this.isResizing=!1,this.resizeDebounce&&(this.wnd.clearTimeout(this.resizeDebounce),this.resizeDebounce=null),r.navigator.epubReadingSystem&&(r.navigator.epubReadingSystem.layoutStyle="scrolling");const l=r.document.createElement("style");return l.dataset.readium="true",l.id=d,l.textContent=`
2
2
  * {
3
3
  scrollbar-width: none; /* for Firefox */
4
4
  }
@@ -6,4 +6,4 @@ import{LocatorText as u,Locator as m,LocatorLocations as g}from"@readium/shared"
6
6
  body::-webkit-scrollbar {
7
7
  display: none; /* for Chrome, Safari, and Opera */
8
8
  }
9
- `,r.document.head.appendChild(l),this.resizeObserver=new ResizeObserver(()=>{this.resizeDebounce&&this.wnd.clearTimeout(this.resizeDebounce),this.isResizing=!0,this.resizeDebounce=this.wnd.setTimeout(()=>{this.isResizing=!1,this.resizeDebounce=null,this.reportProgress()},50)}),this.resizeObserver.observe(r.document.body),r.addEventListener("scroll",this.handleScroll,{passive:!0}),s.register("force_webkit_recalc",o.moduleName,()=>{b(this.wnd);const e=this.doc().scrollTop;e>1?this.doc().scrollTop=e-1:this.doc().scrollTop=e+1,this.doc().scrollTop=e}),s.register("go_progression",o.moduleName,(e,t)=>{const i=e;if(i<0||i>1){s.send("error",{message:"go_progression must be given a position from 0.0 to 1.0"}),t(!1);return}this.wnd.requestAnimationFrame(()=>{this.doc().scrollTop=this.doc().offsetHeight*i,this.reportProgress(this.fragmentFromGeometry()),a(this.wnd),t(!0)})}),s.register("go_id",o.moduleName,(e,t)=>{const i=r.document.getElementById(e);if(!i){t(!1);return}this.wnd.requestAnimationFrame(()=>{this.doc().scrollTop=i.getBoundingClientRect().top+r.scrollY-r.innerHeight/2;const n=e;this.reportProgress(this.timelineEntries.has(n)?n:this.nearestPrecedingTimelineEntry(i)),a(this.wnd),t(!0)})}),s.register("go_text",o.moduleName,(e,t)=>{let i;Array.isArray(e)&&(e.length>1&&(i=e[1]),e=e[0]);const n=u.deserialize(e),c=S(this.wnd.document,new m({href:r.location.href,type:"text/html",text:n,locations:i?new g({otherLocations:new Map([["cssSelector",i]])}):void 0}));if(!c){t(!1);return}this.wnd.requestAnimationFrame(()=>{this.doc().scrollTop=c.getBoundingClientRect().top+r.scrollY-r.innerHeight/2,this.reportProgress(this.nearestPrecedingTimelineEntry(c.startContainer)),a(this.wnd),t(!0)})}),s.register("go_start",o.moduleName,(e,t)=>{if(this.doc().scrollTop===0)return t(!1);this.doc().scrollTop=0,this.reportProgress(this.firstFragmentIfReached()),t(!0)}),s.register("go_end",o.moduleName,(e,t)=>{if(this.doc().scrollTop===this.doc().scrollHeight-this.doc().offsetHeight)return t(!1);this.doc().scrollTop=this.doc().scrollHeight-this.doc().offsetHeight,this.reportProgress(this.sortedFragmentIds[this.sortedFragmentIds.length-1]),t(!0)}),s.register("unfocus",o.moduleName,(e,t)=>{a(this.wnd),t(!0)}),s.register("scroll_protection",o.moduleName,(e,t)=>{this.enableScrollProtection(),t(!0)}),s.register(["go_next","go_prev"],o.moduleName,(e,t)=>t(!1)),s.register("focus",o.moduleName,(e,t)=>{this.reportProgress(),t(!0)}),s.register("first_visible_locator",o.moduleName,(e,t)=>{const i=p(r,!0);this.comms.send("first_visible_locator",i.serialize()),t(!0)}),s.register("timeline_entries",o.moduleName,(e,t)=>{this.updateTimelineEntries(Array.isArray(e)?e:[],r),t(!0)}),s.log("ScrollSnapper Mounted"),!0}unmount(r,s){return s.unregisterAll(o.moduleName),this.resizeObserver.disconnect(),this.handleScroll&&r.removeEventListener("scroll",this.handleScroll),r.document.getElementById(h)?.remove(),this.timelineObserver?.disconnect(),this.timelineObserver=null,this.visibleFragmentIds.clear(),this.timelineEntries.clear(),this.patternAnalyzer&&(this.patternAnalyzer.clear(),this.patternAnalyzer=null,this.isScrollProtectionEnabled=!1),s.log("ScrollSnapper Unmounted"),!0}};o.moduleName="scroll_snapper";let d=o;export{d as ScrollSnapper};
9
+ `,r.document.head.appendChild(l),this.resizeObserver=new ResizeObserver(()=>{this.resizeDebounce&&this.wnd.clearTimeout(this.resizeDebounce),this.isResizing=!0,this.resizeDebounce=this.wnd.setTimeout(()=>{this.isResizing=!1,this.resizeDebounce=null,this.reportProgress()},50)}),this.resizeObserver.observe(r.document.body),r.addEventListener("scroll",this.handleScroll,{passive:!0}),s.register("force_webkit_recalc",o.moduleName,()=>{b(this.wnd);const e=this.doc().scrollTop;e>1?this.doc().scrollTop=e-1:this.doc().scrollTop=e+1,this.doc().scrollTop=e}),s.register("go_progression",o.moduleName,(e,t)=>{const i=e;if(i<0||i>1){s.send("error",{message:"go_progression must be given a position from 0.0 to 1.0"}),t(!1);return}this.wnd.requestAnimationFrame(()=>{this.doc().scrollTop=this.doc().offsetHeight*i,this.reportProgress(this.fragmentFromGeometry()),c(this.wnd),t(!0)})}),s.register("go_id",o.moduleName,(e,t)=>{const i=r.document.getElementById(e);if(!i){t(!1);return}this.wnd.requestAnimationFrame(()=>{this.doc().scrollTop=i.getBoundingClientRect().top+r.scrollY-r.innerHeight/2;const n=e;this.reportProgress(this.timelineEntries.has(n)?n:this.nearestPrecedingTimelineEntry(i)),c(this.wnd),t(!0)})}),s.register("go_text",o.moduleName,(e,t)=>{let i;Array.isArray(e)&&(e.length>1&&(i=e[1]),e=e[0]);const n=m.deserialize(e),a=S(this.wnd.document,new g({href:r.location.href,type:"text/html",text:n,locations:i?new p({otherLocations:new Map([["cssSelector",i]])}):void 0}));if(!a){t(!1);return}this.wnd.requestAnimationFrame(()=>{this.doc().scrollTop=a.getBoundingClientRect().top+r.scrollY-r.innerHeight/2,this.reportProgress(this.nearestPrecedingTimelineEntry(a.startContainer)),c(this.wnd),t(!0)})}),s.register("go_start",o.moduleName,(e,t)=>{if(this.doc().scrollTop===0)return t(!1);this.doc().scrollTop=0,this.reportProgress(this.firstFragmentIfReached()),t(!0)}),s.register("go_end",o.moduleName,(e,t)=>{if(this.doc().scrollTop===this.doc().scrollHeight-this.doc().offsetHeight)return t(!1);this.doc().scrollTop=this.doc().scrollHeight-this.doc().offsetHeight,this.reportProgress(this.sortedFragmentIds[this.sortedFragmentIds.length-1]),t(!0)}),s.register("unfocus",o.moduleName,(e,t)=>{c(this.wnd),t(!0)}),s.register("scroll_protection",o.moduleName,(e,t)=>{this.enableScrollProtection(),t(!0)}),s.register(["go_next","go_prev"],o.moduleName,(e,t)=>t(!1)),s.register("focus",o.moduleName,(e,t)=>{this.reportProgress(),t(!0)}),s.register("first_visible_locator",o.moduleName,(e,t)=>{const i=f(r,!0);this.comms.send("first_visible_locator",i.serialize()),t(!0)}),s.register("timeline_entries",o.moduleName,(e,t)=>{this.updateTimelineEntries(Array.isArray(e)?e:[],r),t(!0)}),s.log("ScrollSnapper Mounted"),!0}unmount(r,s){return s.unregisterAll(o.moduleName),this.resizeObserver.disconnect(),this.handleScroll&&r.removeEventListener("scroll",this.handleScroll),r.document.getElementById(d)?.remove(),this.timelineObserver?.disconnect(),this.timelineObserver=null,this.visibleFragmentIds.clear(),this.timelineEntries.clear(),this.patternAnalyzer&&(this.patternAnalyzer.clear(),this.patternAnalyzer=null,this.isScrollProtectionEnabled=!1),s.log("ScrollSnapper Unmounted"),!0}};o.moduleName="scroll_snapper";let u=o;export{u as ScrollSnapper};
@@ -1,5 +1,5 @@
1
- import{Module as m}from"../Module.js";const o="readium-snapper-style",n=class n extends m{constructor(){super(...arguments),this.protected=!1,this.timelineObserver=null,this.timelineEntries=new Map,this.visibleFragmentIds=new Set,this.cachedFragmentIds=[],this.sortedFragmentIds=[]}static inDomOrder(e,t,i){const r=e.get(t),s=e.get(i);return!r||!s?0:r.compareDocumentPosition(s)&Node.DOCUMENT_POSITION_FOLLOWING?-1:1}static isVisibleEntry(e){const t=e.boundingClientRect;if(t.width>0&&t.height>0)return e.isIntersecting;const i=e.rootBounds;return i?t.left<=i.right&&t.right>=i.left&&t.top<=i.bottom&&t.bottom>=i.top:e.isIntersecting}setupTimelineObserver(){this.timelineObserver&&this.timelineObserver.disconnect(),this.timelineObserver=new IntersectionObserver(e=>{for(const t of e)n.isVisibleEntry(t)?this.visibleFragmentIds.add(t.target.id):this.visibleFragmentIds.delete(t.target.id)},{threshold:[.01]})}updateTimelineEntries(e,t){this.cachedFragmentIds=e,this.timelineObserver?.disconnect(),this.visibleFragmentIds.clear(),this.timelineEntries.clear();for(const i of e){const r=t.document.getElementById(i);r&&(this.timelineEntries.set(i,r),this.timelineObserver?.observe(r))}this.sortedFragmentIds=Array.from(this.timelineEntries.keys()).sort((i,r)=>n.inDomOrder(this.timelineEntries,i,r))}nearestPrecedingTimelineEntry(e){let t;for(const i of this.sortedFragmentIds){const r=this.timelineEntries.get(i),s=r.compareDocumentPosition(e);if(r===e||r.contains(e)||(s&Node.DOCUMENT_POSITION_FOLLOWING)!==0)t=i;else break}return t}fragmentFromGeometry(){let e;for(const t of this.sortedFragmentIds){const i=this.timelineEntries.get(t);if(this.hasScrolledPast(i))e=t;else break}return e}firstFragmentIfReached(){const e=this.sortedFragmentIds[0];if(e===void 0)return;const t=this.timelineEntries.get(e);return this.hasScrolledPast(t)?e:void 0}currentTimelineFragment(){return this.visibleFragmentIds.size>0?Array.from(this.visibleFragmentIds).sort((e,t)=>n.inDomOrder(this.timelineEntries,e,t))[0]:this.fragmentFromGeometry()}buildStyles(){return`
1
+ import{Module as m}from"../Module.js";const o="readium-snapper-style",r=class r extends m{constructor(){super(...arguments),this.protected=!1,this.timelineObserver=null,this.timelineEntries=new Map,this.visibleFragmentIds=new Set,this.cachedFragmentIds=[],this.sortedFragmentIds=[]}static inDomOrder(t,e,i){const s=t.get(e),n=t.get(i);return!s||!n?0:s.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING?-1:1}static isVisibleEntry(t){const e=t.boundingClientRect;if(e.width>0&&e.height>0)return t.isIntersecting;const i=t.rootBounds;return i?e.left<=i.right&&e.right>=i.left&&e.top<=i.bottom&&e.bottom>=i.top:t.isIntersecting}setupTimelineObserver(t="vertical"){this.timelineObserver&&this.timelineObserver.disconnect();const e=`-${50-r.CENTER_TOLERANCE*100}%`;this.timelineObserver=new IntersectionObserver(i=>{for(const s of i)r.isVisibleEntry(s)?this.visibleFragmentIds.add(s.target.id):this.visibleFragmentIds.delete(s.target.id)},{threshold:[.01],rootMargin:t==="vertical"?`${e} 0px ${e} 0px`:`0px ${e} 0px ${e}`})}updateTimelineEntries(t,e){this.cachedFragmentIds=t,this.timelineObserver?.disconnect(),this.visibleFragmentIds.clear(),this.timelineEntries.clear();for(const i of t){const s=e.document.getElementById(i);s&&(this.timelineEntries.set(i,s),this.timelineObserver?.observe(s))}this.sortedFragmentIds=Array.from(this.timelineEntries.keys()).sort((i,s)=>r.inDomOrder(this.timelineEntries,i,s))}nearestPrecedingTimelineEntry(t){let e;for(const i of this.sortedFragmentIds){const s=this.timelineEntries.get(i),n=s.compareDocumentPosition(t);if(s===t||s.contains(t)||(n&Node.DOCUMENT_POSITION_FOLLOWING)!==0)e=i;else break}return e}fragmentFromGeometry(){let t;for(const e of this.sortedFragmentIds){const i=this.timelineEntries.get(e);if(this.hasScrolledPast(i))t=e;else break}return t}firstFragmentIfReached(){const t=this.sortedFragmentIds[0];if(t===void 0)return;const e=this.timelineEntries.get(t);return this.hasScrolledPast(e)?t:void 0}currentTimelineFragment(){return this.visibleFragmentIds.size>0?Array.from(this.visibleFragmentIds).sort((t,e)=>r.inDomOrder(this.timelineEntries,t,e))[0]:this.fragmentFromGeometry()}buildStyles(){return`
2
2
  html, body {
3
3
  touch-action: manipulation;
4
4
  user-select: ${this.protected?"none":"auto"};
5
- }`}mount(e,t){const i=e.document.createElement("style");return i.dataset.readium="true",i.id=o,i.textContent=this.buildStyles(),e.document.head.appendChild(i),t.register("protect",n.moduleName,(r,s)=>{this.protected=!0,i.textContent=this.buildStyles(),s(!0)}),t.register("unprotect",n.moduleName,(r,s)=>{this.protected=!1,i.textContent=this.buildStyles(),s(!0)}),t.log("Snapper Mounted"),!0}unmount(e,t){return e.document.getElementById(o)?.remove(),t.log("Snapper Unmounted"),!0}};n.moduleName="snapper";let l=n;export{l as Snapper};
5
+ }`}mount(t,e){const i=t.document.createElement("style");return i.dataset.readium="true",i.id=o,i.textContent=this.buildStyles(),t.document.head.appendChild(i),e.register("protect",r.moduleName,(s,n)=>{this.protected=!0,i.textContent=this.buildStyles(),n(!0)}),e.register("unprotect",r.moduleName,(s,n)=>{this.protected=!1,i.textContent=this.buildStyles(),n(!0)}),e.log("Snapper Mounted"),!0}unmount(t,e){return t.document.getElementById(o)?.remove(),e.log("Snapper Unmounted"),!0}};r.moduleName="snapper",r.CENTER_TOLERANCE=.01;let l=r;export{l as Snapper};
@@ -1 +1 @@
1
- import{LocatorText as h,Locator as u,LocatorLocations as d}from"@readium/shared";import{deselect as c,findFirstVisibleLocator as g}from"../../helpers/dom.js";import{Snapper as m}from"./Snapper.js";import{rangeFromLocator as p}from"../../helpers/locator.js";import{forceWebkitRecalc as f}from"../../helpers/document.js";import{PatternAnalyzer as b}from"../../protection/PatternAnalyzer.js";import{SCROLL_PROTECTION_CONFIG as S}from"../../protection/config.js";const o=class o extends m{constructor(){super(...arguments),this.patternAnalyzer=null,this.lastScrollTime=0,this.isScrollProtectionEnabled=!1,this.initialScrollHandled=!1,this.isScrolling=!1,this.lastScrollTop=0,this.isResizing=!1,this.resizeDebounce=null,this.handleScroll=s=>{if(this.comms.ready&&!this.isResizing){if(!this.initialScrollHandled){this.lastScrollTop=this.doc().scrollTop,this.initialScrollHandled=!0,this.reportProgress();return}this.isScrolling||(this.isScrolling=!0,this.wnd.requestAnimationFrame(()=>{this.reportProgress();const r=this.doc().scrollTop,e=r-this.lastScrollTop;if(this.lastScrollTop=r,this.isScrollProtectionEnabled&&Math.abs(e)>5){const t=Date.now(),i=t-(this.lastScrollTime||t);if(this.patternAnalyzer&&this.patternAnalyzer.analyze(e>0?"down":"up",Math.abs(e),i)){const n=s.target&&"tagName"in s.target?{tagName:s.target.tagName}:null;this.comms?.send("content_protection",{type:"suspicious_scrolling",timestamp:Date.now(),scrollDelta:e,scrollDirection:e>0?"down":"up",targetElement:n})}this.lastScrollTime=t}this.comms.send("scroll",e),this.isScrolling=!1}))}}}doc(){return this.wnd.document.scrollingElement}hasScrolledPast(s){return s.getBoundingClientRect().bottom<=0}reportProgress(s){if(!this.comms.ready)return;const r=Math.ceil(this.doc().scrollTop),e=this.doc().scrollHeight,t=this.wnd.innerHeight,i=Math.max(0,Math.min(1,r/e)),l=Math.max(0,Math.min(1,(r+t)/e));this.comms.send("progress",{start:i,end:l,fragmentId:s!==void 0?s:this.currentTimelineFragment()})}enableScrollProtection(){this.patternAnalyzer||(this.patternAnalyzer=new b(S),this.isScrollProtectionEnabled=!0,this.comms?.log("Scroll protection enabled"))}mount(s,r){return this.wnd=s,this.comms=r,this.setupTimelineObserver(),this.initialScrollHandled=!1,this.lastScrollTop=0,this.isResizing=!1,this.resizeDebounce&&(this.wnd.clearTimeout(this.resizeDebounce),this.resizeDebounce=null),this.resizeObserver=new ResizeObserver(()=>{this.resizeDebounce&&this.wnd.clearTimeout(this.resizeDebounce),this.isResizing=!0,this.resizeDebounce=this.wnd.setTimeout(()=>{this.isResizing=!1,this.resizeDebounce=null,this.reportProgress()},50)}),this.resizeObserver.observe(s.document.body),s.addEventListener("scroll",this.handleScroll,{passive:!0}),r.register("force_webkit_recalc",o.moduleName,()=>{f(this.wnd);const e=this.doc().scrollTop;e>1?this.doc().scrollTop=e-1:this.doc().scrollTop=e+1,this.doc().scrollTop=e}),r.register("go_progression",o.moduleName,(e,t)=>{const i=e;if(i<0||i>1){r.send("error",{message:"go_progression must be given a position from 0.0 to 1.0"}),t(!1);return}this.wnd.requestAnimationFrame(()=>{this.doc().scrollTop=this.doc().offsetHeight*i,this.reportProgress(this.fragmentFromGeometry()),c(this.wnd),t(!0)})}),r.register("go_id",o.moduleName,(e,t)=>{const i=s.document.getElementById(e);if(!i){t(!1);return}this.wnd.requestAnimationFrame(()=>{this.doc().scrollTop=i.getBoundingClientRect().top+s.scrollY-s.innerHeight/2;const l=e;this.reportProgress(this.timelineEntries.has(l)?l:this.nearestPrecedingTimelineEntry(i)),c(this.wnd),t(!0)})}),r.register("go_text",o.moduleName,(e,t)=>{let i;Array.isArray(e)&&(e.length>1&&(i=e[1]),e=e[0]);const l=h.deserialize(e),n=p(this.wnd.document,new u({href:s.location.href,type:"text/html",text:l,locations:i?new d({otherLocations:new Map([["cssSelector",i]])}):void 0}));if(!n){t(!1);return}this.wnd.requestAnimationFrame(()=>{this.doc().scrollTop=n.getBoundingClientRect().top+s.scrollY-s.innerHeight/2,this.reportProgress(this.nearestPrecedingTimelineEntry(n.startContainer)),c(this.wnd),t(!0)})}),r.register("go_start",o.moduleName,(e,t)=>{if(this.doc().scrollTop===0)return t(!1);this.doc().scrollTop=0,this.reportProgress(this.firstFragmentIfReached()),t(!0)}),r.register("go_end",o.moduleName,(e,t)=>{if(this.doc().scrollTop===this.doc().scrollHeight-this.doc().offsetHeight)return t(!1);this.doc().scrollTop=this.doc().scrollHeight-this.doc().offsetHeight,this.reportProgress(this.sortedFragmentIds[this.sortedFragmentIds.length-1]),t(!0)}),r.register("unfocus",o.moduleName,(e,t)=>{c(this.wnd),t(!0)}),r.register("scroll_protection",o.moduleName,(e,t)=>{this.enableScrollProtection(),t(!0)}),r.register(["go_next","go_prev"],o.moduleName,(e,t)=>t(!1)),r.register("focus",o.moduleName,(e,t)=>{this.reportProgress(),t(!0)}),r.register("first_visible_locator",o.moduleName,(e,t)=>{const i=g(s,!0);r.send("first_visible_locator",i.serialize()),t(!0)}),r.register("timeline_entries",o.moduleName,(e,t)=>{this.updateTimelineEntries(Array.isArray(e)?e:[],s),t(!0)}),r.log("WebPubSnapper Mounted"),!0}unmount(s,r){return r.unregisterAll(o.moduleName),this.resizeObserver.disconnect(),this.handleScroll&&s.removeEventListener("scroll",this.handleScroll),this.patternAnalyzer&&(this.patternAnalyzer.clear(),this.patternAnalyzer=null,this.isScrollProtectionEnabled=!1),this.timelineObserver?.disconnect(),this.timelineObserver=null,this.visibleFragmentIds.clear(),this.timelineEntries.clear(),r.log("WebPubSnapper Unmounted"),!0}};o.moduleName="webpub_snapper";let a=o;export{a as WebPubSnapper};
1
+ import{LocatorText as u,Locator as d,LocatorLocations as g}from"@readium/shared";import{deselect as c,findFirstVisibleLocator as m}from"../../helpers/dom.js";import{Snapper as h}from"./Snapper.js";import{rangeFromLocator as p}from"../../helpers/locator.js";import{forceWebkitRecalc as f}from"../../helpers/document.js";import{PatternAnalyzer as T}from"../../protection/PatternAnalyzer.js";import{SCROLL_PROTECTION_CONFIG as S}from"../../protection/config.js";const o=class o extends h{constructor(){super(...arguments),this.patternAnalyzer=null,this.lastScrollTime=0,this.isScrollProtectionEnabled=!1,this.initialScrollHandled=!1,this.isScrolling=!1,this.lastScrollTop=0,this.isResizing=!1,this.resizeDebounce=null,this.handleScroll=i=>{if(this.comms.ready&&!this.isResizing){if(!this.initialScrollHandled){this.lastScrollTop=this.doc().scrollTop,this.initialScrollHandled=!0,this.reportProgress();return}this.isScrolling||(this.isScrolling=!0,this.wnd.requestAnimationFrame(()=>{this.reportProgress();const r=this.doc().scrollTop,e=r-this.lastScrollTop;if(this.lastScrollTop=r,this.isScrollProtectionEnabled&&Math.abs(e)>5){const t=Date.now(),s=t-(this.lastScrollTime||t);if(this.patternAnalyzer&&this.patternAnalyzer.analyze(e>0?"down":"up",Math.abs(e),s)){const n=i.target&&"tagName"in i.target?{tagName:i.target.tagName}:null;this.comms?.send("content_protection",{type:"suspicious_scrolling",timestamp:Date.now(),scrollDelta:e,scrollDirection:e>0?"down":"up",targetElement:n})}this.lastScrollTime=t}this.comms.send("scroll",e),this.isScrolling=!1}))}}}doc(){return this.wnd.document.scrollingElement}hasScrolledPast(i){const r=this.wnd.innerHeight/2;return i.getBoundingClientRect().top<=r+this.wnd.innerHeight*h.CENTER_TOLERANCE}reportProgress(i){if(!this.comms.ready)return;const r=Math.ceil(this.doc().scrollTop),e=this.doc().scrollHeight,t=this.wnd.innerHeight,s=Math.max(0,Math.min(1,r/e)),l=Math.max(0,Math.min(1,(r+t)/e));this.comms.send("progress",{start:s,end:l,fragmentId:i!==void 0?i:this.currentTimelineFragment()})}enableScrollProtection(){this.patternAnalyzer||(this.patternAnalyzer=new T(S),this.isScrollProtectionEnabled=!0,this.comms?.log("Scroll protection enabled"))}mount(i,r){return this.wnd=i,this.comms=r,this.setupTimelineObserver(),this.initialScrollHandled=!1,this.lastScrollTop=0,this.isResizing=!1,this.resizeDebounce&&(this.wnd.clearTimeout(this.resizeDebounce),this.resizeDebounce=null),this.resizeObserver=new ResizeObserver(()=>{this.resizeDebounce&&this.wnd.clearTimeout(this.resizeDebounce),this.isResizing=!0,this.resizeDebounce=this.wnd.setTimeout(()=>{this.isResizing=!1,this.resizeDebounce=null,this.reportProgress()},50)}),this.resizeObserver.observe(i.document.body),i.addEventListener("scroll",this.handleScroll,{passive:!0}),r.register("force_webkit_recalc",o.moduleName,()=>{f(this.wnd);const e=this.doc().scrollTop;e>1?this.doc().scrollTop=e-1:this.doc().scrollTop=e+1,this.doc().scrollTop=e}),r.register("go_progression",o.moduleName,(e,t)=>{const s=e;if(s<0||s>1){r.send("error",{message:"go_progression must be given a position from 0.0 to 1.0"}),t(!1);return}this.wnd.requestAnimationFrame(()=>{this.doc().scrollTop=this.doc().offsetHeight*s,this.reportProgress(this.fragmentFromGeometry()),c(this.wnd),t(!0)})}),r.register("go_id",o.moduleName,(e,t)=>{const s=i.document.getElementById(e);if(!s){t(!1);return}this.wnd.requestAnimationFrame(()=>{this.doc().scrollTop=s.getBoundingClientRect().top+i.scrollY-i.innerHeight/2;const l=e;this.reportProgress(this.timelineEntries.has(l)?l:this.nearestPrecedingTimelineEntry(s)),c(this.wnd),t(!0)})}),r.register("go_text",o.moduleName,(e,t)=>{let s;Array.isArray(e)&&(e.length>1&&(s=e[1]),e=e[0]);const l=u.deserialize(e),n=p(this.wnd.document,new d({href:i.location.href,type:"text/html",text:l,locations:s?new g({otherLocations:new Map([["cssSelector",s]])}):void 0}));if(!n){t(!1);return}this.wnd.requestAnimationFrame(()=>{this.doc().scrollTop=n.getBoundingClientRect().top+i.scrollY-i.innerHeight/2,this.reportProgress(this.nearestPrecedingTimelineEntry(n.startContainer)),c(this.wnd),t(!0)})}),r.register("go_start",o.moduleName,(e,t)=>{if(this.doc().scrollTop===0)return t(!1);this.doc().scrollTop=0,this.reportProgress(this.firstFragmentIfReached()),t(!0)}),r.register("go_end",o.moduleName,(e,t)=>{if(this.doc().scrollTop===this.doc().scrollHeight-this.doc().offsetHeight)return t(!1);this.doc().scrollTop=this.doc().scrollHeight-this.doc().offsetHeight,this.reportProgress(this.sortedFragmentIds[this.sortedFragmentIds.length-1]),t(!0)}),r.register("unfocus",o.moduleName,(e,t)=>{c(this.wnd),t(!0)}),r.register("scroll_protection",o.moduleName,(e,t)=>{this.enableScrollProtection(),t(!0)}),r.register(["go_next","go_prev"],o.moduleName,(e,t)=>t(!1)),r.register("focus",o.moduleName,(e,t)=>{this.reportProgress(),t(!0)}),r.register("first_visible_locator",o.moduleName,(e,t)=>{const s=m(i,!0);r.send("first_visible_locator",s.serialize()),t(!0)}),r.register("timeline_entries",o.moduleName,(e,t)=>{this.updateTimelineEntries(Array.isArray(e)?e:[],i),t(!0)}),r.log("WebPubSnapper Mounted"),!0}unmount(i,r){return r.unregisterAll(o.moduleName),this.resizeObserver.disconnect(),this.handleScroll&&i.removeEventListener("scroll",this.handleScroll),this.patternAnalyzer&&(this.patternAnalyzer.clear(),this.patternAnalyzer=null,this.isScrollProtectionEnabled=!1),this.timelineObserver?.disconnect(),this.timelineObserver=null,this.visibleFragmentIds.clear(),this.timelineEntries.clear(),r.log("WebPubSnapper Unmounted"),!0}};o.moduleName="webpub_snapper";let a=o;export{a as WebPubSnapper};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@readium/navigator-html-injectables",
3
- "version": "2.6.1",
3
+ "version": "2.6.2",
4
4
  "type": "module",
5
5
  "description": "An embeddable solution for connecting frames of HTML publications with a Readium Navigator",
6
6
  "author": "readium",
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@readium/helpers": "1.0.0",
48
- "@readium/shared": "2.3.0"
48
+ "@readium/shared": "2.3.2"
49
49
  },
50
50
  "devDependencies": {
51
51
  "css-selector-generator": "^3.8.0",
@@ -69,10 +69,16 @@ export class CJKVerticalSnapper extends Snapper {
69
69
  }
70
70
 
71
71
  protected hasScrolledPast(el: Element): boolean {
72
+ // go_id/go_text center the target in the viewport, not scroll it to the edge —
73
+ // so "reached" has to mean "crossed the viewport's horizontal center", the same
74
+ // line navigation scrolls to, not the leading edge. The tolerance matches
75
+ // setupTimelineObserver's rootMargin so both agree even with scrollLeft rounding.
72
76
  const rect = el.getBoundingClientRect();
73
- // vertical-rl: content flows right→left; leading edge is right, scrolled past when off-screen right.
74
- // vertical-lr: content flows left→right; leading edge is left, scrolled past when off-screen left.
75
- return this.verticalLR ? rect.right <= 0 : rect.left >= this.wnd.innerWidth;
77
+ const center = this.wnd.innerWidth / 2;
78
+ const tolerance = this.wnd.innerWidth * Snapper.CENTER_TOLERANCE;
79
+ // vertical-rl: content flows right→left; leading edge is right, scrolled past when past center.
80
+ // vertical-lr: content flows left→right; leading edge is left, scrolled past when past center.
81
+ return this.verticalLR ? rect.right <= center + tolerance : rect.left >= center - tolerance;
76
82
  }
77
83
 
78
84
  private reportProgress(forcedFragmentId?: string) {
@@ -158,7 +164,7 @@ export class CJKVerticalSnapper extends Snapper {
158
164
  mount(wnd: ReadiumWindow, comms: Comms): boolean {
159
165
  this.wnd = wnd;
160
166
  this.comms = comms;
161
- this.setupTimelineObserver();
167
+ this.setupTimelineObserver("horizontal");
162
168
 
163
169
  this.initialScrollHandled = false;
164
170
  this.lastScrollLeft = 0;
@@ -38,7 +38,12 @@ export class ScrollSnapper extends Snapper {
38
38
  }
39
39
 
40
40
  protected hasScrolledPast(el: Element): boolean {
41
- return el.getBoundingClientRect().bottom <= 0;
41
+ // go_id/go_text center the target in the viewport, not scroll it to the top —
42
+ // so "reached" has to mean "crossed the viewport's vertical center", the same
43
+ // line navigation scrolls to, not the top/bottom edge. The tolerance matches
44
+ // setupTimelineObserver's rootMargin so both agree even with scrollTop rounding.
45
+ const center = this.wnd.innerHeight / 2;
46
+ return el.getBoundingClientRect().top <= center + this.wnd.innerHeight * Snapper.CENTER_TOLERANCE;
42
47
  }
43
48
 
44
49
  private reportProgress(forcedFragmentId?: string) {
@@ -8,6 +8,11 @@ const SNAPPER_STYLE_ID = "readium-snapper-style";
8
8
  export abstract class Snapper extends Module {
9
9
  static readonly moduleName: ModuleName = "snapper";
10
10
 
11
+ // Shared by the observer's rootMargin and every hasScrolledPast implementation so both
12
+ // stay pinned to the exact same boundary. Sized to comfortably exceed realistic
13
+ // scrollTop-rounding error (~1px) while still reading as "the center", not a wide band.
14
+ protected static readonly CENTER_TOLERANCE = 0.01;
15
+
11
16
  private protected = false;
12
17
 
13
18
  // Timeline fragment tracking
@@ -45,8 +50,17 @@ export abstract class Snapper extends Module {
45
50
  rect.top <= root.bottom && rect.bottom >= root.top;
46
51
  }
47
52
 
48
- protected setupTimelineObserver(): void {
53
+ /**
54
+ * Collapses the observer's root to a thin band around the same center line
55
+ * `hasScrolledPast` tests against (go_id/go_text navigate by centering the target, not by
56
+ * aligning it to an edge), so "intersecting" and "scrolled past" can't disagree about
57
+ * where "current" is. A literal zero-height root would make every intersection area zero
58
+ * — isIntersecting would never fire — so CENTER_TOLERANCE leaves it a real, if thin, band.
59
+ * Percentage-based margins track viewport resizes automatically, no re-setup needed.
60
+ */
61
+ protected setupTimelineObserver(axis: "vertical" | "horizontal" = "vertical"): void {
49
62
  if (this.timelineObserver) this.timelineObserver.disconnect();
63
+ const inset = `-${50 - Snapper.CENTER_TOLERANCE * 100}%`;
50
64
  this.timelineObserver = new IntersectionObserver(
51
65
  (entries) => {
52
66
  for (const entry of entries) {
@@ -56,7 +70,10 @@ export abstract class Snapper extends Module {
56
70
  this.visibleFragmentIds.delete((entry.target as HTMLElement).id);
57
71
  }
58
72
  },
59
- { threshold: [0.01] }
73
+ {
74
+ threshold: [0.01],
75
+ rootMargin: axis === "vertical" ? `${inset} 0px ${inset} 0px` : `0px ${inset} 0px ${inset}`
76
+ }
60
77
  );
61
78
  }
62
79
 
@@ -32,7 +32,12 @@ export class WebPubSnapper extends Snapper {
32
32
  }
33
33
 
34
34
  protected hasScrolledPast(el: Element): boolean {
35
- return el.getBoundingClientRect().bottom <= 0;
35
+ // go_id/go_text center the target in the viewport, not scroll it to the top —
36
+ // so "reached" has to mean "crossed the viewport's vertical center", the same
37
+ // line navigation scrolls to, not the top/bottom edge. The tolerance matches
38
+ // setupTimelineObserver's rootMargin so both agree even with scrollTop rounding.
39
+ const center = this.wnd.innerHeight / 2;
40
+ return el.getBoundingClientRect().top <= center + this.wnd.innerHeight * Snapper.CENTER_TOLERANCE;
36
41
  }
37
42
 
38
43
  private reportProgress(forcedFragmentId?: string) {
@@ -4,6 +4,7 @@ import { Module } from "../Module.ts";
4
4
  import { ModuleName } from "../ModuleLibrary.ts";
5
5
  export declare abstract class Snapper extends Module {
6
6
  static readonly moduleName: ModuleName;
7
+ protected static readonly CENTER_TOLERANCE = 0.01;
7
8
  private protected;
8
9
  protected timelineObserver: IntersectionObserver | null;
9
10
  protected timelineEntries: Map<string, Element>;
@@ -18,7 +19,15 @@ export declare abstract class Snapper extends Module {
18
19
  * containment check against `rootBounds` instead of trusting `isIntersecting`.
19
20
  */
20
21
  private static isVisibleEntry;
21
- protected setupTimelineObserver(): void;
22
+ /**
23
+ * Collapses the observer's root to a thin band around the same center line
24
+ * `hasScrolledPast` tests against (go_id/go_text navigate by centering the target, not by
25
+ * aligning it to an edge), so "intersecting" and "scrolled past" can't disagree about
26
+ * where "current" is. A literal zero-height root would make every intersection area zero
27
+ * — isIntersecting would never fire — so CENTER_TOLERANCE leaves it a real, if thin, band.
28
+ * Percentage-based margins track viewport resizes automatically, no re-setup needed.
29
+ */
30
+ protected setupTimelineObserver(axis?: "vertical" | "horizontal"): void;
22
31
  /**
23
32
  * Populates `timelineEntries`/`sortedFragmentIds` from a fresh id list, and
24
33
  * (re)starts IntersectionObserver-based visibility tracking for subclasses that use it.