js.foresight 3.3.5 → 3.5.0
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/README.md +2 -2
- package/dist/DesktopHandler-BOXAW4XX.js +1 -0
- package/dist/ScrollPredictor-Y7NELMBI.js +1 -0
- package/dist/TabPredictor-HA2SV3CY.js +1 -0
- package/dist/TouchDeviceHandler-JWBQ2YOV.js +1 -0
- package/dist/TouchStartPredictor-ZH3KJG2C.js +1 -0
- package/dist/ViewportPredictor-H3GLDETY.js +1 -0
- package/dist/chunk-44N4MCQB.js +1 -0
- package/dist/chunk-AODZNE3S.js +1 -0
- package/dist/chunk-PAYO6NXN.js +1 -0
- package/dist/index.d.ts +39 -14
- package/dist/index.js +1 -1
- package/package.json +15 -13
- package/LICENSE +0 -21
package/README.md
CHANGED
|
@@ -34,14 +34,14 @@ yarn add js.foresight
|
|
|
34
34
|
This basic example is in vanilla JS, ofcourse most people will use ForesightJS with a framework. You can read about framework integrations in the [docs](https://foresightjs.com/docs/integrations/react/useForesight).
|
|
35
35
|
|
|
36
36
|
```javascript
|
|
37
|
-
import { ForesightManager } from "
|
|
37
|
+
import { ForesightManager } from "js.foresight"
|
|
38
38
|
|
|
39
39
|
// Initialize the manager if you want custom global settings (do this once at app startup)
|
|
40
40
|
ForesightManager.initialize({
|
|
41
41
|
// Optional props (see configuration)
|
|
42
42
|
})
|
|
43
43
|
|
|
44
|
-
// Register
|
|
44
|
+
// Register a single element (or NodeList)
|
|
45
45
|
const myButton = document.getElementById("my-button")
|
|
46
46
|
|
|
47
47
|
ForesightManager.instance.register({
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c as f,d as b,e as d}from"./chunk-PAYO6NXN.js";import{a as m}from"./chunk-44N4MCQB.js";import{a as l}from"./chunk-AODZNE3S.js";function y(n,i,t,e){let r=performance.now();i.add({point:{x:n.x,y:n.y},time:r});let{x:o,y:s}=n;if(i.length<2){e.x=o,e.y=s;return}let[c,a]=i.getFirstLast();if(!c||!a){e.x=o,e.y=s;return}let p=(a.time-c.time)*.001;if(p===0){e.x=o,e.y=s;return}let v=a.point.x-c.point.x,T=a.point.y-c.point.y,x=v/p,M=T/p,P=t*.001;e.x=o+x*P,e.y=s+M*P}var h=class extends l{constructor(t){super(t.dependencies);this.moduleName="MousePredictor";this.trajectoryPositions=t.trajectoryPositions}updatePointerState(t){let e=this.trajectoryPositions.currentPoint;e.x=t.clientX,e.y=t.clientY,this.settings.enableMousePrediction?y(e,this.trajectoryPositions.positions,this.settings.trajectoryPredictionTime,this.trajectoryPositions.predictedPoint):(this.trajectoryPositions.predictedPoint.x=e.x,this.trajectoryPositions.predictedPoint.y=e.y)}processMouseMovement(t){this.updatePointerState(t);let e=this.settings.enableMousePrediction,r=this.trajectoryPositions.currentPoint;for(let o of this.elements.values()){if(!o.isIntersectingWithViewport||!o.callbackInfo.isCallbackActive||o.callbackInfo.isRunningCallback)continue;let s=o.elementBounds.expandedRect;if(e)m(r,this.trajectoryPositions.predictedPoint,s)&&this.callCallback(o,{kind:"mouse",subType:"trajectory"});else if(d(r,s)){this.callCallback(o,{kind:"mouse",subType:"hover"});return}}this.hasListeners("mouseTrajectoryUpdate")&&this.emit({type:"mouseTrajectoryUpdate",predictionEnabled:e,trajectoryPositions:this.trajectoryPositions})}onDisconnect(){}onConnect(){}};import{PositionObserver as j}from"position-observer";var u=class{constructor(i){this.head=0;this.count=0;if(i<=0)throw new Error("CircularBuffer capacity must be greater than 0");this.capacity=i,this.buffer=new Array(i)}add(i){this.buffer[this.head]=i,this.head=(this.head+1)%this.capacity,this.count<this.capacity&&this.count++}getFirst(){if(this.count!==0)return this.count<this.capacity?this.buffer[0]:this.buffer[this.head]}getLast(){if(this.count!==0){if(this.count<this.capacity)return this.buffer[this.count-1];{let i=(this.head-1+this.capacity)%this.capacity;return this.buffer[i]}}}getFirstLast(){if(this.count===0)return[void 0,void 0];if(this.count===1){let e=this.count<this.capacity?this.buffer[0]:this.buffer[this.head];return[e,e]}let i=this.getFirst(),t=this.getLast();return[i,t]}resize(i){if(i<=0)throw new Error("CircularBuffer capacity must be greater than 0");if(i===this.capacity)return;let t=this.getAllItems();if(this.capacity=i,this.buffer=new Array(i),this.head=0,this.count=0,t.length>i){let e=t.slice(-i);for(let r of e)this.add(r)}else for(let e of t)this.add(e)}getAllItems(){if(this.count===0)return[];let i=new Array(this.count);if(this.count<this.capacity)for(let t=0;t<this.count;t++)i[t]=this.buffer[t];else{let t=this.head;for(let e=0;e<this.capacity;e++){let r=(t+e)%this.capacity;i[e]=this.buffer[r]}}return i}clear(){this.head=0,this.count=0}get length(){return this.count}get size(){return this.capacity}get isFull(){return this.count===this.capacity}get isEmpty(){return this.count===0}};var g=class extends l{constructor(t){super(t);this.moduleName="DesktopHandler";this.tabPredictor=null;this.scrollPredictor=null;this.positionObserver=null;this.trajectoryPositions={positions:new u(8),currentPoint:{x:0,y:0},predictedPoint:{x:0,y:0}};this.handlePositionChange=t=>{let e=this.settings.enableScrollPrediction;for(let r of t){let o=this.elements.get(r.target);o&&(e?this.scrollPredictor?.handleScrollPrefetch(o,r.boundingClientRect):this.checkForMouseHover(o),this.handlePositionChangeDataUpdates(o,r))}e&&this.scrollPredictor?.resetScrollProps()};this.checkForMouseHover=t=>{d(this.trajectoryPositions.currentPoint,t.elementBounds.expandedRect)&&this.callCallback(t,{kind:"mouse",subType:"hover"})};this.handlePositionChangeDataUpdates=(t,e)=>{let r=[],o=e.isIntersecting;t.isIntersectingWithViewport!==o&&(r.push("visibility"),t.isIntersectingWithViewport=o),o&&!b(e.boundingClientRect,t.elementBounds.originalRect)&&(r.push("bounds"),t.elementBounds={hitSlop:t.elementBounds.hitSlop,originalRect:e.boundingClientRect,expandedRect:f(e.boundingClientRect,t.elementBounds.hitSlop)}),r.length&&this.hasListeners("elementDataUpdated")&&this.emit({type:"elementDataUpdated",elementData:t,updatedProps:r})};this.processMouseMovement=t=>this.mousePredictor.processMouseMovement(t);this.invalidateTabCache=()=>this.tabPredictor?.invalidateCache();this.observeElement=t=>this.positionObserver?.observe(t);this.unobserveElement=t=>this.positionObserver?.unobserve(t);this.connectTabPredictor=async()=>{if(!this.tabPredictor){let{TabPredictor:t}=await import("./TabPredictor-HA2SV3CY.js");this.tabPredictor=new t(this.storedDependencies),this.devLog("TabPredictor lazy loaded")}this.tabPredictor.connect()};this.connectScrollPredictor=async()=>{if(!this.scrollPredictor){let{ScrollPredictor:t}=await import("./ScrollPredictor-Y7NELMBI.js");this.scrollPredictor=new t({dependencies:this.storedDependencies,trajectoryPositions:this.trajectoryPositions}),this.devLog("ScrollPredictor lazy loaded")}this.scrollPredictor.connect()};this.connectMousePredictor=()=>this.mousePredictor.connect();this.disconnectTabPredictor=()=>this.tabPredictor?.disconnect();this.disconnectScrollPredictor=()=>this.scrollPredictor?.disconnect();this.disconnectMousePredictor=()=>this.mousePredictor.disconnect();this.storedDependencies=t,this.mousePredictor=new h({dependencies:t,trajectoryPositions:this.trajectoryPositions})}onConnect(){this.settings.enableTabPrediction&&this.connectTabPredictor(),this.settings.enableScrollPrediction&&this.connectScrollPredictor(),this.connectMousePredictor(),this.positionObserver=new j(this.handlePositionChange);let t=["mouse"];this.settings.enableTabPrediction&&t.push("tab (loading...)"),this.settings.enableScrollPrediction&&t.push("scroll (loading...)"),this.devLog(`Connected predictors: [${t.join(", ")}] and PositionObserver`);for(let e of this.elements.keys())this.positionObserver.observe(e)}onDisconnect(){this.disconnectMousePredictor(),this.disconnectTabPredictor(),this.disconnectScrollPredictor(),this.positionObserver?.disconnect(),this.positionObserver=null}get loadedPredictors(){return{mouse:this.mousePredictor!==null,tab:this.tabPredictor!==null,scroll:this.scrollPredictor!==null}}};export{g as DesktopHandler};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as s}from"./chunk-44N4MCQB.js";import{a as l}from"./chunk-AODZNE3S.js";function n(r,e){let o=e.top-r.top,c=e.left-r.left;return o<-1?"down":o>1?"up":c<-1?"right":c>1?"left":"none"}function p(r,e,t){let{x:o,y:c}=r,i={x:o,y:c};switch(e){case"down":i.y+=t;break;case"up":i.y-=t;break;case"left":i.x-=t;break;case"right":i.x+=t;break;case"none":break;default:}return i}var d=class extends l{constructor(t){super(t.dependencies);this.moduleName="ScrollPredictor";this.predictedScrollPoint=null;this.scrollDirection=null;this.onDisconnect=()=>this.resetScrollProps();this.trajectoryPositions=t.trajectoryPositions}resetScrollProps(){this.scrollDirection=null,this.predictedScrollPoint=null}handleScrollPrefetch(t,o){!t.isIntersectingWithViewport||t.callbackInfo.isRunningCallback||!t.callbackInfo.isCallbackActive||(this.scrollDirection=this.scrollDirection??n(t.elementBounds.originalRect,o),this.scrollDirection!=="none"&&(this.predictedScrollPoint=this.predictedScrollPoint??p(this.trajectoryPositions.currentPoint,this.scrollDirection,this.settings.scrollMargin),s(this.trajectoryPositions.currentPoint,this.predictedScrollPoint,t.elementBounds.expandedRect)&&this.callCallback(t,{kind:"scroll",subType:this.scrollDirection}),this.hasListeners("scrollTrajectoryUpdate")&&this.emit({type:"scrollTrajectoryUpdate",currentPoint:this.trajectoryPositions.currentPoint,predictedPoint:this.predictedScrollPoint,scrollDirection:this.scrollDirection})))}onConnect(){}};export{d as ScrollPredictor};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as b}from"./chunk-AODZNE3S.js";import{tabbable as f}from"tabbable";function u(r,l,e,o){if(l!==null&&l>-1){let t=r?l-1:l+1;if(t>=0&&t<e.length&&e[t]===o)return t}return e.findIndex(t=>t===o)}var h=class extends b{constructor(e){super(e);this.moduleName="TabPredictor";this.lastKeyDown=null;this.tabbableElementsCache=[];this.lastFocusedIndex=null;this.handleKeyDown=e=>{e.key==="Tab"&&(this.lastKeyDown=e)};this.handleFocusIn=e=>{if(!this.lastKeyDown)return;let o=e.target;if(!(o instanceof HTMLElement))return;(!this.tabbableElementsCache.length||this.lastFocusedIndex===-1)&&(this.devLog("Caching tabbable elements"),this.tabbableElementsCache=f(document.documentElement));let t=this.lastKeyDown.shiftKey,i=u(t,this.lastFocusedIndex,this.tabbableElementsCache,o);this.lastFocusedIndex=i,this.lastKeyDown=null;let c=[],m=this.settings.tabOffset,d=this.elements;for(let n=0;n<=m;n++){let s=t?i-n:i+n,a=this.tabbableElementsCache[s];a&&a instanceof Element&&d.has(a)&&c.push(a)}for(let n of c){let s=d.get(n);s&&!s.callbackInfo.isRunningCallback&&s.callbackInfo.isCallbackActive&&this.callCallback(s,{kind:"tab",subType:t?"reverse":"forwards"})}}}invalidateCache(){this.tabbableElementsCache.length&&this.devLog("Invalidating tabbable elements cache"),this.tabbableElementsCache=[],this.lastFocusedIndex=null}onConnect(){typeof document>"u"||(this.createAbortController(),document.addEventListener("keydown",this.handleKeyDown,{signal:this.abortController?.signal,passive:!0}),document.addEventListener("focusin",this.handleFocusIn,{signal:this.abortController?.signal,passive:!0}))}onDisconnect(){this.tabbableElementsCache=[],this.lastFocusedIndex=null,this.lastKeyDown=null}};export{h as TabPredictor};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e}from"./chunk-AODZNE3S.js";var r=class extends e{constructor(t){super(t);this.moduleName="TouchDeviceHandler";this.viewportPredictor=null;this.touchStartPredictor=null;this.predictor=null;this.onDisconnect=()=>{this.devLog("Disconnecting touch predictor"),this.predictor?.disconnect()};this.onConnect=()=>this.setTouchPredictor();this.observeElement=t=>this.predictor?.observeElement(t);this.unobserveElement=t=>this.predictor?.unobserveElement(t);this.storedDependencies=t}async getOrCreateViewportPredictor(){if(!this.viewportPredictor){let{ViewportPredictor:t}=await import("./ViewportPredictor-H3GLDETY.js");this.viewportPredictor=new t(this.storedDependencies),this.devLog("ViewportPredictor lazy loaded")}return this.viewportPredictor}async getOrCreateTouchStartPredictor(){if(!this.touchStartPredictor){let{TouchStartPredictor:t}=await import("./TouchStartPredictor-ZH3KJG2C.js");this.touchStartPredictor=new t(this.storedDependencies),this.devLog("TouchStartPredictor lazy loaded")}return this.touchStartPredictor}async setTouchPredictor(){switch(this.predictor?.disconnect(),this.settings.touchDeviceStrategy){case"viewport":this.predictor=await this.getOrCreateViewportPredictor(),this.devLog(`Connected touch strategy: ${this.settings.touchDeviceStrategy} (ViewportPredictor)`);break;case"onTouchStart":this.predictor=await this.getOrCreateTouchStartPredictor(),this.devLog(`Connected touch strategy: ${this.settings.touchDeviceStrategy} (TouchStartPredictor)`);break;case"none":this.predictor=null,this.devLog('Touch strategy set to "none" - no predictor connected');return;default:this.settings.touchDeviceStrategy}this.predictor?.connect();for(let t of this.elements.keys())this.predictor?.observeElement(t)}get loadedPredictors(){return{viewport:this.viewportPredictor!==null,touchStart:this.touchStartPredictor!==null}}};export{r as TouchDeviceHandler};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as n}from"./chunk-AODZNE3S.js";var r=class extends n{constructor(e){super(e);this.moduleName="TouchStartPredictor";this.onConnect=()=>this.createAbortController();this.onDisconnect=()=>{};this.handleTouchStart=e=>{let t=e.currentTarget,o=this.elements.get(t);o&&(this.callCallback(o,{kind:"touch"}),this.unobserveElement(t))}}observeElement(e){e instanceof HTMLElement&&e.addEventListener("pointerdown",this.handleTouchStart,{signal:this.abortController?.signal})}unobserveElement(e){e instanceof HTMLElement&&e.removeEventListener("pointerdown",this.handleTouchStart)}};export{r as TouchStartPredictor};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as n}from"./chunk-AODZNE3S.js";var s=class extends n{constructor(e){super(e);this.moduleName="ViewportPredictor";this.intersectionObserver=null;this.onConnect=()=>this.intersectionObserver=new IntersectionObserver(this.handleViewportEnter);this.observeElement=e=>this.intersectionObserver?.observe(e);this.unobserveElement=e=>this.intersectionObserver?.unobserve(e);this.handleViewportEnter=e=>{for(let t of e){if(!t.isIntersecting)continue;let r=this.elements.get(t.target);r&&(this.callCallback(r,{kind:"viewport"}),this.unobserveElement(t.target))}}}onDisconnect(){this.intersectionObserver?.disconnect(),this.intersectionObserver=null}};export{s as ViewportPredictor};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function y(e,s,n){let r=0,f=1,l=s.x-e.x,u=s.y-e.y,i=(o,a)=>{if(o===0){if(a<0)return!1}else{let t=a/o;if(o<0){if(t>f)return!1;t>r&&(r=t)}else{if(t<r)return!1;t<f&&(f=t)}}return!0};return!i(-l,e.x-n.left)||!i(l,n.right-e.x)||!i(-u,e.y-n.top)||!i(u,n.bottom-e.y)?!1:r<=f}export{y as a};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=class{constructor(t){this._isConnected=!1;this._cachedLogStyle=null;this.elements=t.elements,this.callCallback=t.callCallback,this.emit=t.emit,this.hasListeners=t.hasListeners,this.settings=t.settings}get isConnected(){return this._isConnected}disconnect(){this.isConnected&&(this.devLog(`Disconnecting ${this.moduleName}...`),this.abortController?.abort(`${this.moduleName} module disconnected`),this.onDisconnect(),this._isConnected=!1)}connect(){this.devLog(`Connecting ${this.moduleName}...`),this.onConnect(),this._isConnected=!0}devLog(t){if(this.settings.enableManagerLogging){if(this._cachedLogStyle===null){let o=this.moduleName.includes("Predictor")?"#ea580c":"#2563eb";this._cachedLogStyle=`color: ${o}; font-weight: bold;`}console.log(`%c${this.moduleName}: ${t}`,this._cachedLogStyle)}}createAbortController(){this.abortController&&!this.abortController.signal.aborted||(this.abortController=new AbortController,this.devLog(`Created new AbortController for ${this.moduleName}`))}};export{e as a};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function i(t,o,e,r){return t<o?console.warn(`ForesightJS: "${r}" value ${t} is below minimum bound ${o}, clamping to ${o}`):t>e&&console.warn(`ForesightJS: "${r}" value ${t} is above maximum bound ${e}, clamping to ${e}`),Math.min(Math.max(t,o),e)}function p(t){if(typeof t=="number"){let o=i(t,0,2e3,"hitslop");return{top:o,left:o,right:o,bottom:o}}return{top:i(t.top,0,2e3,"hitslop - top"),left:i(t.left,0,2e3,"hitslop - left"),right:i(t.right,0,2e3,"hitslop - right"),bottom:i(t.bottom,0,2e3,"hitslop - bottom")}}function a(t,o){return{left:t.left-o.left,right:t.right+o.right,top:t.top-o.top,bottom:t.bottom+o.bottom}}function g(t,o){return!t||!o?t===o:t.left===o.left&&t.right===o.right&&t.top===o.top&&t.bottom===o.bottom}function b(t,o){return t.x>=o.left&&t.x<=o.right&&t.y>=o.top&&t.y<=o.bottom}export{i as a,p as b,a as c,g as d,b as e};
|
package/dist/index.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ type Rect = {
|
|
|
27
27
|
* A callback function that is executed when a foresight interaction
|
|
28
28
|
* (e.g., hover, trajectory hit) occurs on a registered element.
|
|
29
29
|
*/
|
|
30
|
-
type ForesightCallback = () => void;
|
|
30
|
+
type ForesightCallback = (element: ForesightElementData) => void;
|
|
31
31
|
/**
|
|
32
32
|
* Represents the HTML element that is being tracked by the ForesightManager.
|
|
33
33
|
* This is typically a standard DOM `Element`.
|
|
@@ -77,6 +77,8 @@ type ForesightRegisterResult = {
|
|
|
77
77
|
* @deprecated no longer need to call this manually, you can call Foresightmanager.instance.unregister if needed
|
|
78
78
|
*/
|
|
79
79
|
unregister: () => void;
|
|
80
|
+
/** The data associated with the registered element. This will be null if the element was not registered */
|
|
81
|
+
elementData: ForesightElementData | null;
|
|
80
82
|
};
|
|
81
83
|
/**
|
|
82
84
|
* Represents the data associated with a registered foresight element.
|
|
@@ -191,6 +193,17 @@ type ForesightManagerData = {
|
|
|
191
193
|
eventListeners: ReadonlyMap<keyof ForesightEventMap, ForesightEventListener[]>;
|
|
192
194
|
currentDeviceStrategy: CurrentDeviceStrategy;
|
|
193
195
|
activeElementCount: number;
|
|
196
|
+
loadedModules: {
|
|
197
|
+
desktopHandler: boolean;
|
|
198
|
+
touchHandler: boolean;
|
|
199
|
+
predictors: {
|
|
200
|
+
mouse: boolean;
|
|
201
|
+
tab: boolean;
|
|
202
|
+
scroll: boolean;
|
|
203
|
+
viewport: boolean;
|
|
204
|
+
touchStart: boolean;
|
|
205
|
+
};
|
|
206
|
+
};
|
|
194
207
|
};
|
|
195
208
|
type TouchDeviceStrategy = "none" | "viewport" | "onTouchStart";
|
|
196
209
|
type MinimumConnectionType = "slow-2g" | "2g" | "3g" | "4g";
|
|
@@ -305,8 +318,20 @@ type UpdateForsightManagerSettings = BaseForesightManagerSettings & {
|
|
|
305
318
|
/**
|
|
306
319
|
* Type used to register elements to the foresight manager
|
|
307
320
|
*/
|
|
308
|
-
type ForesightRegisterOptions = {
|
|
321
|
+
type ForesightRegisterOptions = ForesightRegisterOptionsWithoutElement & {
|
|
309
322
|
element: ForesightElement;
|
|
323
|
+
};
|
|
324
|
+
type ForesightRegisterNodeListOptions = ForesightRegisterOptionsWithoutElement & {
|
|
325
|
+
element: NodeListOf<ForesightElement>;
|
|
326
|
+
};
|
|
327
|
+
/**
|
|
328
|
+
* Use full for if you want to create a custom button component in a modern framework (for example React).
|
|
329
|
+
* And you want to have the ForesightRegisterOptions used in ForesightManager.instance.register({})
|
|
330
|
+
* without the element as the element will be the ref of the component.
|
|
331
|
+
*
|
|
332
|
+
* @link https://foresightjs.com/docs/getting_started/typescript#foresightregisteroptionswithoutelement
|
|
333
|
+
*/
|
|
334
|
+
type ForesightRegisterOptionsWithoutElement = {
|
|
310
335
|
callback: ForesightCallback;
|
|
311
336
|
hitSlop?: HitSlop;
|
|
312
337
|
/**
|
|
@@ -325,14 +350,6 @@ type ForesightRegisterOptions = {
|
|
|
325
350
|
*/
|
|
326
351
|
reactivateAfter?: number;
|
|
327
352
|
};
|
|
328
|
-
/**
|
|
329
|
-
* Usefull for if you want to create a custom button component in a modern framework (for example React).
|
|
330
|
-
* And you want to have the ForesightRegisterOptions used in ForesightManager.instance.register({})
|
|
331
|
-
* without the element as the element will be the ref of the component.
|
|
332
|
-
*
|
|
333
|
-
* @link https://foresightjs.com/docs/getting_started/typescript#foresightregisteroptionswithoutelement
|
|
334
|
-
*/
|
|
335
|
-
type ForesightRegisterOptionsWithoutElement = Omit<ForesightRegisterOptions, "element">;
|
|
336
353
|
/**
|
|
337
354
|
* Fully invisible "slop" around the element.
|
|
338
355
|
* Basically increases the hover hitbox
|
|
@@ -455,7 +472,8 @@ declare class ForesightManager {
|
|
|
455
472
|
private activeElementCount;
|
|
456
473
|
private desktopHandler;
|
|
457
474
|
private touchDeviceHandler;
|
|
458
|
-
private
|
|
475
|
+
private currentlyActiveHandler;
|
|
476
|
+
private handlerDependencies;
|
|
459
477
|
private isSetup;
|
|
460
478
|
private pendingPointerEvent;
|
|
461
479
|
private rafId;
|
|
@@ -465,10 +483,13 @@ declare class ForesightManager {
|
|
|
465
483
|
private _globalCallbackHits;
|
|
466
484
|
private _globalSettings;
|
|
467
485
|
private constructor();
|
|
486
|
+
private getOrCreateDesktopHandler;
|
|
487
|
+
private getOrCreateTouchHandler;
|
|
468
488
|
static initialize(props?: Partial<UpdateForsightManagerSettings>): ForesightManager;
|
|
469
489
|
static get isInitiated(): Readonly<boolean>;
|
|
470
490
|
static get instance(): ForesightManager;
|
|
471
491
|
private generateId;
|
|
492
|
+
private get isUsingDesktopHandler();
|
|
472
493
|
addEventListener<K extends ForesightEvent>(eventType: K, listener: ForesightEventListener<K>, options?: {
|
|
473
494
|
signal?: AbortSignal;
|
|
474
495
|
}): void;
|
|
@@ -476,9 +497,13 @@ declare class ForesightManager {
|
|
|
476
497
|
hasListeners<K extends ForesightEvent>(eventType: K): boolean;
|
|
477
498
|
get getManagerData(): Readonly<ForesightManagerData>;
|
|
478
499
|
get registeredElements(): ReadonlyMap<ForesightElement, ForesightElementData>;
|
|
500
|
+
register(options: ForesightRegisterNodeListOptions): ForesightRegisterResult[];
|
|
479
501
|
register(options: ForesightRegisterOptions): ForesightRegisterResult;
|
|
480
|
-
|
|
481
|
-
|
|
502
|
+
private registerElement;
|
|
503
|
+
unregister(element: ForesightElement | NodeListOf<ForesightElement>, unregisterReason?: ElementUnregisteredReason): void;
|
|
504
|
+
private unregisterElement;
|
|
505
|
+
reactivate(element: ForesightElement | NodeListOf<ForesightElement>): void;
|
|
506
|
+
private reactivateElement;
|
|
482
507
|
private clearReactivateTimeout;
|
|
483
508
|
updateCheckableStatus(elementData: ForesightElementData): void;
|
|
484
509
|
private callCallback;
|
|
@@ -503,4 +528,4 @@ declare class ForesightManager {
|
|
|
503
528
|
|
|
504
529
|
type HasListenersFunction = <K extends ForesightEvent>(eventType: K) => boolean;
|
|
505
530
|
|
|
506
|
-
export { type CallbackCompletedEvent, type CallbackHitType, type CallbackHits, type CallbackInvokedEvent, type DeviceStrategyChangedEvent, type ElementCallbackInfo, type ElementDataUpdatedEvent, type ElementReactivatedEvent, type ElementRegisteredEvent, type ElementUnregisteredEvent, type ForesightElement, type ForesightElementData, type ForesightEvent, ForesightManager, type ForesightManagerSettings, type Rect as ForesightRect, type ForesightRegisterOptions, type ForesightRegisterOptionsWithoutElement, type ForesightRegisterResult, type HasListenersFunction, type HitSlop, type ManagerSettingsChangedEvent, type MinimumConnectionType, type MouseTrajectoryUpdateEvent, type ScrollTrajectoryUpdateEvent, type TouchDeviceStrategy, type UpdateForsightManagerSettings, type UpdatedManagerSetting };
|
|
531
|
+
export { type CallbackCompletedEvent, type CallbackHitType, type CallbackHits, type CallbackInvokedEvent, type DeviceStrategyChangedEvent, type ElementCallbackInfo, type ElementDataUpdatedEvent, type ElementReactivatedEvent, type ElementRegisteredEvent, type ElementUnregisteredEvent, type ForesightCallback, type ForesightElement, type ForesightElementData, type ForesightEvent, ForesightManager, type ForesightManagerSettings, type Rect as ForesightRect, type ForesightRegisterNodeListOptions, type ForesightRegisterOptions, type ForesightRegisterOptionsWithoutElement, type ForesightRegisterResult, type HasListenersFunction, type HitSlop, type ManagerSettingsChangedEvent, type MinimumConnectionType, type MouseTrajectoryUpdateEvent, type ScrollTrajectoryUpdateEvent, type TouchDeviceStrategy, type UpdateForsightManagerSettings, type UpdatedManagerSetting };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function p(n,e,t,i){return n<e?console.warn(`ForesightJS: "${i}" value ${n} is below minimum bound ${e}, clamping to ${e}`):n>t&&console.warn(`ForesightJS: "${i}" value ${n} is above maximum bound ${t}, clamping to ${t}`),Math.min(Math.max(n,e),t)}function E(n){if(typeof n=="number"){let e=p(n,0,2e3,"hitslop");return{top:e,left:e,right:e,bottom:e}}return{top:p(n.top,0,2e3,"hitslop - top"),left:p(n.left,0,2e3,"hitslop - left"),right:p(n.right,0,2e3,"hitslop - right"),bottom:p(n.bottom,0,2e3,"hitslop - bottom")}}function g(n,e){return{left:n.left-e.left,right:n.right+e.right,top:n.top-e.top,bottom:n.bottom+e.bottom}}function b(n,e){return!n||!e?n===e:n.left===e.left&&n.right===e.right&&n.top===e.top&&n.bottom===e.bottom}function T(n,e){return n.x>=e.left&&n.x<=e.right&&n.y>=e.top&&n.y<=e.bottom}function N(){let n=O(),e=Z();return{isTouchDevice:n,isLimitedConnection:e,shouldRegister:!e}}function O(){return typeof window>"u"||typeof navigator>"u"?!1:window.matchMedia("(pointer: coarse)").matches&&navigator.maxTouchPoints>0}function Z(){let n=navigator.connection;if(!n)return!1;let e=S.instance.getManagerData.globalSettings.minimumConnectionType,t=["slow-2g","2g","3g","4g"],i=t.indexOf(n.effectiveType),o=t.indexOf(e);return i<o||n.saveData}function U(n){if(typeof window>"u"||typeof document>"u")return!1;let e=window.innerWidth||document.documentElement.clientWidth,t=window.innerHeight||document.documentElement.clientHeight;return n.top<t&&n.bottom>0&&n.left<e&&n.right>0}function j(){return{mouse:{hover:0,trajectory:0},tab:{forwards:0,reverse:0},scroll:{down:0,left:0,right:0,up:0},touch:0,viewport:0,total:0}}function B(){return{debug:!1,enableManagerLogging:!1,enableMousePrediction:!0,enableScrollPrediction:!0,positionHistorySize:8,trajectoryPredictionTime:120,scrollMargin:150,defaultHitSlop:{top:0,left:0,right:0,bottom:0},enableTabPrediction:!0,tabOffset:2,touchDeviceStrategy:"onTouchStart",minimumConnectionType:"3g"}}function K(n,e,t){let{element:i,callback:o,hitSlop:r,name:a,meta:c,reactivateAfter:h}=n,l=i.getBoundingClientRect(),s=r?E(r):t;return{id:e,element:i,callback:o,elementBounds:{originalRect:l,expandedRect:g(l,s),hitSlop:s},name:a||i.id||"unnamed",isIntersectingWithViewport:U(l),registerCount:1,meta:c??{},callbackInfo:{callbackFiredCount:0,lastCallbackInvokedAt:void 0,lastCallbackCompletedAt:void 0,lastCallbackRuntime:void 0,lastCallbackStatus:void 0,lastCallbackErrorMessage:void 0,reactivateAfter:h??1/0,isCallbackActive:!0,isRunningCallback:!1,reactivateTimeoutId:void 0}}}var I=class{constructor(){this.eventListeners=new Map}addEventListener(e,t,i){if(i?.signal?.aborted)return;let o=this.eventListeners.get(e)??[];o.push(t),this.eventListeners.set(e,o),i?.signal?.addEventListener("abort",()=>this.removeEventListener(e,t))}removeEventListener(e,t){let i=this.eventListeners.get(e);if(!i)return;let o=i.indexOf(t);o>-1&&i.splice(o,1)}emit(e){let t=this.eventListeners.get(e.type);if(!(!t||t.length===0))for(let i=0;i<t.length;i++)try{let o=t[i];o&&o(e)}catch(o){console.error(`Error in ForesightManager event listener ${i} for ${e.type}:`,o)}}hasListeners(e){let t=this.eventListeners.get(e);return t!==void 0&&t.length>0}getEventListeners(){return this.eventListeners}};function w(n,e){return n!==void 0&&e!==n}var pe={trajectoryPredictionTime:{min:10,max:200},positionHistorySize:{min:2,max:30},scrollMargin:{min:30,max:300},tabOffset:{min:0,max:20}};function C(n,e,t){if(!w(t,n[e]))return!1;let{min:i,max:o}=pe[e];return n[e]=p(t,i,o,e),!0}function P(n,e,t){return w(t,n[e])?(n[e]=t,!0):!1}function z(n,e){C(n,"trajectoryPredictionTime",e.trajectoryPredictionTime),C(n,"positionHistorySize",e.positionHistorySize),C(n,"scrollMargin",e.scrollMargin),C(n,"tabOffset",e.tabOffset),P(n,"enableMousePrediction",e.enableMousePrediction),P(n,"enableScrollPrediction",e.enableScrollPrediction),P(n,"enableTabPrediction",e.enableTabPrediction),P(n,"enableManagerLogging",e.enableManagerLogging),e.defaultHitSlop!==void 0&&(n.defaultHitSlop=E(e.defaultHitSlop)),e.touchDeviceStrategy!==void 0&&(n.touchDeviceStrategy=e.touchDeviceStrategy),e.minimumConnectionType!==void 0&&(n.minimumConnectionType=e.minimumConnectionType),e.debug!==void 0&&(n.debug=e.debug)}function V(n,e){let t=[],i=!1,o=!1,r=!1,a=!1,c=!1;if(!e)return{changedSettings:t,positionHistorySizeChanged:i,scrollPredictionChanged:o,tabPredictionChanged:r,hitSlopChanged:a,touchStrategyChanged:c};let h=["trajectoryPredictionTime","positionHistorySize","scrollMargin","tabOffset"];for(let s of h){let u=n[s];C(n,s,e[s])&&(t.push({setting:s,oldValue:u,newValue:n[s]}),s==="positionHistorySize"&&(i=!0))}let l=["enableMousePrediction","enableScrollPrediction","enableTabPrediction"];for(let s of l){let u=n[s];P(n,s,e[s])&&(t.push({setting:s,oldValue:u,newValue:n[s]}),s==="enableScrollPrediction"&&(o=!0),s==="enableTabPrediction"&&(r=!0))}if(e.defaultHitSlop!==void 0){let s=n.defaultHitSlop,u=E(e.defaultHitSlop);b(s,u)||(n.defaultHitSlop=u,t.push({setting:"defaultHitSlop",oldValue:s,newValue:u}),a=!0)}if(e.touchDeviceStrategy!==void 0){let s=n.touchDeviceStrategy;n.touchDeviceStrategy=e.touchDeviceStrategy,t.push({setting:"touchDeviceStrategy",oldValue:s,newValue:e.touchDeviceStrategy}),c=!0}if(e.minimumConnectionType!==void 0){let s=n.minimumConnectionType;n.minimumConnectionType=e.minimumConnectionType,t.push({setting:"minimumConnectionType",oldValue:s,newValue:e.minimumConnectionType})}return{changedSettings:t,positionHistorySizeChanged:i,scrollPredictionChanged:o,tabPredictionChanged:r,hitSlopChanged:a,touchStrategyChanged:c}}var d=class{constructor(e){this._isConnected=!1;this._cachedLogStyle=null;this.elements=e.elements,this.callCallback=e.callCallback,this.emit=e.emit,this.hasListeners=e.hasListeners,this.settings=e.settings}get isConnected(){return this._isConnected}disconnect(){this.isConnected&&(this.devLog(`Disconnecting ${this.moduleName}...`),this.abortController?.abort(`${this.moduleName} module disconnected`),this.onDisconnect(),this._isConnected=!1)}connect(){this.devLog(`Connecting ${this.moduleName}...`),this.onConnect(),this._isConnected=!0}devLog(e){if(this.settings.enableManagerLogging){if(this._cachedLogStyle===null){let t=this.moduleName.includes("Predictor")?"#ea580c":"#2563eb";this._cachedLogStyle=`color: ${t}; font-weight: bold;`}console.log(`%c${this.moduleName}: ${e}`,this._cachedLogStyle)}}createAbortController(){this.abortController&&!this.abortController.signal.aborted||(this.abortController=new AbortController,this.devLog(`Created new AbortController for ${this.moduleName}`))}};function M(n,e,t){let i=0,o=1,r=e.x-n.x,a=e.y-n.y,c=(h,l)=>{if(h===0){if(l<0)return!1}else{let s=l/h;if(h<0){if(s>o)return!1;s>i&&(i=s)}else{if(s<i)return!1;s<o&&(o=s)}}return!0};return!c(-r,n.x-t.left)||!c(r,t.right-n.x)||!c(-a,n.y-t.top)||!c(a,t.bottom-n.y)?!1:i<=o}function $(n,e,t,i){let o=performance.now();e.add({point:{x:n.x,y:n.y},time:o});let{x:r,y:a}=n;if(e.length<2){i.x=r,i.y=a;return}let[c,h]=e.getFirstLast();if(!c||!h){i.x=r,i.y=a;return}let l=(h.time-c.time)*.001;if(l===0){i.x=r,i.y=a;return}let s=h.point.x-c.point.x,u=h.point.y-c.point.y,W=s/l,J=u/l,H=t*.001;i.x=r+W*H,i.y=a+J*H}var k=class extends d{constructor(t){super(t.dependencies);this.moduleName="MousePredictor";this.trajectoryPositions=t.trajectoryPositions}updatePointerState(t){let i=this.trajectoryPositions.currentPoint;i.x=t.clientX,i.y=t.clientY,this.settings.enableMousePrediction?$(i,this.trajectoryPositions.positions,this.settings.trajectoryPredictionTime,this.trajectoryPositions.predictedPoint):(this.trajectoryPositions.predictedPoint.x=i.x,this.trajectoryPositions.predictedPoint.y=i.y)}processMouseMovement(t){this.updatePointerState(t);let i=this.settings.enableMousePrediction,o=this.trajectoryPositions.currentPoint;for(let r of this.elements.values()){if(!r.isIntersectingWithViewport||!r.callbackInfo.isCallbackActive||r.callbackInfo.isRunningCallback)continue;let a=r.elementBounds.expandedRect;if(i)M(o,this.trajectoryPositions.predictedPoint,a)&&this.callCallback(r,{kind:"mouse",subType:"trajectory"});else if(T(o,a)){this.callCallback(r,{kind:"mouse",subType:"hover"});return}}this.hasListeners("mouseTrajectoryUpdate")&&this.emit({type:"mouseTrajectoryUpdate",predictionEnabled:i,trajectoryPositions:this.trajectoryPositions})}onDisconnect(){}onConnect(){}};function G(n,e){let i=e.top-n.top,o=e.left-n.left;return i<-1?"down":i>1?"up":o<-1?"right":o>1?"left":"none"}function Y(n,e,t){let{x:i,y:o}=n,r={x:i,y:o};switch(e){case"down":r.y+=t;break;case"up":r.y-=t;break;case"left":r.x-=t;break;case"right":r.x+=t;break;case"none":break;default:}return r}var R=class extends d{constructor(t){super(t.dependencies);this.moduleName="ScrollPredictor";this.predictedScrollPoint=null;this.scrollDirection=null;this.onDisconnect=()=>this.resetScrollProps();this.trajectoryPositions=t.trajectoryPositions}resetScrollProps(){this.scrollDirection=null,this.predictedScrollPoint=null}handleScrollPrefetch(t,i){!t.isIntersectingWithViewport||t.callbackInfo.isRunningCallback||!t.callbackInfo.isCallbackActive||(this.scrollDirection=this.scrollDirection??G(t.elementBounds.originalRect,i),this.scrollDirection!=="none"&&(this.predictedScrollPoint=this.predictedScrollPoint??Y(this.trajectoryPositions.currentPoint,this.scrollDirection,this.settings.scrollMargin),M(this.trajectoryPositions.currentPoint,this.predictedScrollPoint,t.elementBounds.expandedRect)&&this.callCallback(t,{kind:"scroll",subType:this.scrollDirection}),this.hasListeners("scrollTrajectoryUpdate")&&this.emit({type:"scrollTrajectoryUpdate",currentPoint:this.trajectoryPositions.currentPoint,predictedPoint:this.predictedScrollPoint,scrollDirection:this.scrollDirection})))}onConnect(){}};import{tabbable as me}from"tabbable";function X(n,e,t,i){if(e!==null&&e>-1){let o=n?e-1:e+1;if(o>=0&&o<t.length&&t[o]===i)return o}return t.findIndex(o=>o===i)}var x=class extends d{constructor(t){super(t);this.moduleName="TabPredictor";this.lastKeyDown=null;this.tabbableElementsCache=[];this.lastFocusedIndex=null;this.handleKeyDown=t=>{t.key==="Tab"&&(this.lastKeyDown=t)};this.handleFocusIn=t=>{if(!this.lastKeyDown)return;let i=t.target;if(!(i instanceof HTMLElement))return;(!this.tabbableElementsCache.length||this.lastFocusedIndex===-1)&&(this.devLog("Caching tabbable elements"),this.tabbableElementsCache=me(document.documentElement));let o=this.lastKeyDown.shiftKey,r=X(o,this.lastFocusedIndex,this.tabbableElementsCache,i);this.lastFocusedIndex=r,this.lastKeyDown=null;let a=[],c=this.settings.tabOffset,h=this.elements;for(let l=0;l<=c;l++){let s=o?r-l:r+l,u=this.tabbableElementsCache[s];u&&u instanceof Element&&h.has(u)&&a.push(u)}for(let l of a){let s=h.get(l);s&&!s.callbackInfo.isRunningCallback&&s.callbackInfo.isCallbackActive&&this.callCallback(s,{kind:"tab",subType:o?"reverse":"forwards"})}}}invalidateCache(){this.tabbableElementsCache.length&&this.devLog("Invalidating tabbable elements cache"),this.tabbableElementsCache=[],this.lastFocusedIndex=null}onConnect(){typeof document>"u"||(this.createAbortController(),document.addEventListener("keydown",this.handleKeyDown,{signal:this.abortController?.signal,passive:!0}),document.addEventListener("focusin",this.handleFocusIn,{signal:this.abortController?.signal,passive:!0}))}onDisconnect(){this.tabbableElementsCache=[],this.lastFocusedIndex=null,this.lastKeyDown=null}};import{PositionObserver as ge}from"position-observer";var D=class{constructor(e){this.head=0;this.count=0;if(e<=0)throw new Error("CircularBuffer capacity must be greater than 0");this.capacity=e,this.buffer=new Array(e)}add(e){this.buffer[this.head]=e,this.head=(this.head+1)%this.capacity,this.count<this.capacity&&this.count++}getFirst(){if(this.count!==0)return this.count<this.capacity?this.buffer[0]:this.buffer[this.head]}getLast(){if(this.count!==0){if(this.count<this.capacity)return this.buffer[this.count-1];{let e=(this.head-1+this.capacity)%this.capacity;return this.buffer[e]}}}getFirstLast(){if(this.count===0)return[void 0,void 0];if(this.count===1){let i=this.count<this.capacity?this.buffer[0]:this.buffer[this.head];return[i,i]}let e=this.getFirst(),t=this.getLast();return[e,t]}resize(e){if(e<=0)throw new Error("CircularBuffer capacity must be greater than 0");if(e===this.capacity)return;let t=this.getAllItems();if(this.capacity=e,this.buffer=new Array(e),this.head=0,this.count=0,t.length>e){let i=t.slice(-e);for(let o of i)this.add(o)}else for(let i of t)this.add(i)}getAllItems(){if(this.count===0)return[];let e=new Array(this.count);if(this.count<this.capacity)for(let t=0;t<this.count;t++)e[t]=this.buffer[t];else{let t=this.head;for(let i=0;i<this.capacity;i++){let o=(t+i)%this.capacity;e[i]=this.buffer[o]}}return e}clear(){this.head=0,this.count=0}get length(){return this.count}get size(){return this.capacity}get isFull(){return this.count===this.capacity}get isEmpty(){return this.count===0}};var m=class extends d{constructor(t){super(t);this.moduleName="DesktopHandler";this.positionObserver=null;this.trajectoryPositions={positions:new D(8),currentPoint:{x:0,y:0},predictedPoint:{x:0,y:0}};this.handlePositionChange=t=>{let i=this.settings.enableScrollPrediction;for(let o of t){let r=this.elements.get(o.target);r&&(i?this.scrollPredictor?.handleScrollPrefetch(r,o.boundingClientRect):this.checkForMouseHover(r),this.handlePositionChangeDataUpdates(r,o))}i&&this.scrollPredictor?.resetScrollProps()};this.checkForMouseHover=t=>{T(this.trajectoryPositions.currentPoint,t.elementBounds.expandedRect)&&this.callCallback(t,{kind:"mouse",subType:"hover"})};this.handlePositionChangeDataUpdates=(t,i)=>{let o=[],r=i.isIntersecting;t.isIntersectingWithViewport!==r&&(o.push("visibility"),t.isIntersectingWithViewport=r),r&&!b(i.boundingClientRect,t.elementBounds.originalRect)&&(o.push("bounds"),t.elementBounds={hitSlop:t.elementBounds.hitSlop,originalRect:i.boundingClientRect,expandedRect:g(i.boundingClientRect,t.elementBounds.hitSlop)}),o.length&&this.hasListeners("elementDataUpdated")&&this.emit({type:"elementDataUpdated",elementData:t,updatedProps:o})};this.processMouseMovement=t=>this.mousePredictor.processMouseMovement(t);this.invalidateTabCache=()=>this.tabPredictor?.invalidateCache();this.observeElement=t=>this.positionObserver?.observe(t);this.unobserveElement=t=>this.positionObserver?.unobserve(t);this.connectTabPredictor=()=>this.tabPredictor.connect();this.connectScrollPredictor=()=>this.scrollPredictor.connect();this.connectMousePredictor=()=>this.mousePredictor.connect();this.disconnectTabPredictor=()=>this.tabPredictor.disconnect();this.disconnectScrollPredictor=()=>this.scrollPredictor.disconnect();this.disconnectMousePredictor=()=>this.mousePredictor.disconnect();this.tabPredictor=new x(t),this.scrollPredictor=new R({dependencies:t,trajectoryPositions:this.trajectoryPositions}),this.mousePredictor=new k({dependencies:t,trajectoryPositions:this.trajectoryPositions})}onConnect(){this.settings.enableTabPrediction&&this.connectTabPredictor(),this.settings.enableScrollPrediction&&this.connectScrollPredictor(),this.connectMousePredictor(),this.positionObserver=new ge(this.handlePositionChange);let t=["mouse"];this.settings.enableTabPrediction&&t.push("tab"),this.settings.enableScrollPrediction&&t.push("scroll"),this.devLog(`Connected predictors: [${t.join(", ")}] and PositionObserver`);for(let i of this.elements.keys())this.positionObserver.observe(i)}onDisconnect(){this.disconnectMousePredictor(),this.disconnectTabPredictor(),this.disconnectScrollPredictor(),this.positionObserver?.disconnect(),this.positionObserver=null}};var L=class extends d{constructor(t){super(t);this.moduleName="ViewportPredictor";this.intersectionObserver=null;this.onConnect=()=>this.intersectionObserver=new IntersectionObserver(this.handleViewportEnter);this.observeElement=t=>this.intersectionObserver?.observe(t);this.unobserveElement=t=>this.intersectionObserver?.unobserve(t);this.handleViewportEnter=t=>{for(let i of t){if(!i.isIntersecting)continue;let o=this.elements.get(i.target);o&&(this.callCallback(o,{kind:"viewport"}),this.unobserveElement(i.target))}}}onDisconnect(){this.intersectionObserver?.disconnect(),this.intersectionObserver=null}};var _=class extends d{constructor(t){super(t);this.moduleName="TouchStartPredictor";this.onConnect=()=>this.createAbortController();this.onDisconnect=()=>{};this.handleTouchStart=t=>{let i=t.currentTarget,o=this.elements.get(i);o&&(this.callCallback(o,{kind:"touch"}),this.unobserveElement(i))}}observeElement(t){t instanceof HTMLElement&&t.addEventListener("pointerdown",this.handleTouchStart,{signal:this.abortController?.signal})}unobserveElement(t){t instanceof HTMLElement&&t.removeEventListener("pointerdown",this.handleTouchStart)}};var f=class extends d{constructor(t){super(t);this.moduleName="TouchDeviceHandler";this.predictor=null;this.onDisconnect=()=>{this.devLog("Disconnecting touch predictor"),this.predictor?.disconnect()};this.onConnect=()=>this.setTouchPredictor();this.observeElement=t=>this.predictor?.observeElement(t);this.unobserveElement=t=>this.predictor?.unobserveElement(t);this.viewportPredictor=new L(t),this.touchStartPredictor=new _(t),this.predictor=this.viewportPredictor}setTouchPredictor(){switch(this.predictor?.disconnect(),this.settings.touchDeviceStrategy){case"viewport":this.predictor=this.viewportPredictor,this.devLog("Connected touch strategy: viewport (ViewportPredictor)");break;case"onTouchStart":this.predictor=this.touchStartPredictor,this.devLog("Connected touch strategy: onTouchStart (TouchStartPredictor)");break;case"none":this.predictor=null,this.devLog('Touch strategy set to "none" - no predictor connected');return;default:this.settings.touchDeviceStrategy}this.predictor?.connect();for(let t of this.elements.keys())this.predictor?.observeElement(t)}};var S=class n{constructor(e){this.elements=new Map;this.checkableElements=new Set;this.idCounter=0;this.activeElementCount=0;this.isSetup=!1;this.pendingPointerEvent=null;this.rafId=null;this.domObserver=null;this.currentDeviceStrategy=O()?"touch":"mouse";this.eventEmitter=new I;this._globalCallbackHits=j();this._globalSettings=B();this.handlePointerMove=e=>{this.pendingPointerEvent=e,e.pointerType!==this.currentDeviceStrategy&&(this.eventEmitter.emit({type:"deviceStrategyChanged",timestamp:Date.now(),newStrategy:e.pointerType,oldStrategy:this.currentDeviceStrategy}),this.setDeviceStrategy(this.currentDeviceStrategy=e.pointerType)),!this.rafId&&(this.rafId=requestAnimationFrame(()=>{if(this.handler instanceof f){this.rafId=null;return}this.pendingPointerEvent&&this.handler.processMouseMovement(this.pendingPointerEvent),this.rafId=null}))};this.handleDomMutations=e=>{if(!e.length)return;this.desktopHandler?.invalidateTabCache();let t=!1;for(let i=0;i<e.length;i++){let o=e[i];if(o&&o.type==="childList"&&o.removedNodes.length>0){t=!0;break}}if(t)for(let i of this.elements.keys())i.isConnected||this.unregister(i,"disconnected")};e!==void 0&&z(this._globalSettings,e);let t={elements:this.elements,checkableElements:this.checkableElements,callCallback:this.callCallback.bind(this),emit:this.eventEmitter.emit.bind(this.eventEmitter),hasListeners:this.eventEmitter.hasListeners.bind(this.eventEmitter),updateCheckableStatus:this.updateCheckableStatus.bind(this),settings:this._globalSettings};this.desktopHandler=new m(t),this.touchDeviceHandler=new f(t),this.handler=this.currentDeviceStrategy==="mouse"?this.desktopHandler:this.touchDeviceHandler,this.devLog(`ForesightManager initialized with device strategy: ${this.currentDeviceStrategy}`),this.initializeGlobalListeners()}static initialize(e){return this.isInitiated||(n.manager=new n(e)),n.manager}static get isInitiated(){return!!n.manager}static get instance(){return this.initialize()}generateId(){return`foresight-${++this.idCounter}`}addEventListener(e,t,i){this.eventEmitter.addEventListener(e,t,i)}removeEventListener(e,t){this.eventEmitter.removeEventListener(e,t)}hasListeners(e){return this.eventEmitter.hasListeners(e)}get getManagerData(){return{registeredElements:this.elements,globalSettings:this._globalSettings,globalCallbackHits:this._globalCallbackHits,eventListeners:this.eventEmitter.getEventListeners(),currentDeviceStrategy:this.currentDeviceStrategy,activeElementCount:this.activeElementCount}}get registeredElements(){return this.elements}register(e){let{isTouchDevice:t,isLimitedConnection:i,shouldRegister:o}=N();if(!o)return{isLimitedConnection:i,isTouchDevice:t,isRegistered:!1,unregister:()=>{}};let r=this.elements.get(e.element);if(r)return r.registerCount++,{isLimitedConnection:i,isTouchDevice:t,isRegistered:!1,unregister:()=>{}};this.isSetup||this.initializeGlobalListeners();let a=K(e,this.generateId(),this._globalSettings.defaultHitSlop);return this.elements.set(e.element,a),this.activeElementCount++,this.updateCheckableStatus(a),this.handler.observeElement(e.element),this.eventEmitter.emit({type:"elementRegistered",timestamp:Date.now(),elementData:a}),{isTouchDevice:t,isLimitedConnection:i,isRegistered:!0,unregister:()=>{this.unregister(e.element)}}}unregister(e,t){let i=this.elements.get(e);if(!i)return;this.clearReactivateTimeout(i),this.handler.unobserveElement(e),this.elements.delete(e),this.checkableElements.delete(i),i.callbackInfo.isCallbackActive&&this.activeElementCount--;let o=this.elements.size===0&&this.isSetup;o&&(this.devLog("All elements unregistered, removing global listeners"),this.removeGlobalListeners()),this.eventEmitter.emit({type:"elementUnregistered",elementData:i,timestamp:Date.now(),unregisterReason:t??"by user",wasLastRegisteredElement:o})}reactivate(e){let t=this.elements.get(e);t&&(this.isSetup||this.initializeGlobalListeners(),this.clearReactivateTimeout(t),t.callbackInfo.isRunningCallback||(t.callbackInfo.isCallbackActive=!0,this.activeElementCount++,this.updateCheckableStatus(t),this.handler.observeElement(e),this.eventEmitter.emit({type:"elementReactivated",elementData:t,timestamp:Date.now()})))}clearReactivateTimeout(e){clearTimeout(e.callbackInfo.reactivateTimeoutId),e.callbackInfo.reactivateTimeoutId=void 0}updateCheckableStatus(e){e.isIntersectingWithViewport&&e.callbackInfo.isCallbackActive&&!e.callbackInfo.isRunningCallback?this.checkableElements.add(e):this.checkableElements.delete(e)}callCallback(e,t){e.callbackInfo.isRunningCallback||!e.callbackInfo.isCallbackActive||(this.markElementAsRunning(e),this.executeCallbackAsync(e,t))}markElementAsRunning(e){e.callbackInfo.callbackFiredCount++,e.callbackInfo.lastCallbackInvokedAt=Date.now(),e.callbackInfo.isRunningCallback=!0,this.clearReactivateTimeout(e),this.checkableElements.delete(e)}async executeCallbackAsync(e,t){this.updateHitCounters(t),this.eventEmitter.emit({type:"callbackInvoked",timestamp:Date.now(),elementData:e,hitType:t});let i=performance.now(),o,r=null;try{await e.callback(),o="success"}catch(a){r=a instanceof Error?a.message:String(a),o="error",console.error(`Error in callback for element ${e.name}:`,a)}this.finalizeCallback(e,t,i,o,r)}finalizeCallback(e,t,i,o,r){e.callbackInfo.lastCallbackCompletedAt=Date.now(),e.callbackInfo.isRunningCallback=!1,e.callbackInfo.isCallbackActive=!1,this.activeElementCount--,this.handler.unobserveElement(e.element),e.callbackInfo.reactivateAfter!==1/0&&(e.callbackInfo.reactivateTimeoutId=setTimeout(()=>{this.reactivate(e.element)},e.callbackInfo.reactivateAfter));let a=this.activeElementCount===0;a&&(this.devLog("All elements unactivated, removing global listeners"),this.removeGlobalListeners()),this.eventEmitter.emit({type:"callbackCompleted",timestamp:Date.now(),elementData:e,hitType:t,elapsed:e.callbackInfo.lastCallbackRuntime=performance.now()-i,status:e.callbackInfo.lastCallbackStatus=o,errorMessage:e.callbackInfo.lastCallbackErrorMessage=r,wasLastActiveElement:a})}updateHitCounters(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]++;break;case"touch":this._globalCallbackHits.touch++;break;case"viewport":this._globalCallbackHits.viewport++;break;default:}this._globalCallbackHits.total++}setDeviceStrategy(e){let t=this.handler instanceof m?"mouse":"touch";t!==e&&this.devLog(`Switching device strategy from ${t} to ${e}`),this.handler.disconnect(),this.handler=e==="mouse"?this.desktopHandler:this.touchDeviceHandler,this.handler.connect()}initializeGlobalListeners(){this.isSetup||typeof document>"u"||(this.devLog("Initializing global listeners (pointermove, MutationObserver)"),this.setDeviceStrategy(this.currentDeviceStrategy),document.addEventListener("pointermove",this.handlePointerMove),this.domObserver=new MutationObserver(this.handleDomMutations),this.domObserver.observe(document.documentElement,{childList:!0,subtree:!0,attributes:!1}),this.isSetup=!0)}removeGlobalListeners(){typeof document>"u"||(this.isSetup=!1,this.domObserver?.disconnect(),this.domObserver=null,document.removeEventListener("pointermove",this.handlePointerMove),this.handler.disconnect(),this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.pendingPointerEvent=null)}alterGlobalSettings(e){let t=V(this._globalSettings,e);t.positionHistorySizeChanged&&this.desktopHandler.trajectoryPositions.positions.resize(this._globalSettings.positionHistorySize),t.scrollPredictionChanged&&this.handler instanceof m&&(this._globalSettings.enableScrollPrediction?this.handler.connectScrollPredictor():this.handler.disconnectScrollPredictor()),t.tabPredictionChanged&&this.handler instanceof m&&(this._globalSettings.enableTabPrediction?this.handler.connectTabPredictor():this.handler.disconnectTabPredictor()),t.hitSlopChanged&&this.forceUpdateAllElementBounds(),t.touchStrategyChanged&&this.handler instanceof f&&this.handler.setTouchPredictor(),t.changedSettings.length>0&&this.eventEmitter.emit({type:"managerSettingsChanged",timestamp:Date.now(),managerData:this.getManagerData,updatedSettings:t.changedSettings})}forceUpdateAllElementBounds(){for(let e of this.elements.values())e.isIntersectingWithViewport&&this.forceUpdateElementBounds(e)}forceUpdateElementBounds(e){let t=e.element.getBoundingClientRect(),i=g(t,e.elementBounds.hitSlop);if(!b(i,e.elementBounds.expandedRect)){let o={...e,elementBounds:{...e.elementBounds,originalRect:t,expandedRect:i}};this.elements.set(e.element,o),this.eventEmitter.emit({type:"elementDataUpdated",elementData:o,updatedProps:["bounds"]})}}devLog(e){this._globalSettings.enableManagerLogging&&console.log(`%c\u{1F6E0}\uFE0F ForesightManager: ${e}`,"color: #16a34a; font-weight: bold;")}};export{S as ForesightManager};
|
|
1
|
+
import{a as C,b as g,c as v,d as p}from"./chunk-PAYO6NXN.js";function k(){let n=E(),e=L();return{isTouchDevice:n,isLimitedConnection:e,shouldRegister:!e}}function E(){return typeof window>"u"||typeof navigator>"u"?!1:window.matchMedia("(pointer: coarse)").matches&&navigator.maxTouchPoints>0}function L(){let n=navigator.connection;if(!n)return!1;let e=u.instance.getManagerData.globalSettings.minimumConnectionType,t=["slow-2g","2g","3g","4g"],i=t.indexOf(n.effectiveType),r=t.indexOf(e);return i<r||n.saveData}function F(n){if(typeof window>"u"||typeof document>"u")return!1;let e=window.innerWidth||document.documentElement.clientWidth,t=window.innerHeight||document.documentElement.clientHeight;return n.top<t&&n.bottom>0&&n.left<e&&n.right>0}function T(){return{mouse:{hover:0,trajectory:0},tab:{forwards:0,reverse:0},scroll:{down:0,left:0,right:0,up:0},touch:0,viewport:0,total:0}}function H(){return{debug:!1,enableManagerLogging:!1,enableMousePrediction:!0,enableScrollPrediction:!0,positionHistorySize:8,trajectoryPredictionTime:120,scrollMargin:150,defaultHitSlop:{top:0,left:0,right:0,bottom:0},enableTabPrediction:!0,tabOffset:2,touchDeviceStrategy:"onTouchStart",minimumConnectionType:"3g"}}function R(n,e,t){let{element:i,callback:r,hitSlop:o,name:s,meta:c,reactivateAfter:b}=n,d=i.getBoundingClientRect(),a=o?g(o):t;return{id:e,element:i,callback:r,elementBounds:{originalRect:d,expandedRect:v(d,a),hitSlop:a},name:s||i.id||"unnamed",isIntersectingWithViewport:F(d),registerCount:1,meta:c??{},callbackInfo:{callbackFiredCount:0,lastCallbackInvokedAt:void 0,lastCallbackCompletedAt:void 0,lastCallbackRuntime:void 0,lastCallbackStatus:void 0,lastCallbackErrorMessage:void 0,reactivateAfter:b??1/0,isCallbackActive:!0,isRunningCallback:!1,reactivateTimeoutId:void 0}}}var f=class{constructor(){this.eventListeners=new Map}addEventListener(e,t,i){if(i?.signal?.aborted)return;let r=this.eventListeners.get(e)??[];r.push(t),this.eventListeners.set(e,r),i?.signal?.addEventListener("abort",()=>this.removeEventListener(e,t))}removeEventListener(e,t){let i=this.eventListeners.get(e);if(!i)return;let r=i.indexOf(t);r>-1&&i.splice(r,1)}emit(e){let t=this.eventListeners.get(e.type);if(!(!t||t.length===0))for(let i=0;i<t.length;i++)try{let r=t[i];r&&r(e)}catch(r){console.error(`Error in ForesightManager event listener ${i} for ${e.type}:`,r)}}hasListeners(e){let t=this.eventListeners.get(e);return t!==void 0&&t.length>0}getEventListeners(){return this.eventListeners}};function y(n,e){return n!==void 0&&e!==n}var I={trajectoryPredictionTime:{min:10,max:200},positionHistorySize:{min:2,max:30},scrollMargin:{min:30,max:300},tabOffset:{min:0,max:20}};function h(n,e,t){if(!y(t,n[e]))return!1;let{min:i,max:r}=I[e];return n[e]=C(t,i,r,e),!0}function m(n,e,t){return y(t,n[e])?(n[e]=t,!0):!1}function M(n,e){h(n,"trajectoryPredictionTime",e.trajectoryPredictionTime),h(n,"positionHistorySize",e.positionHistorySize),h(n,"scrollMargin",e.scrollMargin),h(n,"tabOffset",e.tabOffset),m(n,"enableMousePrediction",e.enableMousePrediction),m(n,"enableScrollPrediction",e.enableScrollPrediction),m(n,"enableTabPrediction",e.enableTabPrediction),m(n,"enableManagerLogging",e.enableManagerLogging),e.defaultHitSlop!==void 0&&(n.defaultHitSlop=g(e.defaultHitSlop)),e.touchDeviceStrategy!==void 0&&(n.touchDeviceStrategy=e.touchDeviceStrategy),e.minimumConnectionType!==void 0&&(n.minimumConnectionType=e.minimumConnectionType),e.debug!==void 0&&(n.debug=e.debug)}function D(n,e){let t=[],i=!1,r=!1,o=!1,s=!1,c=!1;if(!e)return{changedSettings:t,positionHistorySizeChanged:i,scrollPredictionChanged:r,tabPredictionChanged:o,hitSlopChanged:s,touchStrategyChanged:c};let b=["trajectoryPredictionTime","positionHistorySize","scrollMargin","tabOffset"];for(let a of b){let l=n[a];h(n,a,e[a])&&(t.push({setting:a,oldValue:l,newValue:n[a]}),a==="positionHistorySize"&&(i=!0))}let d=["enableMousePrediction","enableScrollPrediction","enableTabPrediction"];for(let a of d){let l=n[a];m(n,a,e[a])&&(t.push({setting:a,oldValue:l,newValue:n[a]}),a==="enableScrollPrediction"&&(r=!0),a==="enableTabPrediction"&&(o=!0))}if(e.defaultHitSlop!==void 0){let a=n.defaultHitSlop,l=g(e.defaultHitSlop);p(a,l)||(n.defaultHitSlop=l,t.push({setting:"defaultHitSlop",oldValue:a,newValue:l}),s=!0)}if(e.touchDeviceStrategy!==void 0){let a=n.touchDeviceStrategy;n.touchDeviceStrategy=e.touchDeviceStrategy,t.push({setting:"touchDeviceStrategy",oldValue:a,newValue:e.touchDeviceStrategy}),c=!0}if(e.minimumConnectionType!==void 0){let a=n.minimumConnectionType;n.minimumConnectionType=e.minimumConnectionType,t.push({setting:"minimumConnectionType",oldValue:a,newValue:e.minimumConnectionType})}return{changedSettings:t,positionHistorySizeChanged:i,scrollPredictionChanged:r,tabPredictionChanged:o,hitSlopChanged:s,touchStrategyChanged:c}}var u=class n{constructor(e){this.elements=new Map;this.checkableElements=new Set;this.idCounter=0;this.activeElementCount=0;this.desktopHandler=null;this.touchDeviceHandler=null;this.currentlyActiveHandler=null;this.isSetup=!1;this.pendingPointerEvent=null;this.rafId=null;this.domObserver=null;this.currentDeviceStrategy=E()?"touch":"mouse";this.eventEmitter=new f;this._globalCallbackHits=T();this._globalSettings=H();this.handlePointerMove=e=>{this.pendingPointerEvent=e,e.pointerType!==this.currentDeviceStrategy&&(this.eventEmitter.emit({type:"deviceStrategyChanged",timestamp:Date.now(),newStrategy:e.pointerType,oldStrategy:this.currentDeviceStrategy}),this.currentDeviceStrategy=e.pointerType,this.setDeviceStrategy(this.currentDeviceStrategy)),!this.rafId&&(this.rafId=requestAnimationFrame(()=>{if(!this.isUsingDesktopHandler){this.rafId=null;return}this.pendingPointerEvent&&this.desktopHandler?.processMouseMovement(this.pendingPointerEvent),this.rafId=null}))};this.handleDomMutations=e=>{if(!e.length)return;this.desktopHandler?.invalidateTabCache();let t=!1;for(let i=0;i<e.length;i++){let r=e[i];if(r&&r.type==="childList"&&r.removedNodes.length>0){t=!0;break}}if(t)for(let i of this.elements.keys())i.isConnected||this.unregister(i,"disconnected")};e!==void 0&&M(this._globalSettings,e),this.handlerDependencies={elements:this.elements,callCallback:this.callCallback.bind(this),emit:this.eventEmitter.emit.bind(this.eventEmitter),hasListeners:this.eventEmitter.hasListeners.bind(this.eventEmitter),settings:this._globalSettings},this.devLog(`ForesightManager initialized with device strategy: ${this.currentDeviceStrategy}`),this.initializeGlobalListeners()}async getOrCreateDesktopHandler(){if(!this.desktopHandler){let{DesktopHandler:e}=await import("./DesktopHandler-BOXAW4XX.js");this.desktopHandler=new e(this.handlerDependencies),this.devLog("DesktopHandler lazy loaded")}return this.desktopHandler}async getOrCreateTouchHandler(){if(!this.touchDeviceHandler){let{TouchDeviceHandler:e}=await import("./TouchDeviceHandler-JWBQ2YOV.js");this.touchDeviceHandler=new e(this.handlerDependencies),this.devLog("TouchDeviceHandler lazy loaded")}return this.touchDeviceHandler}static initialize(e){return this.isInitiated||(n.manager=new n(e)),n.manager}static get isInitiated(){return!!n.manager}static get instance(){return this.initialize()}generateId(){return`foresight-${++this.idCounter}`}get isUsingDesktopHandler(){return this.currentDeviceStrategy==="mouse"||this.currentDeviceStrategy==="pen"}addEventListener(e,t,i){this.eventEmitter.addEventListener(e,t,i)}removeEventListener(e,t){this.eventEmitter.removeEventListener(e,t)}hasListeners(e){return this.eventEmitter.hasListeners(e)}get getManagerData(){let e=this.desktopHandler?.loadedPredictors,t=this.touchDeviceHandler?.loadedPredictors;return{registeredElements:this.elements,globalSettings:this._globalSettings,globalCallbackHits:this._globalCallbackHits,eventListeners:this.eventEmitter.getEventListeners(),currentDeviceStrategy:this.currentDeviceStrategy,activeElementCount:this.activeElementCount,loadedModules:{desktopHandler:this.desktopHandler!==null,touchHandler:this.touchDeviceHandler!==null,predictors:{mouse:e?.mouse??!1,tab:e?.tab??!1,scroll:e?.scroll??!1,viewport:t?.viewport??!1,touchStart:t?.touchStart??!1}}}}get registeredElements(){return this.elements}register(e){let{element:t,...i}=e;return t instanceof NodeList?Array.from(t,r=>this.registerElement({...i,element:r})):this.registerElement({...i,element:t})}registerElement(e){let{isTouchDevice:t,isLimitedConnection:i,shouldRegister:r}=k();if(!r)return{isLimitedConnection:i,isTouchDevice:t,isRegistered:!1,unregister:()=>{},elementData:null};let o=this.elements.get(e.element);if(o)return o.registerCount++,{isLimitedConnection:i,isTouchDevice:t,isRegistered:!1,unregister:()=>{},elementData:null};this.isSetup||this.initializeGlobalListeners();let s=R(e,this.generateId(),this._globalSettings.defaultHitSlop);return this.elements.set(e.element,s),this.activeElementCount++,this.updateCheckableStatus(s),this.currentlyActiveHandler?.observeElement(e.element),this.eventEmitter.emit({type:"elementRegistered",timestamp:Date.now(),elementData:s}),{isTouchDevice:t,isLimitedConnection:i,isRegistered:!0,unregister:()=>{this.unregister(e.element)},elementData:s}}unregister(e,t){e instanceof NodeList?e.forEach(i=>this.unregisterElement(i,t)):this.unregisterElement(e,t)}unregisterElement(e,t){let i=this.elements.get(e);if(!i)return;this.clearReactivateTimeout(i),this.currentlyActiveHandler?.unobserveElement(e),this.elements.delete(e),this.checkableElements.delete(i),i.callbackInfo.isCallbackActive&&this.activeElementCount--;let r=this.elements.size===0&&this.isSetup;r&&(this.devLog("All elements unregistered, removing global listeners"),this.removeGlobalListeners()),this.eventEmitter.emit({type:"elementUnregistered",elementData:i,timestamp:Date.now(),unregisterReason:t??"by user",wasLastRegisteredElement:r})}reactivate(e){e instanceof NodeList?e.forEach(t=>this.reactivateElement(t)):this.reactivateElement(e)}reactivateElement(e){let t=this.elements.get(e);t&&(this.isSetup||this.initializeGlobalListeners(),this.clearReactivateTimeout(t),t.callbackInfo.isRunningCallback||(t.callbackInfo.isCallbackActive=!0,this.activeElementCount++,this.updateCheckableStatus(t),this.currentlyActiveHandler?.observeElement(e),this.eventEmitter.emit({type:"elementReactivated",elementData:t,timestamp:Date.now()})))}clearReactivateTimeout(e){clearTimeout(e.callbackInfo.reactivateTimeoutId),e.callbackInfo.reactivateTimeoutId=void 0}updateCheckableStatus(e){e.isIntersectingWithViewport&&e.callbackInfo.isCallbackActive&&!e.callbackInfo.isRunningCallback?this.checkableElements.add(e):this.checkableElements.delete(e)}callCallback(e,t){e.callbackInfo.isRunningCallback||!e.callbackInfo.isCallbackActive||(this.markElementAsRunning(e),this.executeCallbackAsync(e,t))}markElementAsRunning(e){e.callbackInfo.callbackFiredCount++,e.callbackInfo.lastCallbackInvokedAt=Date.now(),e.callbackInfo.isRunningCallback=!0,this.clearReactivateTimeout(e),this.checkableElements.delete(e)}async executeCallbackAsync(e,t){this.updateHitCounters(t),this.eventEmitter.emit({type:"callbackInvoked",timestamp:Date.now(),elementData:e,hitType:t});let i=performance.now(),r,o=null;try{await e.callback(e),r="success"}catch(s){o=s instanceof Error?s.message:String(s),r="error",console.error(`Error in callback for element ${e.name}:`,s)}this.finalizeCallback(e,t,i,r,o)}finalizeCallback(e,t,i,r,o){e.callbackInfo.lastCallbackCompletedAt=Date.now(),e.callbackInfo.isRunningCallback=!1,e.callbackInfo.isCallbackActive=!1,this.activeElementCount--,this.currentlyActiveHandler?.unobserveElement(e.element),e.callbackInfo.reactivateAfter!==1/0&&(e.callbackInfo.reactivateTimeoutId=setTimeout(()=>{this.reactivate(e.element)},e.callbackInfo.reactivateAfter));let s=this.activeElementCount===0;s&&(this.devLog("All elements unactivated, removing global listeners"),this.removeGlobalListeners()),this.eventEmitter.emit({type:"callbackCompleted",timestamp:Date.now(),elementData:e,hitType:t,elapsed:e.callbackInfo.lastCallbackRuntime=performance.now()-i,status:e.callbackInfo.lastCallbackStatus=r,errorMessage:e.callbackInfo.lastCallbackErrorMessage=o,wasLastActiveElement:s})}updateHitCounters(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]++;break;case"touch":this._globalCallbackHits.touch++;break;case"viewport":this._globalCallbackHits.viewport++;break;default:}this._globalCallbackHits.total++}async setDeviceStrategy(e){let t=this.currentDeviceStrategy;t!==e&&this.devLog(`Switching device strategy from ${t} to ${e}`),this.currentlyActiveHandler?.disconnect(),this.currentlyActiveHandler=e==="mouse"||e==="pen"?await this.getOrCreateDesktopHandler():await this.getOrCreateTouchHandler(),this.currentlyActiveHandler.connect()}initializeGlobalListeners(){this.isSetup||typeof document>"u"||(this.devLog("Initializing global listeners (pointermove, MutationObserver)"),this.setDeviceStrategy(this.currentDeviceStrategy),document.addEventListener("pointermove",this.handlePointerMove),this.domObserver=new MutationObserver(this.handleDomMutations),this.domObserver.observe(document.documentElement,{childList:!0,subtree:!0,attributes:!1}),this.isSetup=!0)}removeGlobalListeners(){typeof document>"u"||(this.isSetup=!1,this.domObserver?.disconnect(),this.domObserver=null,document.removeEventListener("pointermove",this.handlePointerMove),this.currentlyActiveHandler?.disconnect(),this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.pendingPointerEvent=null)}alterGlobalSettings(e){let t=D(this._globalSettings,e);t.positionHistorySizeChanged&&this.desktopHandler&&this.desktopHandler.trajectoryPositions.positions.resize(this._globalSettings.positionHistorySize),t.scrollPredictionChanged&&this.isUsingDesktopHandler&&this.desktopHandler&&(this._globalSettings.enableScrollPrediction?this.desktopHandler.connectScrollPredictor():this.desktopHandler.disconnectScrollPredictor()),t.tabPredictionChanged&&this.isUsingDesktopHandler&&this.desktopHandler&&(this._globalSettings.enableTabPrediction?this.desktopHandler.connectTabPredictor():this.desktopHandler.disconnectTabPredictor()),t.hitSlopChanged&&this.forceUpdateAllElementBounds(),t.touchStrategyChanged&&!this.isUsingDesktopHandler&&this.touchDeviceHandler&&this.touchDeviceHandler.setTouchPredictor(),t.changedSettings.length>0&&this.eventEmitter.emit({type:"managerSettingsChanged",timestamp:Date.now(),managerData:this.getManagerData,updatedSettings:t.changedSettings})}forceUpdateAllElementBounds(){for(let e of this.elements.values())e.isIntersectingWithViewport&&this.forceUpdateElementBounds(e)}forceUpdateElementBounds(e){let t=e.element.getBoundingClientRect(),i=v(t,e.elementBounds.hitSlop);if(!p(i,e.elementBounds.expandedRect)){let r={...e,elementBounds:{...e.elementBounds,originalRect:t,expandedRect:i}};this.elements.set(e.element,r),this.eventEmitter.emit({type:"elementDataUpdated",elementData:r,updatedProps:["bounds"]})}}devLog(e){this._globalSettings.enableManagerLogging&&console.log(`%c\u{1F6E0}\uFE0F ForesightManager: ${e}`,"color: #16a34a; font-weight: bold;")}};export{u as ForesightManager};
|
package/package.json
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "js.foresight",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "Predicts where users will click based on mouse movement, keyboard navigation, and scroll behavior. Includes touch device support. Triggers callbacks before interactions happen to enable prefetching and faster UI responses. Works with any framework.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsup --sourcemap",
|
|
9
|
+
"build:prod": "tsup",
|
|
10
|
+
"dev": "tsup --sourcemap --watch",
|
|
11
|
+
"test": "vitest",
|
|
12
|
+
"test:watch": "vitest --watch",
|
|
13
|
+
"test:ui": "vitest --ui",
|
|
14
|
+
"test:coverage": "vitest --coverage",
|
|
15
|
+
"test:run": "vitest run",
|
|
16
|
+
"prepublishOnly": "pnpm test:run && pnpm build:prod"
|
|
17
|
+
},
|
|
6
18
|
"main": "./dist/index.js",
|
|
7
19
|
"module": "./dist/index.js",
|
|
8
20
|
"types": "./dist/index.d.ts",
|
|
@@ -58,16 +70,6 @@
|
|
|
58
70
|
},
|
|
59
71
|
"dependencies": {
|
|
60
72
|
"position-observer": "^1.0.3",
|
|
61
|
-
"tabbable": "^6.
|
|
62
|
-
},
|
|
63
|
-
"scripts": {
|
|
64
|
-
"build": "tsup --sourcemap",
|
|
65
|
-
"build:prod": "tsup",
|
|
66
|
-
"dev": "tsup --sourcemap --watch",
|
|
67
|
-
"test": "vitest",
|
|
68
|
-
"test:watch": "vitest --watch",
|
|
69
|
-
"test:ui": "vitest --ui",
|
|
70
|
-
"test:coverage": "vitest --coverage",
|
|
71
|
-
"test:run": "vitest run"
|
|
73
|
+
"tabbable": "^6.4.0"
|
|
72
74
|
}
|
|
73
|
-
}
|
|
75
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 Bart Spaans
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|