js.foresight
Version:
Predicts mouse trajectory to trigger actions as users approach elements, enabling anticipatory UI updates or pre-loading. Made with vanilla javascript and usable in every framework.
7 lines (6 loc) • 23.7 kB
JavaScript
/*!
* tabbable 6.2.0
* @license MIT, https://github.com/focus-trap/tabbable/blob/master/LICENSE
*/
var t=["input:not([inert])","select:not([inert])","textarea:not([inert])","a[href]:not([inert])","button:not([inert])","[tabindex]:not(slot):not([inert])","audio[controls]:not([inert])","video[controls]:not([inert])",'[contenteditable]:not([contenteditable="false"]):not([inert])',"details>summary:first-of-type:not([inert])","details:not([inert])"].join(","),e="undefined"==typeof Element,i=e?function(){}:Element.prototype.matches||Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector,n=!e&&Element.prototype.getRootNode?function(t){var e;return null==t||null===(e=t.getRootNode)||void 0===e?void 0:e.call(t)}:function(t){return null==t?void 0:t.ownerDocument},o=function t(e,i){var n;void 0===i&&(i=!0);var o=null==e||null===(n=e.getAttribute)||void 0===n?void 0:n.call(e,"inert");return""===o||"true"===o||i&&e&&t(e.parentNode)},s=function e(n,s,r){for(var l=[],a=Array.from(n);a.length;){var c=a.shift();if(!o(c,!1))if("SLOT"===c.tagName){var h=c.assignedElements(),u=e(h.length?h:c.children,!0,r);r.flatten?l.push.apply(l,u):l.push({scopeParent:c,candidates:u})}else{i.call(c,t)&&r.filter(c)&&(s||!n.includes(c))&&l.push(c);var d=c.shadowRoot||"function"==typeof r.getShadowRoot&&r.getShadowRoot(c),g=!o(d,!1)&&(!r.shadowRootFilter||r.shadowRootFilter(c));if(d&&g){var b=e(!0===d?c.children:d.children,!0,r);r.flatten?l.push.apply(l,b):l.push({scopeParent:c,candidates:b})}else a.unshift.apply(a,c.children)}}return l},r=function(t){return!isNaN(parseInt(t.getAttribute("tabindex"),10))},l=function(t){if(!t)throw new Error("No node provided");return t.tabIndex<0&&(/^(AUDIO|VIDEO|DETAILS)$/.test(t.tagName)||function(t){var e,i=null==t||null===(e=t.getAttribute)||void 0===e?void 0:e.call(t,"contenteditable");return""===i||"true"===i}(t))&&!r(t)?0:t.tabIndex},a=function(t,e){return t.tabIndex===e.tabIndex?t.documentOrder-e.documentOrder:t.tabIndex-e.tabIndex},c=function(t){return"INPUT"===t.tagName},h=function(t){return function(t){return c(t)&&"radio"===t.type}(t)&&!function(t){if(!t.name)return!0;var e,i=t.form||n(t),o=function(t){return i.querySelectorAll('input[type="radio"][name="'+t+'"]')};if("undefined"!=typeof window&&void 0!==window.CSS&&"function"==typeof window.CSS.escape)e=o(window.CSS.escape(t.name));else try{e=o(t.name)}catch(t){return console.error("Looks like you have a radio button with a name attribute containing invalid CSS selector characters and need the CSS.escape polyfill: %s",t.message),!1}var s=function(t,e){for(var i=0;i<t.length;i++)if(t[i].checked&&t[i].form===e)return t[i]}(e,t.form);return!s||s===t}(t)},u=function(t){var e=t.getBoundingClientRect(),i=e.width,n=e.height;return 0===i&&0===n},d=function(t,e){var o=e.displayCheck,s=e.getShadowRoot;if("hidden"===getComputedStyle(t).visibility)return!0;var r=i.call(t,"details>summary:first-of-type")?t.parentElement:t;if(i.call(r,"details:not([open]) *"))return!0;if(o&&"full"!==o&&"legacy-full"!==o){if("non-zero-area"===o)return u(t)}else{if("function"==typeof s){for(var l=t;t;){var a=t.parentElement,c=n(t);if(a&&!a.shadowRoot&&!0===s(a))return u(t);t=t.assignedSlot?t.assignedSlot:a||c===t.ownerDocument?a:c.host}t=l}if(function(t){var e,i,o,s,r=t&&n(t),l=null===(e=r)||void 0===e?void 0:e.host,a=!1;if(r&&r!==t)for(a=!!(null!==(i=l)&&void 0!==i&&null!==(o=i.ownerDocument)&&void 0!==o&&o.contains(l)||null!=t&&null!==(s=t.ownerDocument)&&void 0!==s&&s.contains(t));!a&&l;){var c,h,u;a=!(null===(h=l=null===(c=r=n(l))||void 0===c?void 0:c.host)||void 0===h||null===(u=h.ownerDocument)||void 0===u||!u.contains(l))}return a}(t))return!t.getClientRects().length;if("legacy-full"!==o)return!0}return!1},g=function(t,e){return!(e.disabled||o(e)||function(t){return c(t)&&"hidden"===t.type}(e)||d(e,t)||function(t){return"DETAILS"===t.tagName&&Array.prototype.slice.apply(t.children).some(function(t){return"SUMMARY"===t.tagName})}(e)||function(t){if(/^(INPUT|BUTTON|SELECT|TEXTAREA)$/.test(t.tagName))for(var e=t.parentElement;e;){if("FIELDSET"===e.tagName&&e.disabled){for(var n=0;n<e.children.length;n++){var o=e.children.item(n);if("LEGEND"===o.tagName)return!!i.call(e,"fieldset[disabled] *")||!o.contains(t)}return!0}e=e.parentElement}return!1}(e))},b=function(t,e){return!(h(e)||l(e)<0||!g(t,e))},p=function(t){var e=parseInt(t.getAttribute("tabindex"),10);return!!(isNaN(e)||e>=0)},f=function t(e){var i=[],n=[];return e.forEach(function(e,o){var s=!!e.scopeParent,a=s?e.scopeParent:e,c=function(t,e){var i=l(t);return i<0&&e&&!r(t)?0:i}(a,s),h=s?t(e.candidates):a;0===c?s?i.push.apply(i,h):i.push(a):n.push({documentOrder:o,tabIndex:c,item:e,isScope:s,content:h})}),n.sort(a).reduce(function(t,e){return e.isScope?t.push.apply(t,e.content):t.push(e.content),t},[]).concat(i)},m=function(e,n){var r;return r=(n=n||{}).getShadowRoot?s([e],n.includeContainer,{filter:b.bind(null,n),flatten:!1,getShadowRoot:n.getShadowRoot,shadowRootFilter:p}):function(e,n,s){if(o(e))return[];var r=Array.prototype.slice.apply(e.querySelectorAll(t));return n&&i.call(e,t)&&r.unshift(e),r.filter(s)}(e,n.includeContainer,b.bind(null,n)),f(r)};function v(){const t=window.matchMedia("(pointer: coarse)").matches&&navigator.maxTouchPoints>0,e=function(){const t=navigator.connection;return!!t&&(/2g/.test(t.effectiveType)||t.saveData)}();return{isTouchDevice:t,isLimitedConnection:e,shouldRegister:!t&&!e}}const y=2e3;function S(t,e,i,n){return t<e?console.warn(`ForesightJS: "${n}" value ${t} is below minimum bound ${e}, clamping to ${e}`):t>i&&console.warn(`ForesightJS: "${n}" value ${t} is above maximum bound ${i}, clamping to ${i}`),Math.min(Math.max(t,e),i)}function w(t,e,i){let n=0,o=1;const s=e.x-t.x,r=e.y-t.y,l=(t,e)=>{if(0===t){if(e<0)return!1}else{const i=e/t;if(t<0){if(i>o)return!1;i>n&&(n=i)}else{if(i<n)return!1;i<o&&(o=i)}}return!0};return!!l(-s,t.x-i.left)&&(!!l(s,i.right-t.x)&&(!!l(-r,t.y-i.top)&&(!!l(r,i.bottom-t.y)&&n<=o)))}function R(t){if("number"==typeof t){const e=S(t,0,y,"hitslop");return{top:e,left:e,right:e,bottom:e}}return{top:S(t.top,0,y,"hitslop - top"),left:S(t.left,0,y,"hitslop - left"),right:S(t.right,0,y,"hitslop - right"),bottom:S(t.bottom,0,y,"hitslop - bottom")}}function C(t,e){return{left:t.left-e.left,right:t.right+e.right,top:t.top-e.top,bottom:t.bottom+e.bottom}}function P(t,e){return t&&e?t.left===e.left&&t.right===e.right&&t.top===e.top&&t.bottom===e.bottom:t===e}function E(t,e){return t.x>=e.left&&t.x<=e.right&&t.y>=e.top&&t.y<=e.bottom}function O(t,e){return void 0!==t&&e!==t}var D=class{static intersect(t,e){const i=Math.max(t.left,e.left),n=Math.min(t.right,e.right),o=Math.max(t.top,e.top),s=Math.min(t.bottom,e.bottom),r=Math.max(0,n-i),l=Math.max(0,s-o);return new DOMRect(i,o,r,l)}static clip(t,e){const i={...t.toJSON(),top:t.top+e.top,left:t.left+e.left,bottom:t.bottom-e.bottom,right:t.right-e.right};return i.width=i.right-i.left,i.height=i.bottom-i.top,i}static clipOffsets(t,e){return{top:e.top-t.top,left:e.left-t.left,bottom:t.bottom-e.bottom,right:t.right-e.right}}static equals(t,e){return null==t||null==e?t===e:t.x===e.x&&t.y===e.y&&t.width===e.width&&t.height===e.height}static sizeEqual(t,e){return Math.round(t.width)===Math.round(e.width)&&Math.round(t.height)===Math.round(e.height)}};function x(t,e){const i=Math.max(t.width,3),n=Math.max(t.height,3),o=t.top-e.top- -1,s=t.left-e.left- -1,r=e.right-t.left-i- -1,l=e.bottom-t.top-n- -1;return`${-Math.round(o)}px ${-Math.round(r)}px ${-Math.round(l)}px ${-Math.round(s)}px`}var B=[...Array.from({length:1e3},(t,e)=>e/1e3),1],k=class{constructor(t,e,i){this.#t=e,this.#e=i,this.#i=i.clientRect,this.#n(t)}#t;#o=void 0;#e;#i;#s=void 0;get visibleRect(){const t=this.#e.clip;return t?D.clip(this.#i,t):this.#i}get isIntersecting(){const{width:t,height:e}=this.visibleRect;return t>0&&e>0}#n(t){const{root:e,rootBounds:i}=this.#e,{visibleRect:n}=this;this.#o?.disconnect(),this.#o=new IntersectionObserver(this.#r,{root:e,rootMargin:x(n,i),threshold:B}),this.#o.observe(t)}#r=t=>{if(!this.#o)return;const e=t[t.length-1];if(e){const{intersectionRatio:t,boundingClientRect:i}=e,n=this.#i;this.#i=i;const o=this.#s,s=!D.equals(i,n);if(t!==this.#s||s){const n=this.#e.rootBounds,r=D.intersect(i,n),l=r.width>0&&r.height>0;if(!l)return;this.#s=t,(null!=o||s)&&(this.#t(new M(e.target,i,e.intersectionRect,l,n),this),this.#n(e.target))}}};disconnect(){this.#o?.disconnect()}},M=class{constructor(t,e,i,n,o){this.target=t,this.boundingClientRect=e,this.intersectionRect=i,this.isIntersecting=n,this.rootBounds=o}},I=class{constructor(t,e){const i=function(t){return!t||function(t){return t.nodeType===Node.DOCUMENT_NODE}(t)?t?.defaultView??window:t}(t);if(function(t){return L(t)&&t.nodeType===Node.ELEMENT_NODE}(i)){const t=i.ownerDocument??document;this.rootBounds=i.getBoundingClientRect(),this.#l=new ResizeObserver(t=>{for(const i of t){const[{inlineSize:t,blockSize:n}]=i.borderBoxSize;if(D.sizeEqual(this.rootBounds,{width:t,height:n}))continue;const o=i.target.getBoundingClientRect();this.rootBounds=o,e(o,this)}}),this.#l.observe(i),t.addEventListener("scroll",t=>{t.target&&t.target!==i&&L(t.target)&&t.target.contains(i)&&(this.rootBounds=i.getBoundingClientRect(),e(this.rootBounds,this))},{capture:!0,passive:!0,signal:this.#a.signal})}else{const t=i.visualViewport??i;this.rootBounds=T(i);const n=()=>{const t=T(i);D.equals(this.rootBounds,t)||(this.rootBounds=t,e(t,this))};t.addEventListener("resize",n,{signal:this.#a.signal})}}#l;#a=new AbortController;rootBounds;disconnect(){this.#l?.disconnect(),this.#a.abort()}};function T(t){const e=t.visualViewport?.width??t.innerWidth,i=t.visualViewport?.height??t.innerHeight;return new DOMRect(0,0,e,i)}function L(t){return"nodeType"in t}var z=class{constructor(t,e){this.#e=e,this.#t=e=>{const i=[];for(const t of e){const e=this.intersections.get(t.target);this.intersections.set(t.target,t),e?.isIntersecting===t.isIntersecting&&D.equals(e?.intersectionRect,t.intersectionRect)||i.push(t)}i.length>0&&t(i,this)}}#t;#c=new Map;#e;intersections=new WeakMap;observe(t){const e=t.ownerDocument;if(!e)return;let i=this.#c.get(e);i||(i=new IntersectionObserver(this.#t,{...this.#e,threshold:B}),this.#c.set(e,i)),i.observe(t)}unobserve(t){const e=t.ownerDocument;if(!e)return;const i=this.#c.get(e);i&&(i.unobserve(t),this.intersections.delete(t))}disconnect(){for(const t of this.#c.values())t.disconnect();this.#c.clear()}},j=class{constructor(t,e){this.#t=t,this.#e=e,this.#h=new I(e?.root,this.#u),this.#d=new z(this.#g,e),this.#l=new ResizeObserver(this.#b)}#t;#e;#p=new Map;#l;#f=new WeakMap;#h;#d;observe(t){this.#d.observe(t)}unobserve(t){t?(this.#p.get(t)?.disconnect(),this.#d.unobserve(t)):this.disconnect()}disconnect(){for(const t of this.#p.values())t.disconnect();this.#l.disconnect(),this.#h.disconnect(),this.#d.disconnect()}#m(t){const e=[];for(const i of t){const{target:t}=i;N(i,this.#f.get(t))||(this.#f.set(t,i),e.push(i))}e.length>0&&this.#t(e)}#u=t=>{const e=[];for(const[i]of this.#p){const n=i.getBoundingClientRect(),o=this.#v(i,n);e.push(new H(i,n,o.visibleRect,o.isIntersecting,t))}this.#m(e)};#v(t,e){const i=this.#d;this.#p.get(t)?.disconnect();const n=new k(t,this.#y,{clientRect:e,root:this.#e?.root,rootBounds:this.#h.rootBounds,get clip(){const e=i.intersections.get(t);if(!e)return;const{intersectionRect:n,boundingClientRect:o}=e;return D.clipOffsets(o,n)}});return this.#p.set(t,n),n}#g=t=>{const e=[];for(const i of t){const{target:t,isIntersecting:n,boundingClientRect:o}=i;n?(this.#v(t,o),this.#l.observe(t)):(this.#p.get(t)?.disconnect(),this.#p.delete(t),this.#l.unobserve(t));const s=this.#p.get(t);e.push(new H(t,o,s?.visibleRect??i.intersectionRect,n,this.#h.rootBounds))}this.#m(e)};#y=(t,e)=>{this.#m([new H(t.target,t.boundingClientRect,e.visibleRect,t.isIntersecting,this.#h.rootBounds)])};#b=t=>{const e=[];for(const i of t){const{target:t,borderBoxSize:n}=i,o=this.#f.get(t);if(o){const[{inlineSize:t,blockSize:e}]=n;if(D.sizeEqual(o.boundingClientRect,{width:t,height:e}))continue}const s=t.getBoundingClientRect(),r=this.#v(t,s);e.push(new H(t,s,r.visibleRect,this.#d.intersections.get(t)?.isIntersecting??!1,this.#h.rootBounds))}this.#m(e)}},H=class{constructor(t,e,i,n,o){this.target=t,this.boundingClientRect=e,this.intersectionRect=i,this.isIntersecting=n,this.rootBounds=o}};function N(t,e){return null!=e&&(t.target===e.target&&t.isIntersecting===e.isIntersecting&&D.equals(t.boundingClientRect,e.boundingClientRect)&&D.equals(t.intersectionRect,e.intersectionRect))}class _{constructor(){this.elements=new Map,this.isSetup=!1,this._globalCallbackHits={mouse:{hover:0,trajectory:0},tab:{forwards:0,reverse:0},scroll:{down:0,left:0,right:0,up:0},total:0},this._globalSettings={debug:!1,enableMousePrediction:true,enableScrollPrediction:true,positionHistorySize:8,trajectoryPredictionTime:120,scrollMargin:150,defaultHitSlop:{top:0,left:0,right:0,bottom:0},enableTabPrediction:true,tabOffset:2,onAnyCallbackFired:(t,e)=>{}},this.trajectoryPositions={positions:[],currentPoint:{x:0,y:0},predictedPoint:{x:0,y:0}},this.tabbableElementsCache=[],this.lastFocusedIndex=null,this.predictedScrollPoint=null,this.scrollDirection=null,this.domObserver=null,this.positionObserver=null,this.lastKeyDown=null,this.globalListenersController=null,this.eventListeners=new Map,this.handleMouseMove=t=>{this.updatePointerState(t),this.elements.forEach(t=>{t.isIntersectingWithViewport&&this.handleCallbackInteraction(t)}),this.emit({type:"mouseTrajectoryUpdate",predictionEnabled:this._globalSettings.enableMousePrediction,timestamp:Date.now(),trajectoryPositions:this.trajectoryPositions})},this.handleDomMutations=t=>{t.length&&(this.tabbableElementsCache=[],this.lastFocusedIndex=null);for(const e of t)if("childList"===e.type&&e.removedNodes.length>0)for(const t of Array.from(this.elements.keys()))t.isConnected||this.unregister(t,"disconnected")},this.handleKeyDown=t=>{"Tab"===t.key&&(this.lastKeyDown=t)},this.handleFocusIn=t=>{if(!this.lastKeyDown||!this._globalSettings.enableTabPrediction)return;const e=t.target;if(!(e instanceof HTMLElement))return;this.tabbableElementsCache.length||(this.tabbableElementsCache=m(document.documentElement));const i=this.lastKeyDown.shiftKey,n=function(t,e,i,n){if(null!==e){const o=t?e-1:e+1;if(o>=0&&o<i.length&&i[o]===n)return o}return i.findIndex(t=>t===n)}(i,this.lastFocusedIndex,this.tabbableElementsCache,e);this.lastFocusedIndex=n,this.lastKeyDown=null;const o=[];for(let t=0;t<=this._globalSettings.tabOffset;t++)if(i){const e=this.tabbableElementsCache[n-t];this.elements.has(e)&&o.push(e)}else{const e=this.tabbableElementsCache[n+t];this.elements.has(e)&&o.push(e)}o.forEach(t=>{this.callCallback(this.elements.get(t),{kind:"tab",subType:i?"reverse":"forwards"})})},this.handlePositionChange=t=>{for(const e of t){const t=this.elements.get(e.target);if(!t)continue;const i=t.isIntersectingWithViewport,n=e.isIntersecting;t.isIntersectingWithViewport=n,i!==n&&this.emit({type:"elementDataUpdated",elementData:t,timestamp:Date.now(),updatedProp:"visibility"}),n&&(this.updateElementBounds(e.boundingClientRect,t),this.handleScrollPrefetch(t,e.boundingClientRect))}this.scrollDirection=null,this.predictedScrollPoint=null}}static initialize(t){return this.isInitiated||(_.manager=new _),void 0!==t&&_.manager.alterGlobalSettings(t),_.manager}addEventListener(t,e,i){if(i?.signal?.aborted)return()=>{};this.eventListeners.has(t)||this.eventListeners.set(t,[]),this.eventListeners.get(t).push(e),i?.signal?.addEventListener("abort",()=>this.removeEventListener(t,e))}removeEventListener(t,e){const i=this.eventListeners.get(t);if(i){const t=i.indexOf(e);t>-1&&i.splice(t,1)}}logSubscribers(){console.log("%c[ForesightManager] Current Subscribers:","font-weight: bold; color: #3b82f6;");const t=Array.from(this.eventListeners.keys());0!==t.length?t.forEach(t=>{const e=this.eventListeners.get(t);e&&e.length>0&&(console.groupCollapsed(`Event: %c${t}`,"font-weight: bold;",`(${e.length} listener${e.length>1?"s":""})`),e.forEach((t,e)=>{console.log(`[${e}]:`,t)}),console.groupEnd())}):console.log(" No active subscribers.")}emit(t){const e=this.eventListeners.get(t.type);e&&e.forEach(e=>{try{e(t)}catch(e){console.error(`Error in ForesightManager event listener for ${t.type}:`,e)}})}get getManagerData(){return{registeredElements:this.elements,globalSettings:this._globalSettings,globalCallbackHits:this._globalCallbackHits}}static get isInitiated(){return!!_.manager}static get instance(){return this.initialize()}get registeredElements(){return this.elements}register({element:t,callback:e,hitSlop:i,name:n}){const{shouldRegister:o,isTouchDevice:s,isLimitedConnection:r}=v();if(!o)return{isLimitedConnection:r,isTouchDevice:s,isRegistered:!1,unregister:()=>{}};this.isSetup||this.initializeGlobalListeners();const l=i?R(i):this._globalSettings.defaultHitSlop,a={element:t,callback:e,elementBounds:{originalRect:void 0,expandedRect:{top:0,left:0,right:0,bottom:0},hitSlop:l},isHovering:!1,trajectoryHitData:{isTrajectoryHit:!1,trajectoryHitTime:0,trajectoryHitExpirationTimeoutId:void 0},name:n??t.id??"",isIntersectingWithViewport:!0};return this.elements.set(t,a),this.positionObserver?.observe(t),this.emit({type:"elementRegistered",timestamp:Date.now(),elementData:a}),{isTouchDevice:s,isLimitedConnection:r,isRegistered:!0,unregister:()=>this.unregister(t,"apiCall")}}unregister(t,e){if(!this.elements.has(t))return;const i=this.elements.get(t);i&&this.emit({type:"elementUnregistered",elementData:i,timestamp:Date.now(),unregisterReason:e}),i?.trajectoryHitData.trajectoryHitExpirationTimeoutId&&clearTimeout(i.trajectoryHitData.trajectoryHitExpirationTimeoutId),this.positionObserver?.unobserve(t),this.elements.delete(t),0===this.elements.size&&this.isSetup&&this.removeGlobalListeners()}updateNumericSettings(t,e,i,n){return!!O(t,this._globalSettings[e])&&(this._globalSettings[e]=S(t,i,n,e),!0)}updateBooleanSetting(t,e){return!!O(t,this._globalSettings[e])&&(this._globalSettings[e]=t,!0)}alterGlobalSettings(t){const e=this._globalSettings.positionHistorySize,i=this.updateNumericSettings(t?.positionHistorySize,"positionHistorySize",2,30);i&&this._globalSettings.positionHistorySize<e&&this.trajectoryPositions.positions.length>this._globalSettings.positionHistorySize&&(this.trajectoryPositions.positions=this.trajectoryPositions.positions.slice(this.trajectoryPositions.positions.length-this._globalSettings.positionHistorySize));const n=this.updateNumericSettings(t?.trajectoryPredictionTime,"trajectoryPredictionTime",10,200),o=this.updateNumericSettings(t?.scrollMargin,"scrollMargin",30,300),s=this.updateNumericSettings(t?.tabOffset,"tabOffset",0,20),r=this.updateBooleanSetting(t?.enableMousePrediction,"enableMousePrediction"),l=this.updateBooleanSetting(t?.enableScrollPrediction,"enableScrollPrediction"),a=this.updateBooleanSetting(t?.enableTabPrediction,"enableTabPrediction");void 0!==t?.onAnyCallbackFired&&(this._globalSettings.onAnyCallbackFired=t.onAnyCallbackFired);let c=!1;if(void 0!==t?.defaultHitSlop){const e=R(t.defaultHitSlop);P(this._globalSettings.defaultHitSlop,e)||(this._globalSettings.defaultHitSlop=e,c=!0,this.forceUpdateAllElementBounds())}(i||n||s||r||a||l||c||o)&&this.emit({type:"managerSettingsChanged",timestamp:Date.now(),managerData:this.getManagerData})}forceUpdateAllElementBounds(){this.elements.forEach((t,e)=>{const i=this.elements.get(e);i&&i.isIntersectingWithViewport&&this.forceUpdateElementBounds(i)})}updatePointerState(t){this.trajectoryPositions.currentPoint={x:t.clientX,y:t.clientY},this.trajectoryPositions.predictedPoint=this._globalSettings.enableMousePrediction?function(t,e,i,n){const o={point:t,time:performance.now()},{x:s,y:r}=t;if(e.push(o),e.length>i&&e.shift(),e.length<2)return{x:s,y:r};const l=e[0],a=e[e.length-1],c=(a.time-l.time)/1e3;if(0===c)return{x:s,y:r};const h=n/1e3;return{x:s+(a.point.x-l.point.x)/c*h,y:r+(a.point.y-l.point.y)/c*h}}(this.trajectoryPositions.currentPoint,this.trajectoryPositions.positions,this._globalSettings.positionHistorySize,this._globalSettings.trajectoryPredictionTime):{...this.trajectoryPositions.currentPoint}}handleCallbackInteraction(t){const{expandedRect:e}=t.elementBounds;if(this._globalSettings.enableMousePrediction)w(this.trajectoryPositions.currentPoint,this.trajectoryPositions.predictedPoint,e)&&this.callCallback(t,{kind:"mouse",subType:"trajectory"});else if(E(this.trajectoryPositions.currentPoint,e))return void this.callCallback(t,{kind:"mouse",subType:"hover"})}updateHitCounters(t,e){switch(e.kind){case"mouse":this._globalCallbackHits.mouse[e.subType]++;break;case"tab":this._globalCallbackHits.tab[e.subType]++;break;case"scroll":this._globalCallbackHits.scroll[e.subType]++}this._globalCallbackHits.total++}callCallback(t,e){t&&(this.updateHitCounters(t,e),t.callback(),this._globalSettings.onAnyCallbackFired(t,this.getManagerData),this.emit({type:"callbackFired",timestamp:Date.now(),elementData:t,hitType:e,managerData:this.getManagerData}),this.unregister(t.element,"callbackHit"))}forceUpdateElementBounds(t){const e=t.element.getBoundingClientRect(),i=C(e,t.elementBounds.hitSlop);if(!P(i,t.elementBounds.expandedRect)){const n={...t,elementBounds:{...t.elementBounds,originalRect:e,expandedRect:i}};this.elements.set(t.element,n),this.emit({type:"elementDataUpdated",timestamp:Date.now(),elementData:n,updatedProp:"bounds"})}}updateElementBounds(t,e){const i={...e,elementBounds:{...e.elementBounds,originalRect:t,expandedRect:C(t,e.elementBounds.hitSlop)}};this.elements.set(e.element,i),this.emit({type:"elementDataUpdated",timestamp:Date.now(),elementData:i,updatedProp:"bounds"})}handleScrollPrefetch(t,e){if(this._globalSettings.enableScrollPrediction){if(!t.elementBounds.originalRect)return;if(this.scrollDirection=this.scrollDirection??function(t,e){const i=e.top-t.top,n=e.left-t.left;return i<-1?"down":i>1?"up":n<-1?"right":n>1?"left":"none"}(t.elementBounds.originalRect,e),"none"===this.scrollDirection)return;this.predictedScrollPoint=this.predictedScrollPoint??function(t,e,i){const{x:n,y:o}=t,s={x:n,y:o};switch(e){case"down":s.y+=i;break;case"up":s.y-=i;break;case"left":s.x-=i;break;case"right":s.x+=i}return s}(this.trajectoryPositions.currentPoint,this.scrollDirection,this._globalSettings.scrollMargin),w(this.trajectoryPositions.currentPoint,this.predictedScrollPoint,t?.elementBounds.expandedRect)&&this.callCallback(t,{kind:"scroll",subType:this.scrollDirection}),this.emit({type:"scrollTrajectoryUpdate",timestamp:Date.now(),currentPoint:this.trajectoryPositions.currentPoint,predictedPoint:this.predictedScrollPoint})}else E(this.trajectoryPositions.currentPoint,t.elementBounds.expandedRect)&&this.callCallback(t,{kind:"mouse",subType:"hover"})}initializeGlobalListeners(){if(this.isSetup)return;if("undefined"==typeof window||"undefined"==typeof document)return;this.globalListenersController=new AbortController;const{signal:t}=this.globalListenersController;document.addEventListener("mousemove",this.handleMouseMove),document.addEventListener("keydown",this.handleKeyDown,{signal:t}),document.addEventListener("focusin",this.handleFocusIn,{signal:t}),this.domObserver=new MutationObserver(this.handleDomMutations),this.domObserver.observe(document.documentElement,{childList:!0,subtree:!0,attributes:!1}),this.positionObserver=new j(this.handlePositionChange),this.isSetup=!0}removeGlobalListeners(){this.isSetup=!1,this.globalListenersController?.abort(),this.globalListenersController=null,this.domObserver?.disconnect(),this.domObserver=null,this.positionObserver?.disconnect(),this.positionObserver=null}}export{_ as ForesightManager};
//# sourceMappingURL=index.mjs.map