@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.
- package/dist/helpers/css.js +1 -1
- package/dist/modules/Decorator.js +6 -6
- package/dist/modules/setup/Setup.js +1 -1
- package/dist/modules/snapper/ColumnSnapper.js +2 -2
- package/package.json +3 -3
- package/src/helpers/css.ts +2 -0
- package/src/helpers/document.ts +1 -1
- package/src/modules/Decorator.ts +91 -0
- package/src/modules/snapper/ColumnSnapper.ts +22 -3
- package/types/src/helpers/css.d.ts +1 -0
- package/types/src/helpers/document.d.ts +1 -1
package/dist/helpers/css.js
CHANGED
|
@@ -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
|
|
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),
|
|
15
|
-
color: ${
|
|
16
|
-
background-color: ${
|
|
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: ${
|
|
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.
|
|
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
|
|
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(
|
|
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.
|
|
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.
|
|
48
|
-
"@readium/shared": "2.
|
|
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",
|
package/src/helpers/css.ts
CHANGED
package/src/helpers/document.ts
CHANGED
|
@@ -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:
|
|
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("*{}"));
|
package/src/modules/Decorator.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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");
|
|
@@ -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:
|
|
93
|
+
export declare function forceWebkitRecalc(wnd: Window): void;
|