@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.
- package/dist/modules/setup/Setup.js +1 -1
- package/dist/modules/snapper/CJKVerticalSnapper.js +2 -2
- package/dist/modules/snapper/ScrollSnapper.js +2 -2
- package/dist/modules/snapper/Snapper.js +2 -2
- package/dist/modules/snapper/WebPubSnapper.js +1 -1
- package/package.json +2 -2
- package/src/modules/snapper/CJKVerticalSnapper.ts +10 -4
- package/src/modules/snapper/ScrollSnapper.ts +6 -1
- package/src/modules/snapper/Snapper.ts +19 -2
- package/src/modules/snapper/WebPubSnapper.ts +6 -1
- package/types/src/modules/snapper/Snapper.d.ts +10 -1
|
@@ -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
|
+
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
|
|
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(
|
|
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
|
|
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()),
|
|
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",
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
{
|
|
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
|
-
|
|
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
|
-
|
|
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.
|