@readium/navigator-html-injectables 2.8.2 → 2.8.4

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
- const l=new Set(["backgroundColor","textColor","linkColor","visitedColor","primaryColor","secondaryColor","selectionBackgroundColor","selectionTextColor","blendFilter","darkenFilter","invertFilter","invertGaiji"]),s=/--(?:USER|RS)__([\w-]+)/g;function f(e,t){const r=e??"",o=t??"",n=new Set;for(const c of r.matchAll(s))n.add(c[1]);for(const c of o.matchAll(s))n.add(c[1]);for(const c of n)if(!l.has(c))return!0;return!1}function i(e){const t={},r=e.document.documentElement.style;for(const o in e.document.documentElement.style)Object.hasOwn(r,o)&&!Number.isNaN(Number.parseInt(o))&&(t[r[o]]=r.getPropertyValue(r[o]));return t}function m(e,t){const r=i(e);Object.keys(r).forEach(o=>{t.hasOwnProperty(o)||a(e,o)}),Object.entries(t).forEach(([o,n])=>{r[o]!==n&&u(e,o,n)})}function d(e,t){return e.document.documentElement.style.getPropertyValue(t)}function u(e,t,r){e.document.documentElement.style.setProperty(t,r)}function a(e,t){e.document.documentElement.style.removeProperty(t)}export{i as getProperties,d as getProperty,a as removeProperty,u as setProperty,f as styleChangeAffectsLayout,m as updateProperties};
1
+ const a=()=>typeof window.CSSTransformValue<"u",l=new Set(["backgroundColor","textColor","linkColor","visitedColor","primaryColor","secondaryColor","selectionBackgroundColor","selectionTextColor","blendFilter","darkenFilter","invertFilter","invertGaiji"]),s=/--(?:USER|RS)__([\w-]+)/g;function m(e,t){const r=e??"",o=t??"",n=new Set;for(const c of r.matchAll(s))n.add(c[1]);for(const c of o.matchAll(s))n.add(c[1]);for(const c of n)if(!l.has(c))return!0;return!1}function i(e){const t={},r=e.document.documentElement.style;for(const o in e.document.documentElement.style)Object.hasOwn(r,o)&&!Number.isNaN(Number.parseInt(o))&&(t[r[o]]=r.getPropertyValue(r[o]));return t}function d(e,t){const r=i(e);Object.keys(r).forEach(o=>{t.hasOwnProperty(o)||f(e,o)}),Object.entries(t).forEach(([o,n])=>{r[o]!==n&&u(e,o,n)})}function p(e,t){return e.document.documentElement.style.getPropertyValue(t)}function u(e,t,r){e.document.documentElement.style.setProperty(t,r)}function f(e,t){e.document.documentElement.style.removeProperty(t)}export{i as getProperties,p as getProperty,a as isTypedOMSupported,f as removeProperty,u as setProperty,m as styleChangeAffectsLayout,d as updateProperties};
@@ -1,4 +1,4 @@
1
- import{Locator as K}from"@readium/shared";import{Module as N}from"./Module.js";import{rangeFromLocator as P}from"../helpers/locator.js";import{getClientRectsNoOverlap as H,rectContainsPoint as E,getTextClientRects as U}from"../helpers/rect.js";import{getProperty as I}from"../helpers/css.js";import{sML as j,adjustColorForContrast as T,getContrastingTextColor as O,colorToRgba as M,isDarkColor as q}from"@readium/helpers";import{makeWritingContext as z}from"../helpers/document.js";import{sanitizeHTML as V}from"../helpers/sanitize.js";function x(S){switch(S){case d.Mask:return"rgba(255, 255, 255, 0.5)";case d.Highlight:case d.HighlightUnderline:return"#FFFF00";default:return"#FF0000"}}const d={Highlight:"highlight",HighlightUnderline:"highlightUnderline",Underline:"underline",Strikethrough:"strikethrough",Outline:"outline",TextColor:"textColor",Mask:"mask",Template:"template"};var G=(S=>(S.Wrap="wrap",S.Viewport="viewport",S.Bounds="bounds",S.Page="page",S))(G||{}),X=(S=>(S.Boxes="boxes",S.Bounds="bounds",S))(X||{});const Z=()=>"Highlight"in window,F=["IMG","IMAGE","AUDIO","VIDEO","SVG"];class Y{constructor(e,t,i,o){this.wnd=e,this.comms=t,this.id=i,this.name=o,this.items=[],this.lastItemId=0,this.container=void 0,this._activatable=!1,this._hoverable=!1,this.hoveredItem=void 0,this.experimentalHighlights=!1,this._tintSubKeys=new Map,this._subKeyCounter=0,this.maskSvg=void 0,this.shadowHost=void 0,this.shadowRoot=void 0,this.currentRender=0,Z()&&(this.experimentalHighlights=!0,this.notTextFlag=new Map),this.activationHandler=this.handleActivation.bind(this),this.wnd.document.addEventListener("pointerup",this.activationHandler),this.hoverHandler=this.handleHover.bind(this),this.wnd.document.addEventListener("pointermove",this.hoverHandler)}get activatable(){return this._activatable}set activatable(e){this._activatable=e}get hoverable(){return this._hoverable}get hasOverlayItems(){return!this.experimentalHighlights||(this.notTextFlag?.size??0)>0}set hoverable(e){if(this._hoverable=e,!e&&this.hoveredItem){const t=this.hoveredItem.range.getBoundingClientRect(),i=this.wnd.devicePixelRatio;this.comms.send("decoration_pointer_leave",{decorationId:this.hoveredItem.decoration.id,group:this.name,rect:{top:t.top*i,left:t.left*i,width:t.width*i,height:t.height*i}}),this.hoveredItem=void 0}}add(e){const t=`${this.id}-${this.lastItemId++}`,i=P(this.wnd.document,e.locator);if(!i){this.comms.log("Can't locate DOM range for decoration",e);return}const o=i.commonAncestorContainer;if(o.nodeType!==Node.TEXT_NODE&&this.experimentalHighlights&&(F.includes(o.nodeName.toUpperCase())&&this.notTextFlag?.set(t,!0),i.cloneContents().querySelector(F.join(", ").toLowerCase())&&this.notTextFlag?.set(t,!0),(o.textContent?.trim()||"").length===0&&this.notTextFlag?.set(t,!0)),this.experimentalHighlights&&!this.notTextFlag?.has(t)){const n=r=>{for(;r&&r.nodeType===Node.ELEMENT_NODE;){if(r.namespaceURI?.includes("svg"))return!0;r=r.parentNode}return!1};(n(i.startContainer)||n(i.endContainer))&&this.notTextFlag?.set(t,!0)}if(this.experimentalHighlights){const{type:n}=e.style,{layout:r,width:p,expand:a}=e.style;n!==d.TextColor&&(n===d.Outline||n===d.Template||n===d.Mask||r!==void 0&&r!=="boxes"||p!==void 0&&p!=="wrap"||a)&&this.notTextFlag?.set(t,!0)}const s={decoration:e,id:t,range:i,hitRects:[],clickableElements:void 0,container:void 0};this.items.push(s),this.layout(s),s.hitRects=this.clientRectsToDocCoords(H(s.range,!1,!1,(s.decoration.style.expand??0)+this.hitGap())),this.renderLayout([s])}remove(e){const t=this.items.findIndex(s=>s.decoration.id===e);if(t<0)return;const i=this.items[t],o=i.decoration.style?.type===d.Mask;if(this.items.splice(t,1),i.clickableElements=void 0,i.container&&(i.container.remove(),i.container=void 0),this.experimentalHighlights&&!this.notTextFlag?.has(i.id)&&i.highlightSubKey){const s=this.wnd.CSS.highlights;s.get(i.highlightSubKey)?.delete(i.range),this.items.some(r=>r.highlightSubKey===i.highlightSubKey)||s.delete(i.highlightSubKey);const n=this.wnd.document.getElementById(`${this.id}-style`);n&&this._rebuildHighlightStylesheet(n)}this.notTextFlag?.delete(i.id),this.hoveredItem===i&&(this.hoveredItem=void 0),o&&this.updateSharedMask()}update(e){this.remove(e.id),this.add(e)}clear(){this.clearContainer(),this.items.length=0,this.notTextFlag?.clear(),this.hoveredItem=void 0,this.maskSvg&&(this.maskSvg.remove(),this.maskSvg=void 0),this.shadowHost&&(this.shadowHost.remove(),this.shadowHost=void 0,this.shadowRoot=void 0)}destroy(){this.clear(),this.wnd.document.removeEventListener("pointerup",this.activationHandler),this.wnd.document.removeEventListener("pointermove",this.hoverHandler)}clientRectsToDocCoords(e,t=z(this.wnd)){const i=t.xDocOffset,o=t.yDocOffset;return i===0&&o===0?e:e.map(s=>({left:s.left+i,top:s.top+o,right:s.right+i,bottom:s.bottom+o,width:s.width,height:s.height}))}pointerToDocCoords(e){const t=z(this.wnd);return{docX:e.clientX+t.xDocOffset,docY:e.clientY+t.yDocOffset}}effectiveZoom(){if(!j.UA.Blink)return 1;const e=parseFloat(this.wnd.getComputedStyle(this.wnd.document.documentElement).zoom),t=parseFloat(this.wnd.getComputedStyle(this.wnd.document.body).zoom);return(e||1)*(t||1)}hitGap(){return 2*this.effectiveZoom()}handleActivation(e){if(!this._activatable)return;const{docX:t,docY:i}=this.pointerToDocCoords(e),o=this.wnd.devicePixelRatio;for(const s of this.items){let n;if(s.decoration.style.type===d.Template)for(const r of s.clickableElements??[]){const p=r.getBoundingClientRect();if(E(p,e.clientX,e.clientY,0)){n=p;break}}else for(const r of s.hitRects)if(E(r,t,i,0)){n=s.range.getBoundingClientRect();break}if(n){this.comms.send("decoration_activated",{decorationId:s.decoration.id,group:this.name,rect:{top:n.top*o,left:n.left*o,width:n.width*o,height:n.height*o},point:{x:e.clientX*o,y:e.clientY*o}});return}}}handleHover(e){if(!this._hoverable)return;const{docX:t,docY:i}=this.pointerToDocCoords(e),o=this.wnd.devicePixelRatio;let s,n;for(const r of this.items){if(r.decoration.style.type===d.Template)for(const p of r.clickableElements??[]){const a=p.getBoundingClientRect();if(E(a,e.clientX,e.clientY,0)){s=r,n=a;break}}else for(const p of r.hitRects)if(E(p,t,i,0)){s=r,n=r.range.getBoundingClientRect();break}if(s)break}if(s!==this.hoveredItem){if(this.hoveredItem){const p=this.hoveredItem.range.commonAncestorContainer.isConnected?this.hoveredItem.range.getBoundingClientRect():null;this.comms.send("decoration_pointer_leave",{decorationId:this.hoveredItem.decoration.id,group:this.name,rect:p?{top:p.top*o,left:p.left*o,width:p.width*o,height:p.height*o}:void 0,point:{x:e.clientX*o,y:e.clientY*o}})}this.hoveredItem=s,s&&n&&this.comms.send("decoration_pointer_enter",{decorationId:s.decoration.id,group:this.name,rect:{top:n.top*o,left:n.left*o,width:n.width*o,height:n.height*o},point:{x:e.clientX*o,y:e.clientY*o}})}}requestLayout(){this.wnd.cancelAnimationFrame(this.currentRender),this.clearContainer(),this.wnd.document.fonts.ready.then(()=>{this.currentRender=this.wnd.requestAnimationFrame(()=>{this.items.forEach(e=>{this.layout(e),e.hitRects=this.clientRectsToDocCoords(H(e.range,!1,!1,(e.decoration.style.expand??0)+this.hitGap()))}),this.renderLayout(this.items),this.updateSharedMask()})})}repositionOverlays(){let e=!1;const t=[],i=z(this.wnd);this.items.forEach(o=>{if(!(this.experimentalHighlights&&!this.notTextFlag?.has(o.id))){if(o.decoration.style?.type===d.Mask){e=!0;return}this.repositionItem(o,i)||(o.container?.remove(),this.layout(o),t.push(o)),o.hitRects=this.clientRectsToDocCoords(H(o.range,!1,!1,(o.decoration.style.expand??0)+this.hitGap()),i)}}),t.length&&this.renderLayout(t),e&&this.updateSharedMask()}experimentalLayout(e){const t=this.requireContainer(!0),i=this.wnd.CSS.highlights,o=e.decoration.style,s=o.type??d.Highlight,n=o.tint??x(s),r=o.width,p=o.layout,a=this._getSubKey(s,n);e.highlightSubKey&&(i.get(e.highlightSubKey)?.delete(e.range),e.highlightSubKey!==a&&!this.items.some(f=>f!==e&&f.highlightSubKey===e.highlightSubKey)&&i.delete(e.highlightSubKey)),e.highlightSubKey=a;let g;i.has(a)?g=i.get(a):(g=new this.wnd.Highlight,i.set(a,g));const h=(c,f)=>this.wnd.document.caretPositionFromPoint?.(c,f)??null;if(s===d.TextColor&&(p==="bounds"||r==="bounds"||r==="page")){const c=z(this.wnd);if(c.isVertical)console.warn("Vertical writing detected: caretPositionFromPoint has known bugs, falling back to original range"),g.add(e.range);else{const f=e.range.getBoundingClientRect();let k,y;r==="page"?(k=Math.floor(c.inlineStart(f)/c.pageInlineSize)*c.pageInlineSize,y=c.pageInlineSize):(k=c.inlineStart(f),y=c.inlineSize(f));const v=h(k,c.blockStart(f)+1),w=h(k+y,c.blockStart(f)+c.blockSize(f)-1);if(v&&w){const C=this.wnd.document.createRange();C.setStart(v.offsetNode,v.offset),C.setEnd(w.offsetNode,w.offset),g.add(C),e.range=C}else g.add(e.range)}}else g.add(e.range);const l=this.getBackgroundColor(),b=o.enforceContrast!==!1?T(n,l):n;let m;switch(s){case d.Underline:m=`::highlight(${a}) {
1
+ import{Locator as D}from"@readium/shared";import{Module as K}from"./Module.js";import{rangeFromLocator as U}from"../helpers/locator.js";import{getClientRectsNoOverlap as z,rectContainsPoint as H,getTextClientRects as q}from"../helpers/rect.js";import{getProperty as I}from"../helpers/css.js";import{sML as O,adjustColorForContrast as E,getContrastingTextColor as F,colorToRgba as M,isDarkColor as W}from"@readium/helpers";import{makeWritingContext as T}from"../helpers/document.js";import{sanitizeHTML as j}from"../helpers/sanitize.js";function $(S){switch(S){case d.Mask:return"rgba(255, 255, 255, 0.5)";case d.Highlight:case d.HighlightUnderline:return"#FFFF00";default:return"#FF0000"}}const d={Highlight:"highlight",HighlightUnderline:"highlightUnderline",Underline:"underline",Strikethrough:"strikethrough",Outline:"outline",TextColor:"textColor",Mask:"mask",Template:"template"};var V=(S=>(S.Wrap="wrap",S.Viewport="viewport",S.Bounds="bounds",S.Page="page",S))(V||{}),Z=(S=>(S.Boxes="boxes",S.Bounds="bounds",S))(Z||{});const G=()=>"Highlight"in window,P=["IMG","IMAGE","AUDIO","VIDEO","SVG"],X=new Set(["SUP","SUB","SMALL","CODE"]);class Y{constructor(e,t,i,o){this.wnd=e,this.comms=t,this.id=i,this.name=o,this.items=[],this.lastItemId=0,this.container=void 0,this._activatable=!1,this._hoverable=!1,this.hoveredItem=void 0,this.experimentalHighlights=!1,this._tintSubKeys=new Map,this._subKeyCounter=0,this.pendingWebkitReflow=null,this.maskSvg=void 0,this.shadowHost=void 0,this.shadowRoot=void 0,this.currentRender=0,G()&&(this.experimentalHighlights=!0,this.notTextFlag=new Map),this.activationHandler=this.handleActivation.bind(this),this.wnd.document.addEventListener("pointerup",this.activationHandler),this.hoverHandler=this.handleHover.bind(this),this.wnd.document.addEventListener("pointermove",this.hoverHandler)}get activatable(){return this._activatable}set activatable(e){this._activatable=e}get hoverable(){return this._hoverable}get hasOverlayItems(){return!this.experimentalHighlights||(this.notTextFlag?.size??0)>0}set hoverable(e){if(this._hoverable=e,!e&&this.hoveredItem){const t=this.hoveredItem.range.getBoundingClientRect(),i=this.wnd.devicePixelRatio;this.comms.send("decoration_pointer_leave",{decorationId:this.hoveredItem.decoration.id,group:this.name,rect:{top:t.top*i,left:t.left*i,width:t.width*i,height:t.height*i}}),this.hoveredItem=void 0}}add(e){const t=`${this.id}-${this.lastItemId++}`,i=U(this.wnd.document,e.locator);if(!i){this.comms.log("Can't locate DOM range for decoration",e);return}const o=i.commonAncestorContainer;if(o.nodeType!==Node.TEXT_NODE&&this.experimentalHighlights&&(P.includes(o.nodeName.toUpperCase())&&this.notTextFlag?.set(t,!0),i.cloneContents().querySelector(P.join(", ").toLowerCase())&&this.notTextFlag?.set(t,!0),(o.textContent?.trim()||"").length===0&&this.notTextFlag?.set(t,!0)),this.experimentalHighlights&&!this.notTextFlag?.has(t)){const n=r=>{for(;r&&r.nodeType===Node.ELEMENT_NODE;){if(r.namespaceURI?.includes("svg"))return!0;r=r.parentNode}return!1};(n(i.startContainer)||n(i.endContainer))&&this.notTextFlag?.set(t,!0)}if(this.experimentalHighlights){const{type:n}=e.style,{layout:r,width:p,expand:a}=e.style;n!==d.TextColor&&(n===d.Outline||n===d.Template||n===d.Mask||r!==void 0&&r!=="boxes"||p!==void 0&&p!=="wrap"||a)&&this.notTextFlag?.set(t,!0)}const s={decoration:e,id:t,range:i,hitRects:[],clickableElements:void 0,container:void 0};this.items.push(s),this.layout(s),s.hitRects=this.clientRectsToDocCoords(z(s.range,!1,!1,(s.decoration.style.expand??0)+this.hitGap())),this.renderLayout([s])}remove(e){const t=this.items.findIndex(s=>s.decoration.id===e);if(t<0)return;const i=this.items[t],o=i.decoration.style?.type===d.Mask;if(this.items.splice(t,1),i.clickableElements=void 0,i.container&&(i.container.remove(),i.container=void 0),this.experimentalHighlights&&!this.notTextFlag?.has(i.id)&&i.highlightSubKey){const s=this.wnd.CSS.highlights;s.get(i.highlightSubKey)?.delete(i.range),this.items.some(r=>r.highlightSubKey===i.highlightSubKey)||s.delete(i.highlightSubKey);const n=this.wnd.document.getElementById(`${this.id}-style`);n&&this._rebuildHighlightStylesheet(n),this.scheduleWebkitRepaintFix([i])}this.notTextFlag?.delete(i.id),this.hoveredItem===i&&(this.hoveredItem=void 0),o&&this.updateSharedMask()}update(e){this.remove(e.id),this.add(e)}clear(){this.experimentalHighlights&&this.scheduleWebkitRepaintFix(this.items),this.clearContainer(),this.items.length=0,this.notTextFlag?.clear(),this.hoveredItem=void 0,this.maskSvg&&(this.maskSvg.remove(),this.maskSvg=void 0),this.shadowHost&&(this.shadowHost.remove(),this.shadowHost=void 0,this.shadowRoot=void 0)}destroy(){this.clear(),this.wnd.document.removeEventListener("pointerup",this.activationHandler),this.wnd.document.removeEventListener("pointermove",this.hoverHandler)}isWebkitRepaintProne(e){const t=this.wnd.getComputedStyle(e);if(X.has(e.tagName.toUpperCase())||t.display==="inline-block"||t.verticalAlign!=="baseline")return!0;const i=e.parentElement?this.wnd.getComputedStyle(e.parentElement):null;return!!i&&parseFloat(t.fontSize)!==parseFloat(i.fontSize)}findWebkitRepaintProneElement(e){let t=e.startContainer.nodeType===Node.ELEMENT_NODE?e.startContainer:e.startContainer.parentElement;for(;t;){if(this.isWebkitRepaintProne(t))return t;if(this.wnd.getComputedStyle(t).display!=="inline")break;t=t.parentElement}const i=e.commonAncestorContainer,o=i.nodeType===Node.ELEMENT_NODE?i:i.parentElement;if(!o)return null;const s=this.wnd.document.createTreeWalker(o,NodeFilter.SHOW_ELEMENT,n=>e.intersectsNode(n)?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_REJECT);for(let n=s.nextNode();n;n=s.nextNode())if(this.isWebkitRepaintProne(n))return n;return null}forceWebkitBlockReflow(e){let t=e;for(;t;){const n=this.wnd.getComputedStyle(t).display;if(n!=="inline"&&n!=="inline-block")break;t=t.parentElement}if(!t||!t.isConnected)return;const i=t,o=i.style.getPropertyValue("transform"),s=i.style.getPropertyPriority("transform");i.style.setProperty("transform","translateZ(0.001px)","important"),i.offsetHeight,i.style.setProperty("transform","translateZ(0px)","important"),i.offsetHeight,o?i.style.setProperty("transform",o,s):i.style.removeProperty("transform")}scheduleWebkitRepaintFix(e){if(O.UA.WebKit)for(const t of e){if(this.notTextFlag?.has(t.id)||!t.highlightSubKey)continue;const i=this.findWebkitRepaintProneElement(t.range);i&&this.queueWebkitReflow(i)}}queueWebkitReflow(e){this.pendingWebkitReflow||(this.pendingWebkitReflow=new Set,this.wnd.requestAnimationFrame(()=>{const t=this.pendingWebkitReflow;this.pendingWebkitReflow=null,t?.forEach(i=>this.forceWebkitBlockReflow(i))})),this.pendingWebkitReflow.add(e)}clientRectsToDocCoords(e,t=T(this.wnd)){const i=t.xDocOffset,o=t.yDocOffset;return i===0&&o===0?e:e.map(s=>({left:s.left+i,top:s.top+o,right:s.right+i,bottom:s.bottom+o,width:s.width,height:s.height}))}pointerToDocCoords(e){const t=T(this.wnd);return{docX:e.clientX+t.xDocOffset,docY:e.clientY+t.yDocOffset}}effectiveZoom(){if(!O.UA.Blink)return 1;const e=parseFloat(this.wnd.getComputedStyle(this.wnd.document.documentElement).zoom),t=parseFloat(this.wnd.getComputedStyle(this.wnd.document.body).zoom);return(e||1)*(t||1)}hitGap(){return 2*this.effectiveZoom()}handleActivation(e){if(!this._activatable)return;const{docX:t,docY:i}=this.pointerToDocCoords(e),o=this.wnd.devicePixelRatio;for(const s of this.items){let n;if(s.decoration.style.type===d.Template)for(const r of s.clickableElements??[]){const p=r.getBoundingClientRect();if(H(p,e.clientX,e.clientY,0)){n=p;break}}else for(const r of s.hitRects)if(H(r,t,i,0)){n=s.range.getBoundingClientRect();break}if(n){this.comms.send("decoration_activated",{decorationId:s.decoration.id,group:this.name,rect:{top:n.top*o,left:n.left*o,width:n.width*o,height:n.height*o},point:{x:e.clientX*o,y:e.clientY*o}});return}}}handleHover(e){if(!this._hoverable)return;const{docX:t,docY:i}=this.pointerToDocCoords(e),o=this.wnd.devicePixelRatio;let s,n;for(const r of this.items){if(r.decoration.style.type===d.Template)for(const p of r.clickableElements??[]){const a=p.getBoundingClientRect();if(H(a,e.clientX,e.clientY,0)){s=r,n=a;break}}else for(const p of r.hitRects)if(H(p,t,i,0)){s=r,n=r.range.getBoundingClientRect();break}if(s)break}if(s!==this.hoveredItem){if(this.hoveredItem){const p=this.hoveredItem.range.commonAncestorContainer.isConnected?this.hoveredItem.range.getBoundingClientRect():null;this.comms.send("decoration_pointer_leave",{decorationId:this.hoveredItem.decoration.id,group:this.name,rect:p?{top:p.top*o,left:p.left*o,width:p.width*o,height:p.height*o}:void 0,point:{x:e.clientX*o,y:e.clientY*o}})}this.hoveredItem=s,s&&n&&this.comms.send("decoration_pointer_enter",{decorationId:s.decoration.id,group:this.name,rect:{top:n.top*o,left:n.left*o,width:n.width*o,height:n.height*o},point:{x:e.clientX*o,y:e.clientY*o}})}}requestLayout(){this.wnd.cancelAnimationFrame(this.currentRender),this.clearContainer(),this.wnd.document.fonts.ready.then(()=>{this.currentRender=this.wnd.requestAnimationFrame(()=>{this.items.forEach(e=>{this.layout(e),e.hitRects=this.clientRectsToDocCoords(z(e.range,!1,!1,(e.decoration.style.expand??0)+this.hitGap()))}),this.renderLayout(this.items),this.updateSharedMask()})})}repositionOverlays(){let e=!1;const t=[],i=T(this.wnd);this.items.forEach(o=>{if(!(this.experimentalHighlights&&!this.notTextFlag?.has(o.id))){if(o.decoration.style?.type===d.Mask){e=!0;return}this.repositionItem(o,i)||(o.container?.remove(),this.layout(o),t.push(o)),o.hitRects=this.clientRectsToDocCoords(z(o.range,!1,!1,(o.decoration.style.expand??0)+this.hitGap()),i)}}),t.length&&this.renderLayout(t),e&&this.updateSharedMask()}experimentalLayout(e){const t=this.requireContainer(!0),i=this.wnd.CSS.highlights,o=e.decoration.style,s=o.type??d.Highlight,n=o.tint??$(s),r=o.width,p=o.layout,a=this._getSubKey(s,n);e.highlightSubKey&&(i.get(e.highlightSubKey)?.delete(e.range),e.highlightSubKey!==a&&!this.items.some(f=>f!==e&&f.highlightSubKey===e.highlightSubKey)&&i.delete(e.highlightSubKey)),e.highlightSubKey=a;let g;i.has(a)?g=i.get(a):(g=new this.wnd.Highlight,i.set(a,g));const l=(c,f)=>this.wnd.document.caretPositionFromPoint?.(c,f)??null;if(s===d.TextColor&&(p==="bounds"||r==="bounds"||r==="page")){const c=T(this.wnd);if(c.isVertical)console.warn("Vertical writing detected: caretPositionFromPoint has known bugs, falling back to original range"),g.add(e.range);else{const f=e.range.getBoundingClientRect();let k,v;r==="page"?(k=Math.floor(c.inlineStart(f)/c.pageInlineSize)*c.pageInlineSize,v=c.pageInlineSize):(k=c.inlineStart(f),v=c.inlineSize(f));const y=l(k,c.blockStart(f)+1),w=l(k+v,c.blockStart(f)+c.blockSize(f)-1);if(y&&w){const C=this.wnd.document.createRange();C.setStart(y.offsetNode,y.offset),C.setEnd(w.offsetNode,w.offset),g.add(C),e.range=C}else g.add(e.range)}}else g.add(e.range);const h=this.getBackgroundColor(),b=o.enforceContrast!==!1?E(n,h):n;let m;switch(s){case d.Underline:m=`::highlight(${a}) {
2
2
  text-decoration: underline;
3
3
  text-decoration-color: ${b};
4
4
  text-decoration-thickness: 0.1em;
@@ -11,14 +11,14 @@ import{Locator as K}from"@readium/shared";import{Module as N}from"./Module.js";i
11
11
  outline-offset: 1px;
12
12
  }`;break;case d.TextColor:m=`::highlight(${a}) {
13
13
  color: ${b};
14
- }`;break;case d.HighlightUnderline:{const{r:c,g:f,b:k}=M(b),y=`rgba(${c}, ${f}, ${k}, 0.3)`;m=`::highlight(${a}) {
15
- color: ${O(b,l)};
16
- background-color: ${y};
14
+ }`;break;case d.HighlightUnderline:{const{r:c,g:f,b:k}=M(b),v=`rgba(${c}, ${f}, ${k}, 0.3)`;m=`::highlight(${a}) {
15
+ color: ${F(b,h)};
16
+ background-color: ${v};
17
17
  text-decoration: underline;
18
18
  text-decoration-color: ${b};
19
19
  text-decoration-thickness: 0.1em;
20
20
  }`;break}case d.Highlight:default:m=`::highlight(${a}) {
21
- color: ${O(b,l)};
21
+ color: ${F(b,h)};
22
22
  background-color: ${b};
23
23
  }`}e.highlightCSS=m,this._rebuildHighlightStylesheet(t)}_getSubKey(e,t){const i=`${e}::${t}`;let o=this._tintSubKeys.get(i);return o||(o=`${this.id}--${this._subKeyCounter++}`,this._tintSubKeys.set(i,o)),o}_rebuildHighlightStylesheet(e){const t=new Set,i=[];for(const o of this.items)o.highlightSubKey&&o.highlightCSS&&!t.has(o.highlightSubKey)&&(t.add(o.highlightSubKey),i.push(o.highlightCSS));e.innerHTML=i.join(`
24
- `)}positionElement(e,t,i,o,s,n,r=0){const p=e.decoration?.style?.width,a=s;switch(p){case"viewport":{const g=Math.floor(t.inlineStart(a)/t.viewportInlineSize)*t.viewportInlineSize;t.applyPosition(o,g+t.inlineScrollOffset+r,t.blockStart(a)+t.blockScrollOffset,t.viewportInlineSize-2*r,t.blockSize(a),i);break}case"page":{const g=Math.floor(t.inlineStart(a)/t.pageInlineSize)*t.pageInlineSize;t.applyPosition(o,g+t.inlineScrollOffset+r,t.blockStart(a)+t.blockScrollOffset,t.pageInlineSize-2*r,t.blockSize(a),i);break}case"bounds":{t.applyPosition(o,t.inlineStart(n)+t.inlineScrollOffset,t.blockStart(a)+t.blockScrollOffset,t.inlineSize(n),t.blockSize(a),i);break}default:t.applyPosition(o,t.inlineStart(a)+t.inlineScrollOffset,t.blockStart(a)+t.blockScrollOffset,t.inlineSize(a),t.blockSize(a),i)}}computeOverlayRects(e,t,i,o){if(e.decoration?.style?.layout==="bounds")return[o?{left:i.left-o,right:i.right+o,top:i.top-o,bottom:i.bottom+o,width:i.width+o*2,height:i.height+o*2}:i];const n=e.decoration.style.type,r=n===d.Underline||n===d.Strikethrough,p=n===d.Strikethrough,a=r?U(e.range,["rt","rp"]):e.range;let g=H(a,!0,t.isVertical,r?0:o);return g=g.sort((h,l)=>t.isVertical?(t.isVertLR?1:-1)*(h.left-l.left):h.top-l.top),g.map(h=>{let l=h;if(p){const u=t.blockSize(h)*.1,m=t.blockStart(h)+t.blockSize(h)/2-u/2;l=t.isVertical?{left:m,right:m+u,top:h.top,bottom:h.bottom,width:u,height:h.height}:{top:m,bottom:m+u,left:h.left,right:h.right,height:u,width:h.width}}return o&&r&&(l=t.isVertical?{...l,top:l.top-o,bottom:l.bottom+o,height:l.height+o*2}:{...l,left:l.left-o,right:l.right+o,width:l.width+o*2}),l})}repositionItem(e,t){if(!e.container)return!0;const i=e.decoration.style;if(i.type!==d.Template){const g=i.type??d.Highlight;if(g===d.TextColor||g===d.Mask)return!0}const o=1/this.effectiveZoom(),s=i.expand??0,n=e.range.getBoundingClientRect(),r=(()=>{if(i.type!==d.Outline)return 0;const g=i.width;return g==="page"||g==="viewport"?3:0})(),p=this.computeOverlayRects(e,t,n,s),a=Array.from(e.container.children);return a.length!==p.length?!1:(a.forEach((g,h)=>this.positionElement(e,t,o,g,p[h],n,r)),!0)}layout(e){if(this.experimentalHighlights&&!this.notTextFlag?.has(e.id))return this.experimentalLayout(e);const t=this.wnd.document.createElement("div");t.setAttribute("id",e.id),t.dataset.highlightId=e.decoration.id,t.style.setProperty("pointer-events","none");const i=z(this.wnd),o=1/this.effectiveZoom(),s=e.decoration.style.expand??0,n=e.range.getBoundingClientRect(),r=e.decoration.style,p=(()=>{if(r.type!==d.Outline)return 0;const h=r.width;return h==="page"||h==="viewport"?3:0})();let a;if(r.type===d.Template){r.stylesheet&&this.injectCustomStylesheet(r.stylesheet);const h=V(this.wnd,r.element);if(!h){e.container=t,e.clickableElements=[];return}h.style.setProperty("pointer-events","none"),a=h}else{const h=r,l=h.type??d.Highlight,u=h.tint??x(l);if(l===d.TextColor){e.container=t,e.clickableElements=[];return}if(l===d.Mask){e.container=t,e.clickableElements=[],this.updateSharedMask();return}const b=this.getCurrentDarkMode(),m=this.getBackgroundColor(),c=h.enforceContrast!==!1,f=(()=>{switch(l){case d.Underline:{const v=c?T(u,m):u,w=h.layout==="bounds",[C,$]=i.isVertical?["border-right","border-left"]:["border-bottom","border-top"];return[w?`${$}: 0.1em solid ${v} !important`:null,`${C}: 0.1em solid ${v} !important`,"background-color: transparent !important","box-sizing: border-box !important"].filter(Boolean).join("; ")}case d.Strikethrough:{const v=c?T(u,m):u;return h.layout==="bounds"?[`background: repeating-linear-gradient(-45deg, transparent, transparent 19px, ${v} 19px, ${v} 20px) !important`,"background-color: transparent !important","box-sizing: border-box !important"].join("; "):[`background-color: ${v} !important`,"box-sizing: border-box !important"].join("; ")}case d.Outline:return[`outline: 2px solid ${c?T(u,m):u} !important`,"outline-offset: 1px !important","background-color: transparent !important","box-sizing: border-box !important"].join("; ");case d.HighlightUnderline:{const v=c?T(u,m):u,{r:w,g:C,b:$}=M(v),_=`rgba(${w}, ${C}, ${$}, 0.3)`,B=h.layout==="bounds",[D,L]=i.isVertical?["border-right","border-left"]:["border-bottom","border-top"];return[`background-color: ${_} !important`,B?`${L}: 0.1em solid ${v} !important`:null,`${D}: 0.1em solid ${v} !important`,"box-sizing: border-box !important"].filter(Boolean).join("; ")}case d.Highlight:default:return[`background-color: ${c?T(u,m):u} !important`,`mix-blend-mode: ${b?"exclusion":"multiply"} !important`,"opacity: 1 !important","box-sizing: border-box !important","transform: translateZ(0) !important"].join("; ")}})(),k=this.wnd.document.createElement("template");k.innerHTML=`<div data-readium="true" class="readium-${l}" style="${f}"></div>`.trim(),a=k.content.firstElementChild}const g=this.computeOverlayRects(e,i,n,s);for(const h of g){const l=a.cloneNode(!0);l.style.setProperty("pointer-events","none"),this.positionElement(e,i,o,l,h,n,p),t.append(l)}e.container=t,e.clickableElements=Array.from(t.querySelectorAll("[data-activable='1']")),e.clickableElements.length||(e.clickableElements=Array.from(t.children))}renderLayout(e){this.wnd.cancelAnimationFrame(this.currentRender),this.currentRender=this.wnd.requestAnimationFrame(()=>{if(e=e.filter(i=>!this.experimentalHighlights||!!this.notTextFlag?.has(i.id)),!e||e.length===0)return;this.requireContainer().append(...e.map(i=>i.container).filter(i=>!!i))})}requireContainer(e=!1){if(e){let t;return this.wnd.document.getElementById(`${this.id}-style`)?t=this.wnd.document.getElementById(`${this.id}-style`):(t=this.wnd.document.createElement("style"),t.dataset.readium="true",t.id=`${this.id}-style`,this.wnd.document.head.appendChild(t)),t}return this.container||(this.shadowRoot||(this.shadowHost=this.wnd.document.createElement("div"),this.shadowHost.style.cssText="position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none",this.wnd.document.body.appendChild(this.shadowHost),this.shadowRoot=this.shadowHost.attachShadow({mode:"open"})),this.container=this.wnd.document.createElement("div"),this.container.setAttribute("id",this.id),this.container.dataset.group=this.name,this.container.dataset.readium="true",this.container.style.setProperty("pointer-events","none"),this.container.style.display="contents",this.shadowRoot.appendChild(this.container)),this.container}getCurrentDarkMode(){return I(this.wnd,"--USER__appearance")==="readium-night-on"||q(this.getBackgroundColor())}getBackgroundColor(){return I(this.wnd,"--USER__backgroundColor")||this.wnd.getComputedStyle(this.wnd.document.documentElement).getPropertyValue("background-color")}updateSharedMask(){const e=this.items.filter(l=>l.decoration.style?.type===d.Mask);if(e.length===0){this.maskSvg&&(this.maskSvg.remove(),this.maskSvg=void 0),this.shadowHost&&!this.container&&(this.shadowHost.remove(),this.shadowRoot=void 0,this.shadowHost=void 0);return}const t=z(this.wnd),i=1/this.effectiveZoom(),o=this.wnd.document.documentElement,s=o.scrollWidth,n=o.scrollHeight,r=[];for(const l of e){const u=l.decoration.style,b=u.layout??"boxes",m=u.width??"wrap",c=u.expand??0,f=l.range.getBoundingClientRect(),k=b==="bounds"?[c?{left:f.left-c,top:f.top-c,right:f.right+c,bottom:f.bottom+c,width:f.width+c*2,height:f.height+c*2}:f]:H(l.range,!1,!1,c);for(const y of k){let v;switch(m){case"viewport":{const w=Math.floor(t.inlineStart(y)/t.viewportInlineSize)*t.viewportInlineSize;v=t.toRect(w,t.blockStart(y),t.viewportInlineSize,t.blockSize(y));break}case"page":{const w=Math.floor(t.inlineStart(y)/t.pageInlineSize)*t.pageInlineSize;v=t.toRect(w,t.blockStart(y),t.pageInlineSize,t.blockSize(y));break}case"bounds":{v=t.toRect(t.inlineStart(f),t.blockStart(y),t.inlineSize(f),t.blockSize(y));break}default:v=t.toRect(t.inlineStart(y),t.blockStart(y),t.inlineSize(y),t.blockSize(y))}r.push(v)}}const p=[`M0 0 H${s} V${n} H0 Z`,...r.map(l=>{const u=(l.left+t.xDocOffset)*i,b=(l.top+t.yDocOffset)*i,m=(l.right+t.xDocOffset)*i,c=(l.bottom+t.yDocOffset)*i;return`M${u} ${b} H${m} V${c} H${u} Z`})].join(" "),a="http://www.w3.org/2000/svg";if(!this.maskSvg){this.shadowRoot||(this.shadowHost=this.wnd.document.createElement("div"),this.shadowHost.style.cssText="position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none",this.wnd.document.body.appendChild(this.shadowHost),this.shadowRoot=this.shadowHost.attachShadow({mode:"open"})),this.maskSvg=this.wnd.document.createElementNS(a,"svg"),this.maskSvg.style.cssText=`position:absolute;top:0;left:0;width:${s}px;height:${n}px;pointer-events:none;z-index:2147483647`,this.maskSvg.dataset.readium="true";const l=this.wnd.document.createElementNS(a,"defs"),u=this.wnd.document.createElementNS(a,"clipPath"),b=`${this.id}-mask-clip`;u.setAttribute("id",b),u.setAttribute("clipPathUnits","userSpaceOnUse");const m=this.wnd.document.createElementNS(a,"path");m.setAttribute("clip-rule","evenodd"),u.appendChild(m),l.appendChild(u),this.maskSvg.appendChild(l);const c=this.wnd.document.createElementNS(a,"rect");c.setAttribute("id",`${this.id}-mask-rect`),c.setAttribute("clip-path",`url(#${b})`),c.style.pointerEvents="none",this.maskSvg.appendChild(c),this.shadowRoot.appendChild(this.maskSvg)}this.maskSvg.style.width=`${s}px`,this.maskSvg.style.height=`${n}px`;const g=this.maskSvg.querySelector("path");g&&g.setAttribute("d",p);const h=this.maskSvg.querySelector("rect");if(h){const u=e[0].decoration.style.tint,b=u??this.getBackgroundColor()??x(d.Mask),m=u?"1":"0.5";h.setAttribute("x","0"),h.setAttribute("y","0"),h.setAttribute("width",String(s)),h.setAttribute("height",String(n)),h.setAttribute("fill",b),h.setAttribute("fill-opacity",m)}}injectCustomStylesheet(e){const t=`${this.id}-custom-style`;let i=this.wnd.document.getElementById(t);i||(i=this.wnd.document.createElement("style"),i.id=t,i.dataset.readium="true",this.wnd.document.head.appendChild(i)),i.innerHTML=e}clearContainer(){if(this.experimentalHighlights){const e=this.wnd.CSS.highlights;for(const t of this._tintSubKeys.values())e.delete(t);this._tintSubKeys.clear(),this._subKeyCounter=0}this.wnd.document.getElementById(`${this.id}-custom-style`)?.remove(),this.container&&(this.container.remove(),this.container=void 0)}}const R=class R extends N{constructor(){super(...arguments),this.observedResizeTargets=new Map,this.resizeFrame=0,this.lastGroupId=0,this.groups=new Map,this.handleResizer=this.handleResize.bind(this),this.scrollScheduled=!1,this.handleScroller=this.handleScroll.bind(this)}cleanup(){this.groups.forEach(e=>e.destroy()),this.groups.clear(),this.observedResizeTargets.clear()}updateHighlightStyles(){this.groups.forEach(e=>{e.requestLayout()})}handleResize(){this.wnd.clearTimeout(this.resizeFrame),this.resizeFrame=this.wnd.setTimeout(()=>{this.groups.forEach(e=>e.requestLayout())},50)}handleScroll(){this.scrollScheduled||(this.scrollScheduled=!0,this.wnd.requestAnimationFrame(()=>{this.scrollScheduled=!1,this.groups.forEach(e=>{e.hasOverlayItems&&e.repositionOverlays()})}))}mount(e,t){return this.wnd=e,t.register("decorate",R.moduleName,(i,o)=>{const s=i;(s.action==="add"||s.action==="update")&&s.decoration.locator&&(s.decoration.locator=K.deserialize(s.decoration.locator)),this.groups.has(s.group)||this.groups.set(s.group,new Y(e,t,`readium-decoration-${this.lastGroupId++}`,s.group));const n=this.groups.get(s.group);switch(s.action){case"add":n?.add(s.decoration);break;case"remove":n?.remove(s.decoration.id);break;case"clear":n?.clear();break;case"update":n?.update(s.decoration);break}o(!0)}),t.register("decoration_resize",R.moduleName,(i,o)=>{const s=i;s.action==="watch"?this.addDecorationResizeTarget(s.selector):this.removeDecorationResizeTarget(s.selector),o(!0)}),t.register("decoration_activatable",R.moduleName,(i,o)=>{const s=i,n=this.groups.get(s.group);n&&(n.activatable=s.activatable),o(!0)}),t.register("decoration_hoverable",R.moduleName,(i,o)=>{const s=i,n=this.groups.get(s.group);n&&(n.hoverable=s.hoverable),o(!0)}),this.resizeObserver=new ResizeObserver(()=>e.requestAnimationFrame(()=>this.handleResize())),this.resizeObserver.observe(e.document.documentElement),e.addEventListener("orientationchange",this.handleResizer),e.addEventListener("resize",this.handleResizer),e.addEventListener("scroll",this.handleScroller,{passive:!0,capture:!0}),this.styleObserver=new MutationObserver(i=>{i.some(s=>s.type==="attributes"&&s.attributeName==="style"&&s.oldValue!==s.target.getAttribute("style"))&&this.updateHighlightStyles()}),this.styleObserver.observe(e.document.documentElement,{attributes:!0,attributeFilter:["style"],attributeOldValue:!0}),t.log("Decorator Mounted"),!0}unmount(e,t){return e.removeEventListener("orientationchange",this.handleResizer),e.removeEventListener("resize",this.handleResizer),e.removeEventListener("scroll",this.handleScroller,{capture:!0}),t.unregisterAll(R.moduleName),this.resizeObserver.disconnect(),this.styleObserver.disconnect(),this.cleanup(),t.log("Decorator Unmounted"),!0}addDecorationResizeTarget(e){const t=this.observedResizeTargets.get(e);t&&this.resizeObserver.unobserve(t);const i=this.wnd.document.querySelector(e);i?(this.resizeObserver.observe(i),this.observedResizeTargets.set(e,i)):this.observedResizeTargets.delete(e)}removeDecorationResizeTarget(e){const t=this.observedResizeTargets.get(e);t&&(this.resizeObserver.unobserve(t),this.observedResizeTargets.delete(e))}};R.moduleName="decorator";let A=R;export{X as DecorationLayout,d as DecorationStyleType,G as DecorationWidth,A as Decorator};
24
+ `)}positionElement(e,t,i,o,s,n,r=0){const p=e.decoration?.style?.width,a=s;switch(p){case"viewport":{const g=Math.floor(t.inlineStart(a)/t.viewportInlineSize)*t.viewportInlineSize;t.applyPosition(o,g+t.inlineScrollOffset+r,t.blockStart(a)+t.blockScrollOffset,t.viewportInlineSize-2*r,t.blockSize(a),i);break}case"page":{const g=Math.floor(t.inlineStart(a)/t.pageInlineSize)*t.pageInlineSize;t.applyPosition(o,g+t.inlineScrollOffset+r,t.blockStart(a)+t.blockScrollOffset,t.pageInlineSize-2*r,t.blockSize(a),i);break}case"bounds":{t.applyPosition(o,t.inlineStart(n)+t.inlineScrollOffset,t.blockStart(a)+t.blockScrollOffset,t.inlineSize(n),t.blockSize(a),i);break}default:t.applyPosition(o,t.inlineStart(a)+t.inlineScrollOffset,t.blockStart(a)+t.blockScrollOffset,t.inlineSize(a),t.blockSize(a),i)}}computeOverlayRects(e,t,i,o){if(e.decoration?.style?.layout==="bounds")return[o?{left:i.left-o,right:i.right+o,top:i.top-o,bottom:i.bottom+o,width:i.width+o*2,height:i.height+o*2}:i];const n=e.decoration.style.type,r=n===d.Underline||n===d.Strikethrough,p=n===d.Strikethrough,a=r?q(e.range,["rt","rp"]):e.range;let g=z(a,!0,t.isVertical,r?0:o);return g=g.sort((l,h)=>t.isVertical?(t.isVertLR?1:-1)*(l.left-h.left):l.top-h.top),g.map(l=>{let h=l;if(p){const u=t.blockSize(l)*.1,m=t.blockStart(l)+t.blockSize(l)/2-u/2;h=t.isVertical?{left:m,right:m+u,top:l.top,bottom:l.bottom,width:u,height:l.height}:{top:m,bottom:m+u,left:l.left,right:l.right,height:u,width:l.width}}return o&&r&&(h=t.isVertical?{...h,top:h.top-o,bottom:h.bottom+o,height:h.height+o*2}:{...h,left:h.left-o,right:h.right+o,width:h.width+o*2}),h})}repositionItem(e,t){if(!e.container)return!0;const i=e.decoration.style;if(i.type!==d.Template){const g=i.type??d.Highlight;if(g===d.TextColor||g===d.Mask)return!0}const o=1/this.effectiveZoom(),s=i.expand??0,n=e.range.getBoundingClientRect(),r=(()=>{if(i.type!==d.Outline)return 0;const g=i.width;return g==="page"||g==="viewport"?3:0})(),p=this.computeOverlayRects(e,t,n,s),a=Array.from(e.container.children);return a.length!==p.length?!1:(a.forEach((g,l)=>this.positionElement(e,t,o,g,p[l],n,r)),!0)}layout(e){if(this.experimentalHighlights&&!this.notTextFlag?.has(e.id))return this.experimentalLayout(e);const t=this.wnd.document.createElement("div");t.setAttribute("id",e.id),t.dataset.highlightId=e.decoration.id,t.style.setProperty("pointer-events","none");const i=T(this.wnd),o=1/this.effectiveZoom(),s=e.decoration.style.expand??0,n=e.range.getBoundingClientRect(),r=e.decoration.style,p=(()=>{if(r.type!==d.Outline)return 0;const l=r.width;return l==="page"||l==="viewport"?3:0})();let a;if(r.type===d.Template){r.stylesheet&&this.injectCustomStylesheet(r.stylesheet);const l=j(this.wnd,r.element);if(!l){e.container=t,e.clickableElements=[];return}l.style.setProperty("pointer-events","none"),a=l}else{const l=r,h=l.type??d.Highlight,u=l.tint??$(h);if(h===d.TextColor){e.container=t,e.clickableElements=[];return}if(h===d.Mask){e.container=t,e.clickableElements=[],this.updateSharedMask();return}const b=this.getCurrentDarkMode(),m=this.getBackgroundColor(),c=l.enforceContrast!==!1,f=(()=>{switch(h){case d.Underline:{const y=c?E(u,m):u,w=l.layout==="bounds",[C,x]=i.isVertical?["border-right","border-left"]:["border-bottom","border-top"];return[w?`${x}: 0.1em solid ${y} !important`:null,`${C}: 0.1em solid ${y} !important`,"background-color: transparent !important","box-sizing: border-box !important"].filter(Boolean).join("; ")}case d.Strikethrough:{const y=c?E(u,m):u;return l.layout==="bounds"?[`background: repeating-linear-gradient(-45deg, transparent, transparent 19px, ${y} 19px, ${y} 20px) !important`,"background-color: transparent !important","box-sizing: border-box !important"].join("; "):[`background-color: ${y} !important`,"box-sizing: border-box !important"].join("; ")}case d.Outline:return[`outline: 2px solid ${c?E(u,m):u} !important`,"outline-offset: 1px !important","background-color: transparent !important","box-sizing: border-box !important"].join("; ");case d.HighlightUnderline:{const y=c?E(u,m):u,{r:w,g:C,b:x}=M(y),N=`rgba(${w}, ${C}, ${x}, 0.3)`,L=l.layout==="bounds",[_,B]=i.isVertical?["border-right","border-left"]:["border-bottom","border-top"];return[`background-color: ${N} !important`,L?`${B}: 0.1em solid ${y} !important`:null,`${_}: 0.1em solid ${y} !important`,"box-sizing: border-box !important"].filter(Boolean).join("; ")}case d.Highlight:default:return[`background-color: ${c?E(u,m):u} !important`,`mix-blend-mode: ${b?"exclusion":"multiply"} !important`,"opacity: 1 !important","box-sizing: border-box !important","transform: translateZ(0) !important"].join("; ")}})(),k=this.wnd.document.createElement("template");k.innerHTML=`<div data-readium="true" class="readium-${h}" style="${f}"></div>`.trim(),a=k.content.firstElementChild}const g=this.computeOverlayRects(e,i,n,s);for(const l of g){const h=a.cloneNode(!0);h.style.setProperty("pointer-events","none"),this.positionElement(e,i,o,h,l,n,p),t.append(h)}e.container=t,e.clickableElements=Array.from(t.querySelectorAll("[data-activable='1']")),e.clickableElements.length||(e.clickableElements=Array.from(t.children))}renderLayout(e){this.wnd.cancelAnimationFrame(this.currentRender),this.currentRender=this.wnd.requestAnimationFrame(()=>{if(e=e.filter(i=>!this.experimentalHighlights||!!this.notTextFlag?.has(i.id)),!e||e.length===0)return;this.requireContainer().append(...e.map(i=>i.container).filter(i=>!!i))})}requireContainer(e=!1){if(e){let t;return this.wnd.document.getElementById(`${this.id}-style`)?t=this.wnd.document.getElementById(`${this.id}-style`):(t=this.wnd.document.createElement("style"),t.dataset.readium="true",t.id=`${this.id}-style`,this.wnd.document.head.appendChild(t)),t}return this.container||(this.shadowRoot||(this.shadowHost=this.wnd.document.createElement("div"),this.shadowHost.style.cssText="position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none",this.wnd.document.body.appendChild(this.shadowHost),this.shadowRoot=this.shadowHost.attachShadow({mode:"open"})),this.container=this.wnd.document.createElement("div"),this.container.setAttribute("id",this.id),this.container.dataset.group=this.name,this.container.dataset.readium="true",this.container.style.setProperty("pointer-events","none"),this.container.style.display="contents",this.shadowRoot.appendChild(this.container)),this.container}getCurrentDarkMode(){return I(this.wnd,"--USER__appearance")==="readium-night-on"||W(this.getBackgroundColor())}getBackgroundColor(){return I(this.wnd,"--USER__backgroundColor")||this.wnd.getComputedStyle(this.wnd.document.documentElement).getPropertyValue("background-color")}updateSharedMask(){const e=this.items.filter(h=>h.decoration.style?.type===d.Mask);if(e.length===0){this.maskSvg&&(this.maskSvg.remove(),this.maskSvg=void 0),this.shadowHost&&!this.container&&(this.shadowHost.remove(),this.shadowRoot=void 0,this.shadowHost=void 0);return}const t=T(this.wnd),i=1/this.effectiveZoom(),o=this.wnd.document.documentElement,s=o.scrollWidth,n=o.scrollHeight,r=[];for(const h of e){const u=h.decoration.style,b=u.layout??"boxes",m=u.width??"wrap",c=u.expand??0,f=h.range.getBoundingClientRect(),k=b==="bounds"?[c?{left:f.left-c,top:f.top-c,right:f.right+c,bottom:f.bottom+c,width:f.width+c*2,height:f.height+c*2}:f]:z(h.range,!1,!1,c);for(const v of k){let y;switch(m){case"viewport":{const w=Math.floor(t.inlineStart(v)/t.viewportInlineSize)*t.viewportInlineSize;y=t.toRect(w,t.blockStart(v),t.viewportInlineSize,t.blockSize(v));break}case"page":{const w=Math.floor(t.inlineStart(v)/t.pageInlineSize)*t.pageInlineSize;y=t.toRect(w,t.blockStart(v),t.pageInlineSize,t.blockSize(v));break}case"bounds":{y=t.toRect(t.inlineStart(f),t.blockStart(v),t.inlineSize(f),t.blockSize(v));break}default:y=t.toRect(t.inlineStart(v),t.blockStart(v),t.inlineSize(v),t.blockSize(v))}r.push(y)}}const p=[`M0 0 H${s} V${n} H0 Z`,...r.map(h=>{const u=(h.left+t.xDocOffset)*i,b=(h.top+t.yDocOffset)*i,m=(h.right+t.xDocOffset)*i,c=(h.bottom+t.yDocOffset)*i;return`M${u} ${b} H${m} V${c} H${u} Z`})].join(" "),a="http://www.w3.org/2000/svg";if(!this.maskSvg){this.shadowRoot||(this.shadowHost=this.wnd.document.createElement("div"),this.shadowHost.style.cssText="position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none",this.wnd.document.body.appendChild(this.shadowHost),this.shadowRoot=this.shadowHost.attachShadow({mode:"open"})),this.maskSvg=this.wnd.document.createElementNS(a,"svg"),this.maskSvg.style.cssText=`position:absolute;top:0;left:0;width:${s}px;height:${n}px;pointer-events:none;z-index:2147483647`,this.maskSvg.dataset.readium="true";const h=this.wnd.document.createElementNS(a,"defs"),u=this.wnd.document.createElementNS(a,"clipPath"),b=`${this.id}-mask-clip`;u.setAttribute("id",b),u.setAttribute("clipPathUnits","userSpaceOnUse");const m=this.wnd.document.createElementNS(a,"path");m.setAttribute("clip-rule","evenodd"),u.appendChild(m),h.appendChild(u),this.maskSvg.appendChild(h);const c=this.wnd.document.createElementNS(a,"rect");c.setAttribute("id",`${this.id}-mask-rect`),c.setAttribute("clip-path",`url(#${b})`),c.style.pointerEvents="none",this.maskSvg.appendChild(c),this.shadowRoot.appendChild(this.maskSvg)}this.maskSvg.style.width=`${s}px`,this.maskSvg.style.height=`${n}px`;const g=this.maskSvg.querySelector("path");g&&g.setAttribute("d",p);const l=this.maskSvg.querySelector("rect");if(l){const u=e[0].decoration.style.tint,b=u??this.getBackgroundColor()??$(d.Mask),m=u?"1":"0.5";l.setAttribute("x","0"),l.setAttribute("y","0"),l.setAttribute("width",String(s)),l.setAttribute("height",String(n)),l.setAttribute("fill",b),l.setAttribute("fill-opacity",m)}}injectCustomStylesheet(e){const t=`${this.id}-custom-style`;let i=this.wnd.document.getElementById(t);i||(i=this.wnd.document.createElement("style"),i.id=t,i.dataset.readium="true",this.wnd.document.head.appendChild(i)),i.innerHTML=e}clearContainer(){if(this.experimentalHighlights){const e=this.wnd.CSS.highlights;for(const t of this._tintSubKeys.values())e.delete(t);this._tintSubKeys.clear(),this._subKeyCounter=0}this.wnd.document.getElementById(`${this.id}-custom-style`)?.remove(),this.container&&(this.container.remove(),this.container=void 0)}}const R=class R extends K{constructor(){super(...arguments),this.observedResizeTargets=new Map,this.resizeFrame=0,this.lastGroupId=0,this.groups=new Map,this.handleResizer=this.handleResize.bind(this),this.scrollScheduled=!1,this.handleScroller=this.handleScroll.bind(this)}cleanup(){this.groups.forEach(e=>e.destroy()),this.groups.clear(),this.observedResizeTargets.clear()}updateHighlightStyles(){this.groups.forEach(e=>{e.requestLayout()})}handleResize(){this.wnd.clearTimeout(this.resizeFrame),this.resizeFrame=this.wnd.setTimeout(()=>{this.groups.forEach(e=>e.requestLayout())},50)}handleScroll(){this.scrollScheduled||(this.scrollScheduled=!0,this.wnd.requestAnimationFrame(()=>{this.scrollScheduled=!1,this.groups.forEach(e=>{e.hasOverlayItems&&e.repositionOverlays()})}))}mount(e,t){return this.wnd=e,t.register("decorate",R.moduleName,(i,o)=>{const s=i;(s.action==="add"||s.action==="update")&&s.decoration.locator&&(s.decoration.locator=D.deserialize(s.decoration.locator)),this.groups.has(s.group)||this.groups.set(s.group,new Y(e,t,`readium-decoration-${this.lastGroupId++}`,s.group));const n=this.groups.get(s.group);switch(s.action){case"add":n?.add(s.decoration);break;case"remove":n?.remove(s.decoration.id);break;case"clear":n?.clear();break;case"update":n?.update(s.decoration);break}o(!0)}),t.register("decoration_resize",R.moduleName,(i,o)=>{const s=i;s.action==="watch"?this.addDecorationResizeTarget(s.selector):this.removeDecorationResizeTarget(s.selector),o(!0)}),t.register("decoration_activatable",R.moduleName,(i,o)=>{const s=i,n=this.groups.get(s.group);n&&(n.activatable=s.activatable),o(!0)}),t.register("decoration_hoverable",R.moduleName,(i,o)=>{const s=i,n=this.groups.get(s.group);n&&(n.hoverable=s.hoverable),o(!0)}),this.resizeObserver=new ResizeObserver(()=>e.requestAnimationFrame(()=>this.handleResize())),this.resizeObserver.observe(e.document.documentElement),e.addEventListener("orientationchange",this.handleResizer),e.addEventListener("resize",this.handleResizer),e.addEventListener("scroll",this.handleScroller,{passive:!0,capture:!0}),this.styleObserver=new MutationObserver(i=>{i.some(s=>s.type==="attributes"&&s.attributeName==="style"&&s.oldValue!==s.target.getAttribute("style"))&&this.updateHighlightStyles()}),this.styleObserver.observe(e.document.documentElement,{attributes:!0,attributeFilter:["style"],attributeOldValue:!0}),t.log("Decorator Mounted"),!0}unmount(e,t){return e.removeEventListener("orientationchange",this.handleResizer),e.removeEventListener("resize",this.handleResizer),e.removeEventListener("scroll",this.handleScroller,{capture:!0}),t.unregisterAll(R.moduleName),this.resizeObserver.disconnect(),this.styleObserver.disconnect(),this.cleanup(),t.log("Decorator Unmounted"),!0}addDecorationResizeTarget(e){const t=this.observedResizeTargets.get(e);t&&this.resizeObserver.unobserve(t);const i=this.wnd.document.querySelector(e);i?(this.resizeObserver.observe(i),this.observedResizeTargets.set(e,i)):this.observedResizeTargets.delete(e)}removeDecorationResizeTarget(e){const t=this.observedResizeTargets.get(e);t&&(this.resizeObserver.unobserve(t),this.observedResizeTargets.delete(e))}};R.moduleName="decorator";let A=R;export{Z as DecorationLayout,d as DecorationStyleType,V as DecorationWidth,A as Decorator};
@@ -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.8.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
+ 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.8.4",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{Snapper as C}from"./Snapper.js";import{getColumnCountPerScreen as x,isRTL as A,appendVirtualColumnIfNeeded as W}from"../../helpers/document.js";import{styleChangeAffectsLayout as F}from"../../helpers/css.js";import{easeInOutQuad as P}from"../../helpers/animation.js";import{LocatorText as N,Locator as M,LocatorLocations as z}from"@readium/shared";import{rangeFromLocator as _}from"../../helpers/locator.js";import{deselect as m,findFirstVisibleLocator as X}from"../../helpers/dom.js";import{PatternAnalyzer as w}from"../../protection/PatternAnalyzer.js";const T="readium-column-snapper-style",q=200,h=class h extends C{constructor(){super(...arguments),this.isSnapProtectionEnabled=!1,this.patternAnalyzer=null,this.lastTurnTime=0,this.rtl=!1,this.shakeTimeout=0,this.snappingCancelled=!1,this.alreadyScrollLeft=0,this.overscroll=0,this.cachedScrollWidth=0,this.touchState=0,this.startingX=void 0,this.endingX=void 0,this.onTouchStarter=this.onTouchStart.bind(this),this.onTouchEnder=this.onTouchEnd.bind(this),this.onWidthChanger=this.onWidthChange.bind(this),this.onTouchMover=this.onTouchMove.bind(this)}doc(){return this.wnd.document.scrollingElement}scrollOffset(){const t=this.doc().scrollLeft;return t!==0?t:this.alreadyScrollLeft}snapOffset(t){const e=t+(this.rtl?-1:1);return e-e%this.wnd.innerWidth}snapNormOffset(t){const e=t+1;return e-e%this.wnd.innerWidth}normScroll(){const t=this.doc().scrollLeft||this.alreadyScrollLeft;return this.rtl?Math.abs(t):Math.max(0,this.wnd.scrollX>0?this.wnd.scrollX:t)}hasScrolledPast(t){const e=t.getBoundingClientRect();return this.rtl?e.left>=this.wnd.innerWidth:e.right<=0}currentTimelineFragment(){if(this.timelineEntries.size===0)return;const t=this.wnd.innerWidth;let e;for(const r of this.sortedFragmentIds){const l=this.timelineEntries.get(r).getBoundingClientRect();if(this.rtl?l.right>0:l.left<t)e=r;else break}return e}sortedVisibleFragmentIds(){const t=this.wnd.innerWidth;return this.sortedFragmentIds.filter(e=>{const r=this.timelineEntries.get(e).getBoundingClientRect();return this.rtl?r.left<t&&r.right>0:r.right>0&&r.left<t})}reportProgress(t){const e=this.cachedScrollWidth,r=this.wnd.innerWidth,n=this.normScroll(),l=Math.max(1,e-r),i=Math.max(0,Math.min(1,n/l)),s=Math.max(0,Math.min(1,(n+r)/e));this.comms.send("progress",{start:i,end:s,fragmentId:t!==void 0?t:this.currentTimelineFragment(),visibleFragmentIds:this.sortedVisibleFragmentIds()})}shake(){if(this.overscroll!==0||this.shakeTimeout!==0)return;const t=this.doc(),e=this.normScroll()<5,r=this.rtl?e?"readium-bounce-r":"readium-bounce-l":"readium-bounce-r";t.classList.add(r);const n=this.scrollOffset();this.shakeTimeout=this.wnd.setTimeout(()=>{t.classList.remove(r),this.shakeTimeout=0,this.doc().scrollLeft=n},150)}takeOverSnap(){this.snappingCancelled=!0,this.clearTouches();const t=this.doc();this.overscroll=t.style.transform?.length>12?parseFloat(t.style.transform.slice(12).split("px")[0]):0}snapCurrentOffset(t=!1,e=!1){const r=this.doc(),n=x(this.wnd),l=this.cachedScrollWidth-this.wnd.innerWidth,i=Math.min(Math.max(0,this.normScroll()),l),s=this.dragOffset(),o=this.rtl?-s:s,d=this.wnd.innerWidth/3*(o>0?2:1),a=Math.min(l,Math.max(0,this.snapNormOffset(i+d))),c=this.rtl?-a:a,u=this.rtl?-i:i,O=c>u?"right":"left";if(this.checkSuspiciousSnap(O,Math.abs(c-u)),t&&c!==u){this.snappingCancelled=!1;const L=(p,f,g,S)=>g>S?f:p+(f-p)*P(g/S),v=q*n;let b;const y=p=>{if(this.snappingCancelled)return;b||(b=p);const f=p-b,g=L(this.overscroll,0,f,v),S=L(u,c,f,v);r.scrollLeft=S,this.overscroll!==0&&(r.style.transform=`translate3d(${-g}px, 0px, 0px)`),f<v?this.wnd.requestAnimationFrame(y):(this.clearTouches(),r.style.removeProperty("transform"),r.scrollLeft=c,e||this.reportProgress())};this.wnd.requestAnimationFrame(y)}else r.style.removeProperty("transform"),this.wnd.requestAnimationFrame(()=>{r.scrollLeft=c,this.clearTouches(),e||this.reportProgress()})}dragOffset(){return(this.startingX??0)-(this.endingX??0)}clearTouches(){this.startingX=void 0,this.endingX=void 0,this.overscroll=0}onTouchStart(t){switch(t.stopPropagation(),this.takeOverSnap(),t.touches.length){case 1:break;case 2:this.onTouchEnd(t);return;default:{this.onTouchEnd(t),this.comms.send("tap_more",t.touches.length);return}}this.startingX=t.touches[0].clientX,this.alreadyScrollLeft=this.doc().scrollLeft,this.touchState=1}onTouchEnd(t){if(this.touchState===2){const e=this.dragOffset(),r=this.normScroll(),n=this.cachedScrollWidth-this.wnd.innerWidth,l=this.rtl?-e:e;this.cachedScrollWidth<=this.wnd.innerWidth?(this.reportProgress(),l>5&&this.comms.send("no_more",void 0),l<-5&&this.comms.send("no_less",void 0)):r<5&&l<5?(this.alreadyScrollLeft=0,this.comms.send("no_less",void 0)):n-r<5&&l>5&&(this.alreadyScrollLeft=this.rtl?-n:n,this.comms.send("no_more",void 0)),this.snapCurrentOffset(!0),this.comms.send("swipe",e)}this.touchState=0}onWidthChange(){this.cachedScrollWidth=this.doc().scrollWidth,this.comms.ready&&this.snapCurrentOffset()}onTouchMove(t){if(this.touchState===0)return;this.touchState===1&&(this.touchState=2,m(this.wnd)),this.endingX=t.touches[0].clientX;const e=this.dragOffset(),r=this.alreadyScrollLeft+e,n=this.rtl?-(this.cachedScrollWidth-this.wnd.innerWidth):0,l=this.rtl?0:this.cachedScrollWidth-this.wnd.innerWidth;r<n?(this.overscroll=r,this.doc().style.transform=`translate3d(${-this.overscroll}px, 0px, 0px)`):r>l?(this.overscroll=r,this.doc().style.transform=`translate3d(${-r}px, 0px, 0px)`):(this.overscroll=0,this.doc().style.removeProperty("transform"),this.doc().scrollLeft=r)}enableSnapProtection(){this.patternAnalyzer||(this.patternAnalyzer=new w({maxVelocity:this.wnd.innerWidth,minVariance:.1,historySize:5,maxConsistentScrolls:3,minDirectionChanges:.3}),this.isSnapProtectionEnabled=!0,this.comms?.log("Snap protection enabled"))}checkSuspiciousSnap(t,e){if(!this.isSnapProtectionEnabled||!this.patternAnalyzer)return;const r=Date.now(),n=r-(this.lastTurnTime||r);this.lastTurnTime=r,this.patternAnalyzer.analyze(t,e,n)&&this.comms?.send("content_protection",{type:"suspicious_snapping",timestamp:Date.now(),event:null})}mount(t,e){if(this.wnd=t,this.comms=e,this.rtl=A(t),!super.mount(t,e))return!1;t.navigator.epubReadingSystem&&(t.navigator.epubReadingSystem.layoutStyle="paginated");const r=t.document.createElement("style");r.dataset.readium="true",r.id=T,r.textContent=`
1
+ import{Snapper as O}from"./Snapper.js";import{getColumnCountPerScreen as A,isRTL as F,appendVirtualColumnIfNeeded as C}from"../../helpers/document.js";import{isTypedOMSupported as y,styleChangeAffectsLayout as P}from"../../helpers/css.js";import{easeInOutQuad as N}from"../../helpers/animation.js";import{LocatorText as M,Locator as w,LocatorLocations as z}from"@readium/shared";import{rangeFromLocator as _}from"../../helpers/locator.js";import{deselect as m,findFirstVisibleLocator as X}from"../../helpers/dom.js";import{PatternAnalyzer as q}from"../../protection/PatternAnalyzer.js";const T="readium-column-snapper-style",I=200,h=class h extends O{constructor(){super(...arguments),this.isSnapProtectionEnabled=!1,this.patternAnalyzer=null,this.lastTurnTime=0,this.rtl=!1,this.shakeTimeout=0,this.snappingCancelled=!1,this.alreadyScrollLeft=0,this.overscroll=0,this.cachedScrollWidth=0,this.touchState=0,this.startingX=void 0,this.endingX=void 0,this.onTouchStarter=this.onTouchStart.bind(this),this.onTouchEnder=this.onTouchEnd.bind(this),this.onWidthChanger=this.onWidthChange.bind(this),this.onTouchMover=this.onTouchMove.bind(this)}doc(){return this.wnd.document.scrollingElement}scrollOffset(){const t=this.doc().scrollLeft;return t!==0?t:this.alreadyScrollLeft}snapOffset(t){const e=t+(this.rtl?-1:1);return e-e%this.wnd.innerWidth}snapNormOffset(t){const e=t+1;return e-e%this.wnd.innerWidth}normScroll(){const t=this.doc().scrollLeft||this.alreadyScrollLeft;return this.rtl?Math.abs(t):Math.max(0,this.wnd.scrollX>0?this.wnd.scrollX:t)}hasScrolledPast(t){const e=t.getBoundingClientRect();return this.rtl?e.left>=this.wnd.innerWidth:e.right<=0}currentTimelineFragment(){if(this.timelineEntries.size===0)return;const t=this.wnd.innerWidth;let e;for(const s of this.sortedFragmentIds){const l=this.timelineEntries.get(s).getBoundingClientRect();if(this.rtl?l.right>0:l.left<t)e=s;else break}return e}sortedVisibleFragmentIds(){const t=this.wnd.innerWidth;return this.sortedFragmentIds.filter(e=>{const s=this.timelineEntries.get(e).getBoundingClientRect();return this.rtl?s.left<t&&s.right>0:s.right>0&&s.left<t})}reportProgress(t){const e=this.cachedScrollWidth,s=this.wnd.innerWidth,n=this.normScroll(),l=Math.max(1,e-s),i=Math.max(0,Math.min(1,n/l)),r=Math.max(0,Math.min(1,(n+s)/e));this.comms.send("progress",{start:i,end:r,fragmentId:t!==void 0?t:this.currentTimelineFragment(),visibleFragmentIds:this.sortedVisibleFragmentIds()})}shake(){if(this.overscroll!==0||this.shakeTimeout!==0)return;const t=this.doc(),e=this.normScroll()<5,s=this.rtl?e?"readium-bounce-r":"readium-bounce-l":"readium-bounce-r";t.classList.add(s);const n=this.scrollOffset();this.shakeTimeout=this.wnd.setTimeout(()=>{t.classList.remove(s),this.shakeTimeout=0,this.doc().scrollLeft=n},150)}takeOverSnap(){this.snappingCancelled=!0,this.clearTouches();const t=this.doc();this.overscroll=t.style.transform?.length>12?parseFloat(t.style.transform.slice(12).split("px")[0]):0}snapCurrentOffset(t=!1,e=!1){const s=this.doc(),n=A(this.wnd),l=this.cachedScrollWidth-this.wnd.innerWidth,i=Math.min(Math.max(0,this.normScroll()),l),r=this.dragOffset(),o=this.rtl?-r:r,d=this.wnd.innerWidth/3*(o>0?2:1),a=Math.min(l,Math.max(0,this.snapNormOffset(i+d))),c=this.rtl?-a:a,u=this.rtl?-i:i,E=c>u?"right":"left";if(this.checkSuspiciousSnap(E,Math.abs(c-u)),t&&c!==u){this.snappingCancelled=!1;const L=(p,f,g,S)=>g>S?f:p+(f-p)*N(g/S),v=I*n;let b;const W=p=>{if(this.snappingCancelled)return;b||(b=p);const f=p-b,g=L(this.overscroll,0,f,v),S=L(u,c,f,v);s.scrollLeft=S,this.overscroll!==0&&(y()?s.attributeStyleMap.set("transform",new CSSTransformValue([new CSSTranslate(CSS.px(-g),CSS.px(0),CSS.px(0))])):s.style.transform=`translate3d(${-g}px, 0px, 0px)`),f<v?this.wnd.requestAnimationFrame(W):(this.clearTouches(),s.style.removeProperty("transform"),s.scrollLeft=c,e||this.reportProgress())};this.wnd.requestAnimationFrame(W)}else s.style.removeProperty("transform"),this.wnd.requestAnimationFrame(()=>{s.scrollLeft=c,this.clearTouches(),e||this.reportProgress()})}dragOffset(){return(this.startingX??0)-(this.endingX??0)}clearTouches(){this.startingX=void 0,this.endingX=void 0,this.overscroll=0}onTouchStart(t){switch(t.stopPropagation(),this.takeOverSnap(),t.touches.length){case 1:break;case 2:this.onTouchEnd(t);return;default:{this.onTouchEnd(t),this.comms.send("tap_more",t.touches.length);return}}this.startingX=t.touches[0].clientX,this.alreadyScrollLeft=this.doc().scrollLeft,this.touchState=1}onTouchEnd(t){if(this.touchState===2){const e=this.dragOffset(),s=this.normScroll(),n=this.cachedScrollWidth-this.wnd.innerWidth,l=this.rtl?-e:e;this.cachedScrollWidth<=this.wnd.innerWidth?(this.reportProgress(),l>5&&this.comms.send("no_more",void 0),l<-5&&this.comms.send("no_less",void 0)):s<5&&l<5?(this.alreadyScrollLeft=0,this.comms.send("no_less",void 0)):n-s<5&&l>5&&(this.alreadyScrollLeft=this.rtl?-n:n,this.comms.send("no_more",void 0)),this.snapCurrentOffset(!0),this.comms.send("swipe",e)}this.touchState=0}onWidthChange(){this.cachedScrollWidth=this.doc().scrollWidth,this.comms.ready&&this.snapCurrentOffset()}onTouchMove(t){if(this.touchState===0)return;this.touchState===1&&(this.touchState=2,m(this.wnd)),this.endingX=t.touches[0].clientX;const e=this.dragOffset(),s=this.alreadyScrollLeft+e,n=this.rtl?-(this.cachedScrollWidth-this.wnd.innerWidth):0,l=this.rtl?0:this.cachedScrollWidth-this.wnd.innerWidth;s<n?(this.overscroll=s,y()?this.doc().attributeStyleMap.set("transform",new CSSTransformValue([new CSSTranslate(CSS.px(-s),CSS.px(0),CSS.px(0))])):this.doc().style.transform=`translate3d(${-s}px, 0px, 0px)`):s>l?(this.overscroll=s,y()?this.doc().attributeStyleMap.set("transform",new CSSTransformValue([new CSSTranslate(CSS.px(-s),CSS.px(0),CSS.px(0))])):this.doc().style.transform=`translate3d(${-s}px, 0px, 0px)`):(this.overscroll=0,this.doc().style.removeProperty("transform"),this.doc().scrollLeft=s)}enableSnapProtection(){this.patternAnalyzer||(this.patternAnalyzer=new q({maxVelocity:this.wnd.innerWidth,minVariance:.1,historySize:5,maxConsistentScrolls:3,minDirectionChanges:.3}),this.isSnapProtectionEnabled=!0,this.comms?.log("Snap protection enabled"))}checkSuspiciousSnap(t,e){if(!this.isSnapProtectionEnabled||!this.patternAnalyzer)return;const s=Date.now(),n=s-(this.lastTurnTime||s);this.lastTurnTime=s,this.patternAnalyzer.analyze(t,e,n)&&this.comms?.send("content_protection",{type:"suspicious_snapping",timestamp:Date.now(),event:null})}mount(t,e){if(this.wnd=t,this.comms=e,this.rtl=F(t),!super.mount(t,e))return!1;t.navigator.epubReadingSystem&&(t.navigator.epubReadingSystem.layoutStyle="paginated");const s=t.document.createElement("style");s.dataset.readium="true",s.id=T,s.textContent=`
2
2
  @keyframes readium-bounce-l-animation {
3
3
  0%, 100% {transform: translate3d(0, 0, 0);}
4
4
  50% {transform: translate3d(-50px, 0, 0);}
@@ -32,4 +32,4 @@ import{Snapper as C}from"./Snapper.js";import{getColumnCountPerScreen as x,isRTL
32
32
  body::-webkit-scrollbar {
33
33
  display: none; /* for Chrome, Safari, and Opera */
34
34
  }
35
- `,t.document.head.appendChild(r),this.resizeObserver=new ResizeObserver(()=>{t.requestAnimationFrame(()=>{t&&W(t)}),this.onWidthChange()}),this.resizeObserver.observe(t.document.body),this.mutationObserver=new MutationObserver(i=>{for(const s of i)if(s.target===this.wnd.document.documentElement){const o=s.oldValue,d=s.target.getAttribute("style"),a=/transform\s*:\s*([^;]+)/,c=o?.match(a),u=d?.match(a);(!c&&!u&&F(o,d)||c&&!u||c&&u&&c[1]!==u[1])&&(t.requestAnimationFrame(()=>{t&&W(t)}),this.onWidthChange())}else t.requestAnimationFrame(()=>this.cachedScrollWidth=this.doc().scrollWidth)}),t.frameElement&&this.mutationObserver.observe(t.frameElement,{attributes:!0,attributeFilter:["style"]}),this.mutationObserver.observe(t.document,{attributes:!0,attributeFilter:["style"]}),this.mutationObserver.observe(t.document.documentElement,{attributes:!0,attributeFilter:["style"]}),e.register("scroll_protection",h.moduleName,(i,s)=>{this.enableSnapProtection(),s(!0)});const n=i=>{const s=this.doc().scrollLeft;return this.doc().scrollLeft=this.snapOffset(i),s!==this.doc().scrollLeft},l=i=>{const s=this.doc().scrollLeft,o=this.snapNormOffset(Math.max(0,Math.min(this.cachedScrollWidth-t.innerWidth,i)));return this.doc().scrollLeft=-o,s!==this.doc().scrollLeft};return t.addEventListener("orientationchange",this.onWidthChanger),t.addEventListener("resize",this.onWidthChanger),t.requestAnimationFrame(()=>this.cachedScrollWidth=this.doc().scrollWidth),e.register("go_progression",h.moduleName,(i,s)=>{const o=i;if(o<0||o>1){e.send("error",{message:"go_progression must be given a position from 0.0 to 1.0"}),s(!1);return}this.wnd.requestAnimationFrame(()=>{this.cachedScrollWidth=this.doc().scrollWidth;const a=(this.cachedScrollWidth-t.innerWidth)*o;this.rtl?this.doc().scrollLeft=-this.snapNormOffset(a):this.doc().scrollLeft=this.snapOffset(a),this.reportProgress(),m(this.wnd),s(!0)})}),e.register("go_id",h.moduleName,(i,s)=>{const o=t.document.getElementById(i);if(!o){s(!1);return}this.wnd.requestAnimationFrame(()=>{this.rtl?this.doc().scrollLeft=-this.snapNormOffset(o.getBoundingClientRect().left+t.scrollX):this.doc().scrollLeft=this.snapOffset(o.getBoundingClientRect().left+t.scrollX);const d=i;this.reportProgress(this.timelineEntries.has(d)?d:this.nearestPrecedingTimelineEntry(o)),m(this.wnd),s(!0)})}),e.register("go_text",h.moduleName,(i,s)=>{let o;Array.isArray(i)&&(i.length>1&&(o=i[1]),i=i[0]);const d=N.deserialize(i),a=_(this.wnd.document,new M({href:t.location.href,type:"text/html",text:d,locations:o?new z({otherLocations:new Map([["cssSelector",o]])}):void 0}));if(!a){s(!1);return}this.wnd.requestAnimationFrame(()=>{this.rtl?this.doc().scrollLeft=-this.snapNormOffset(a.getBoundingClientRect().left+t.scrollX):this.doc().scrollLeft=this.snapOffset(a.getBoundingClientRect().left+t.scrollX),this.reportProgress(this.nearestPrecedingTimelineEntry(a.startContainer)),m(this.wnd),s(!0)})}),e.register("go_end",h.moduleName,(i,s)=>{this.wnd.requestAnimationFrame(()=>{this.cachedScrollWidth=this.doc().scrollWidth;let o;if(this.rtl?o=-this.snapNormOffset(this.cachedScrollWidth-t.innerWidth):o=this.snapOffset(this.cachedScrollWidth),this.doc().scrollLeft===o)return s(!1);this.doc().scrollLeft=o,this.reportProgress(this.sortedFragmentIds[this.sortedFragmentIds.length-1]),m(this.wnd),s(!0)})}),e.register("go_start",h.moduleName,(i,s)=>{this.wnd.requestAnimationFrame(()=>{if(this.doc().scrollLeft===0)return s(!1);this.doc().scrollLeft=0,this.reportProgress(this.firstFragmentIfReached()),m(this.wnd),s(!0)})}),e.register("go_prev",h.moduleName,(i,s)=>{this.wnd.requestAnimationFrame(()=>{this.cachedScrollWidth=this.doc().scrollWidth;let o;this.rtl?o=l(this.normScroll()-t.innerWidth):o=n(t.scrollX-t.innerWidth),this.reportProgress(),o&&(m(this.wnd),this.checkSuspiciousSnap("left",this.wnd.innerWidth)),s(o)})}),e.register("go_next",h.moduleName,(i,s)=>{this.wnd.requestAnimationFrame(()=>{this.cachedScrollWidth=this.doc().scrollWidth;let o;this.rtl?o=l(this.normScroll()+t.innerWidth):o=n(t.scrollX+t.innerWidth),this.reportProgress(),o&&(m(this.wnd),this.checkSuspiciousSnap("right",this.wnd.innerWidth)),s(o)})}),e.register("unfocus",h.moduleName,(i,s)=>{this.snappingCancelled=!0,m(this.wnd),s(!0)}),e.register("shake",h.moduleName,(i,s)=>{this.shake(),s(!0)}),e.register("focus",h.moduleName,(i,s)=>{this.wnd.requestAnimationFrame(()=>{this.cachedScrollWidth=this.doc().scrollWidth,this.snapCurrentOffset(!1,!0),this.reportProgress(),s(!0)})}),e.register("first_visible_locator",h.moduleName,(i,s)=>{const o=X(t,!1);this.comms.send("first_visible_locator",o.serialize()),s(!0)}),e.register("timeline_entries",h.moduleName,(i,s)=>{this.updateTimelineEntries(Array.isArray(i)?i:[],t),t.requestAnimationFrame(()=>this.reportProgress()),s(!0)}),t.addEventListener("touchstart",this.onTouchStarter,{passive:!0}),t.addEventListener("touchend",this.onTouchEnder,{passive:!0}),t.addEventListener("touchmove",this.onTouchMover,{passive:!0}),t.document.addEventListener("touchstart",()=>{}),e.log("ColumnSnapper Mounted"),!0}unmount(t,e){return this.snappingCancelled=!0,e.unregisterAll(h.moduleName),this.resizeObserver.disconnect(),this.mutationObserver.disconnect(),this.patternAnalyzer&&(this.patternAnalyzer.clear(),this.patternAnalyzer=null,this.isSnapProtectionEnabled=!1),t.removeEventListener("touchstart",this.onTouchStarter),t.removeEventListener("touchend",this.onTouchEnder),t.removeEventListener("touchmove",this.onTouchMover),t.removeEventListener("orientationchange",this.onWidthChanger),t.removeEventListener("resize",this.onWidthChanger),t.document.getElementById(T)?.remove(),this.timelineEntries.clear(),e.log("ColumnSnapper Unmounted"),super.unmount(t,e)}};h.moduleName="column_snapper";let E=h;export{E as ColumnSnapper};
35
+ `,t.document.head.appendChild(s),this.resizeObserver=new ResizeObserver(()=>{t.requestAnimationFrame(()=>{t&&C(t)}),this.onWidthChange()}),this.resizeObserver.observe(t.document.body),this.mutationObserver=new MutationObserver(i=>{for(const r of i)if(r.target===this.wnd.document.documentElement){const o=r.oldValue,d=r.target.getAttribute("style"),a=/transform\s*:\s*([^;]+)/,c=o?.match(a),u=d?.match(a);(!c&&!u&&P(o,d)||c&&!u||c&&u&&c[1]!==u[1])&&(t.requestAnimationFrame(()=>{t&&C(t)}),this.onWidthChange())}else t.requestAnimationFrame(()=>this.cachedScrollWidth=this.doc().scrollWidth)}),t.frameElement&&this.mutationObserver.observe(t.frameElement,{attributes:!0,attributeFilter:["style"]}),this.mutationObserver.observe(t.document,{attributes:!0,attributeFilter:["style"]}),this.mutationObserver.observe(t.document.documentElement,{attributes:!0,attributeFilter:["style"]}),e.register("scroll_protection",h.moduleName,(i,r)=>{this.enableSnapProtection(),r(!0)});const n=i=>{const r=this.doc().scrollLeft;return this.doc().scrollLeft=this.snapOffset(i),r!==this.doc().scrollLeft},l=i=>{const r=this.doc().scrollLeft,o=this.snapNormOffset(Math.max(0,Math.min(this.cachedScrollWidth-t.innerWidth,i)));return this.doc().scrollLeft=-o,r!==this.doc().scrollLeft};return t.addEventListener("orientationchange",this.onWidthChanger),t.addEventListener("resize",this.onWidthChanger),t.requestAnimationFrame(()=>this.cachedScrollWidth=this.doc().scrollWidth),e.register("go_progression",h.moduleName,(i,r)=>{const o=i;if(o<0||o>1){e.send("error",{message:"go_progression must be given a position from 0.0 to 1.0"}),r(!1);return}this.wnd.requestAnimationFrame(()=>{this.cachedScrollWidth=this.doc().scrollWidth;const a=(this.cachedScrollWidth-t.innerWidth)*o;this.rtl?this.doc().scrollLeft=-this.snapNormOffset(a):this.doc().scrollLeft=this.snapOffset(a),this.reportProgress(),m(this.wnd),r(!0)})}),e.register("go_id",h.moduleName,(i,r)=>{const o=t.document.getElementById(i);if(!o){r(!1);return}this.wnd.requestAnimationFrame(()=>{this.rtl?this.doc().scrollLeft=-this.snapNormOffset(o.getBoundingClientRect().left+t.scrollX):this.doc().scrollLeft=this.snapOffset(o.getBoundingClientRect().left+t.scrollX);const d=i;this.reportProgress(this.timelineEntries.has(d)?d:this.nearestPrecedingTimelineEntry(o)),m(this.wnd),r(!0)})}),e.register("go_text",h.moduleName,(i,r)=>{let o;Array.isArray(i)&&(i.length>1&&(o=i[1]),i=i[0]);const d=M.deserialize(i),a=_(this.wnd.document,new w({href:t.location.href,type:"text/html",text:d,locations:o?new z({otherLocations:new Map([["cssSelector",o]])}):void 0}));if(!a){r(!1);return}this.wnd.requestAnimationFrame(()=>{this.rtl?this.doc().scrollLeft=-this.snapNormOffset(a.getBoundingClientRect().left+t.scrollX):this.doc().scrollLeft=this.snapOffset(a.getBoundingClientRect().left+t.scrollX),this.reportProgress(this.nearestPrecedingTimelineEntry(a.startContainer)),m(this.wnd),r(!0)})}),e.register("go_end",h.moduleName,(i,r)=>{this.wnd.requestAnimationFrame(()=>{this.cachedScrollWidth=this.doc().scrollWidth;let o;if(this.rtl?o=-this.snapNormOffset(this.cachedScrollWidth-t.innerWidth):o=this.snapOffset(this.cachedScrollWidth),this.doc().scrollLeft===o)return r(!1);this.doc().scrollLeft=o,this.reportProgress(this.sortedFragmentIds[this.sortedFragmentIds.length-1]),m(this.wnd),r(!0)})}),e.register("go_start",h.moduleName,(i,r)=>{this.wnd.requestAnimationFrame(()=>{if(this.doc().scrollLeft===0)return r(!1);this.doc().scrollLeft=0,this.reportProgress(this.firstFragmentIfReached()),m(this.wnd),r(!0)})}),e.register("go_prev",h.moduleName,(i,r)=>{this.wnd.requestAnimationFrame(()=>{this.cachedScrollWidth=this.doc().scrollWidth;let o;this.rtl?o=l(this.normScroll()-t.innerWidth):o=n(t.scrollX-t.innerWidth),this.reportProgress(),o&&(m(this.wnd),this.checkSuspiciousSnap("left",this.wnd.innerWidth)),r(o)})}),e.register("go_next",h.moduleName,(i,r)=>{this.wnd.requestAnimationFrame(()=>{this.cachedScrollWidth=this.doc().scrollWidth;let o;this.rtl?o=l(this.normScroll()+t.innerWidth):o=n(t.scrollX+t.innerWidth),this.reportProgress(),o&&(m(this.wnd),this.checkSuspiciousSnap("right",this.wnd.innerWidth)),r(o)})}),e.register("unfocus",h.moduleName,(i,r)=>{this.snappingCancelled=!0,m(this.wnd),r(!0)}),e.register("shake",h.moduleName,(i,r)=>{this.shake(),r(!0)}),e.register("focus",h.moduleName,(i,r)=>{this.wnd.requestAnimationFrame(()=>{this.cachedScrollWidth=this.doc().scrollWidth,this.snapCurrentOffset(!1,!0),this.reportProgress(),r(!0)})}),e.register("first_visible_locator",h.moduleName,(i,r)=>{const o=X(t,!1);this.comms.send("first_visible_locator",o.serialize()),r(!0)}),e.register("timeline_entries",h.moduleName,(i,r)=>{this.updateTimelineEntries(Array.isArray(i)?i:[],t),t.requestAnimationFrame(()=>this.reportProgress()),r(!0)}),t.addEventListener("touchstart",this.onTouchStarter,{passive:!0}),t.addEventListener("touchend",this.onTouchEnder,{passive:!0}),t.addEventListener("touchmove",this.onTouchMover,{passive:!0}),t.document.addEventListener("touchstart",()=>{}),e.log("ColumnSnapper Mounted"),!0}unmount(t,e){return this.snappingCancelled=!0,e.unregisterAll(h.moduleName),this.resizeObserver.disconnect(),this.mutationObserver.disconnect(),this.patternAnalyzer&&(this.patternAnalyzer.clear(),this.patternAnalyzer=null,this.isSnapProtectionEnabled=!1),t.removeEventListener("touchstart",this.onTouchStarter),t.removeEventListener("touchend",this.onTouchEnder),t.removeEventListener("touchmove",this.onTouchMover),t.removeEventListener("orientationchange",this.onWidthChanger),t.removeEventListener("resize",this.onWidthChanger),t.document.getElementById(T)?.remove(),this.timelineEntries.clear(),e.log("ColumnSnapper Unmounted"),super.unmount(t,e)}};h.moduleName="column_snapper";let x=h;export{x as ColumnSnapper};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@readium/navigator-html-injectables",
3
- "version": "2.8.2",
3
+ "version": "2.8.4",
4
4
  "type": "module",
5
5
  "description": "An embeddable solution for connecting frames of HTML publications with a Readium Navigator",
6
6
  "author": "readium",
@@ -44,8 +44,8 @@
44
44
  "node": ">=18"
45
45
  },
46
46
  "dependencies": {
47
- "@readium/helpers": "1.1.0",
48
- "@readium/shared": "2.5.1"
47
+ "@readium/helpers": "1.1.1",
48
+ "@readium/shared": "2.6.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "css-selector-generator": "^3.8.0",
@@ -1,5 +1,7 @@
1
1
  import { ReadiumWindow } from "./dom.ts";
2
2
 
3
+ export const isTypedOMSupported = () => typeof window.CSSTransformValue !== 'undefined';
4
+
3
5
  const COSMETIC_PROPERTIES = new Set([
4
6
  "backgroundColor", "textColor",
5
7
  "linkColor", "visitedColor",
@@ -218,7 +218,7 @@ export function appendVirtualColumnIfNeeded(wnd: ReadiumWindow): boolean {
218
218
  * unless you force a recalc or reflow.
219
219
  * It is not needed in paginated mode.
220
220
  */
221
- export function forceWebkitRecalc(wnd: ReadiumWindow) {
221
+ export function forceWebkitRecalc(wnd: Window) {
222
222
  // Borrowed from APB themselves…
223
223
  const styleElement = wnd.document.createElement("style");
224
224
  styleElement.appendChild(wnd.document.createTextNode("*{}"));
@@ -120,6 +120,8 @@ interface DecorationItem {
120
120
 
121
121
  const canNativeHighlight = () => ("Highlight" in window);
122
122
  const cannotNativeHighlight = ["IMG", "IMAGE", "AUDIO", "VIDEO", "SVG"];
123
+ // Elements that trigger WebKit's CSS Custom Highlight repaint bug on removal.
124
+ const webkitRepaintProneTags = new Set(["SUP", "SUB", "SMALL", "CODE"]);
123
125
 
124
126
  class DecorationGroup {
125
127
  public readonly items: DecorationItem[] = [];
@@ -132,6 +134,7 @@ class DecorationGroup {
132
134
  private readonly notTextFlag: Map<string, boolean> | undefined;
133
135
  private readonly _tintSubKeys = new Map<string, string>(); // (type::adjustedTint) → subKey
134
136
  private _subKeyCounter = 0;
137
+ private pendingWebkitReflow: Set<Element> | null = null;
135
138
  private readonly activationHandler: (e: PointerEvent) => void;
136
139
  private readonly hoverHandler: (e: PointerEvent) => void;
137
140
  private maskSvg: SVGSVGElement | undefined = undefined;
@@ -295,6 +298,7 @@ class DecorationGroup {
295
298
  }
296
299
  const stylesheet = this.wnd.document.getElementById(`${this.id}-style`) as HTMLStyleElement | null;
297
300
  if (stylesheet) this._rebuildHighlightStylesheet(stylesheet);
301
+ this.scheduleWebkitRepaintFix([item]);
298
302
  }
299
303
  this.notTextFlag?.delete(item.id);
300
304
  if (this.hoveredItem === item) {
@@ -320,6 +324,9 @@ class DecorationGroup {
320
324
  * Removes all decorations from this group.
321
325
  */
322
326
  clear() {
327
+ if (this.experimentalHighlights) {
328
+ this.scheduleWebkitRepaintFix(this.items);
329
+ }
323
330
  this.clearContainer();
324
331
  this.items.length = 0;
325
332
  this.notTextFlag?.clear();
@@ -346,6 +353,90 @@ class DecorationGroup {
346
353
  this.wnd.document.removeEventListener("pointermove", this.hoverHandler);
347
354
  }
348
355
 
356
+ /** True if the element triggers WebKit's CSS Custom Highlight repaint bug. */
357
+ private isWebkitRepaintProne(el: Element): boolean {
358
+ const style = this.wnd.getComputedStyle(el);
359
+ if (webkitRepaintProneTags.has(el.tagName.toUpperCase()) || style.display === "inline-block" || style.verticalAlign !== "baseline") {
360
+ return true;
361
+ }
362
+ const parentStyle = el.parentElement ? this.wnd.getComputedStyle(el.parentElement) : null;
363
+ return !!parentStyle && parseFloat(style.fontSize) !== parseFloat(parentStyle.fontSize);
364
+ }
365
+
366
+ /** Finds a repaint-prone element (see isWebkitRepaintProne) intersecting the range, if any. */
367
+ private findWebkitRepaintProneElement(range: Range): Element | null {
368
+ let el: Element | null = range.startContainer.nodeType === Node.ELEMENT_NODE
369
+ ? range.startContainer as Element
370
+ : range.startContainer.parentElement;
371
+ while (el) {
372
+ if (this.isWebkitRepaintProne(el)) return el;
373
+ if (this.wnd.getComputedStyle(el).display !== "inline") break; // reached the containing block
374
+ el = el.parentElement;
375
+ }
376
+
377
+ const root = range.commonAncestorContainer;
378
+ const walkRoot = root.nodeType === Node.ELEMENT_NODE ? root as Element : root.parentElement;
379
+ if (!walkRoot) return null;
380
+ // Reject prunes subtrees the range doesn't touch, so a small range under a large
381
+ // common ancestor (e.g. spanning two paragraphs in a big section) doesn't walk the whole thing.
382
+ const walker = this.wnd.document.createTreeWalker(walkRoot, NodeFilter.SHOW_ELEMENT, node =>
383
+ range.intersectsNode(node as Element) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT
384
+ );
385
+ for (let node = walker.nextNode() as Element | null; node; node = walker.nextNode() as Element | null) {
386
+ if (this.isWebkitRepaintProne(node)) return node;
387
+ }
388
+ return null;
389
+ }
390
+
391
+ /** Reflows the containing block of the offending element; its own box isn't enough since inline layout is computed per line. */
392
+ private forceWebkitBlockReflow(offender: Element) {
393
+ let el: Element | null = offender;
394
+ while (el) {
395
+ const display = this.wnd.getComputedStyle(el).display;
396
+ if (display !== "inline" && display !== "inline-block") break;
397
+ el = el.parentElement;
398
+ }
399
+ if (!el || !el.isConnected) return;
400
+ const block = el as HTMLElement;
401
+ const previousTransform = block.style.getPropertyValue("transform");
402
+ const previousPriority = block.style.getPropertyPriority("transform");
403
+ // Paint-only nudge: promotes then repaints the compositing layer without
404
+ // touching layout, so it can't disturb scroll position, focus, or selection.
405
+ block.style.setProperty("transform", "translateZ(0.001px)", "important");
406
+ void block.offsetHeight;
407
+ block.style.setProperty("transform", "translateZ(0px)", "important");
408
+ void block.offsetHeight;
409
+ if (previousTransform) {
410
+ block.style.setProperty("transform", previousTransform, previousPriority);
411
+ } else {
412
+ block.style.removeProperty("transform");
413
+ }
414
+ }
415
+
416
+ /** Finds WebKit repaint-prone elements among the given items and queues a reflow of their containing blocks. */
417
+ private scheduleWebkitRepaintFix(items: DecorationItem[]) {
418
+ if (!sML.UA.WebKit) return;
419
+ for (const item of items) {
420
+ if (this.notTextFlag?.has(item.id) || !item.highlightSubKey) continue;
421
+ const offender = this.findWebkitRepaintProneElement(item.range);
422
+ if (offender) this.queueWebkitReflow(offender);
423
+ }
424
+ }
425
+
426
+ /** Batches offenders behind a single animation frame so many decorations removed in the same tick only reflow each block once. */
427
+ private queueWebkitReflow(offender: Element) {
428
+ if (!this.pendingWebkitReflow) {
429
+ this.pendingWebkitReflow = new Set();
430
+ // Forcing layout synchronously doesn't guarantee a paint; defer to a real frame.
431
+ this.wnd.requestAnimationFrame(() => {
432
+ const pending = this.pendingWebkitReflow;
433
+ this.pendingWebkitReflow = null;
434
+ pending?.forEach(el => this.forceWebkitBlockReflow(el));
435
+ });
436
+ }
437
+ this.pendingWebkitReflow.add(offender);
438
+ }
439
+
349
440
  private clientRectsToDocCoords(rects: Rect[], ctx: WritingContext = makeWritingContext(this.wnd)): Rect[] {
350
441
  const dx = ctx.xDocOffset;
351
442
  const dy = ctx.yDocOffset;
@@ -9,6 +9,7 @@ import { rangeFromLocator } from "../../helpers/locator.ts";
9
9
  import { ReadiumWindow, deselect, findFirstVisibleLocator } from "../../helpers/dom.ts";
10
10
  import { PatternAnalyzer } from "../../protection/PatternAnalyzer.ts";
11
11
  import { BaseSuspiciousActivityEvent } from "../Peripherals.ts";
12
+ import { isTypedOMSupported } from "../../helpers/css.ts";
12
13
 
13
14
  const COLUMN_SNAPPER_STYLE_ID = "readium-column-snapper-style";
14
15
 
@@ -214,7 +215,13 @@ export class ColumnSnapper extends Snapper {
214
215
  const spos = position(currentScrollLeft, so, elapsed, period);
215
216
  doc.scrollLeft = spos;
216
217
  if(this.overscroll !== 0)
217
- doc.style.transform = `translate3d(${-lpos}px, 0px, 0px)`;
218
+ if(isTypedOMSupported()) {
219
+ doc.attributeStyleMap.set("transform", new CSSTransformValue([
220
+ new CSSTranslate(CSS.px(-lpos), CSS.px(0), CSS.px(0))
221
+ ]));
222
+ } else {
223
+ doc.style.transform = `translate3d(${-lpos}px, 0px, 0px)`;
224
+ }
218
225
 
219
226
  if (elapsed < period)
220
227
  this.wnd.requestAnimationFrame(step);
@@ -330,10 +337,22 @@ export class ColumnSnapper extends Snapper {
330
337
 
331
338
  if(newpos < minScrollLeft) {
332
339
  this.overscroll = newpos;
333
- this.doc().style.transform = `translate3d(${-this.overscroll}px, 0px, 0px)`;
340
+ if(isTypedOMSupported()) {
341
+ this.doc().attributeStyleMap.set("transform", new CSSTransformValue([
342
+ new CSSTranslate(CSS.px(-newpos), CSS.px(0), CSS.px(0))
343
+ ]));
344
+ } else {
345
+ this.doc().style.transform = `translate3d(${-newpos}px, 0px, 0px)`;
346
+ }
334
347
  } else if(newpos > maxScrollLeft) {
335
348
  this.overscroll = newpos;
336
- this.doc().style.transform = `translate3d(${-newpos}px, 0px, 0px)`;
349
+ if(isTypedOMSupported()) {
350
+ this.doc().attributeStyleMap.set("transform", new CSSTransformValue([
351
+ new CSSTranslate(CSS.px(-newpos), CSS.px(0), CSS.px(0))
352
+ ]));
353
+ } else {
354
+ this.doc().style.transform = `translate3d(${-newpos}px, 0px, 0px)`;
355
+ }
337
356
  } else {
338
357
  this.overscroll = 0;
339
358
  this.doc().style.removeProperty("transform");
@@ -1,4 +1,5 @@
1
1
  import { ReadiumWindow } from "./dom.ts";
2
+ export declare const isTypedOMSupported: () => boolean;
2
3
  /**
3
4
  * Returns true if the style attribute change between oldValue and newValue
4
5
  * includes at least one ReadiumCSS property that affects document layout/geometry.
@@ -90,4 +90,4 @@ export declare function appendVirtualColumnIfNeeded(wnd: ReadiumWindow): boolean
90
90
  * unless you force a recalc or reflow.
91
91
  * It is not needed in paginated mode.
92
92
  */
93
- export declare function forceWebkitRecalc(wnd: ReadiumWindow): void;
93
+ export declare function forceWebkitRecalc(wnd: Window): void;