js.foresight 3.5.0 → 4.0.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.
Files changed (38) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +14 -46
  3. package/dist/BaseForesightModule-BxWJ6mzK.mjs +2 -0
  4. package/dist/BaseForesightModule-BxWJ6mzK.mjs.map +1 -0
  5. package/dist/DesktopHandler-BgCQieJ9.mjs +2 -0
  6. package/dist/DesktopHandler-BgCQieJ9.mjs.map +1 -0
  7. package/dist/ElementObservingModule-OqTcnagr.mjs +2 -0
  8. package/dist/ElementObservingModule-OqTcnagr.mjs.map +1 -0
  9. package/dist/ScrollPredictor-r4YnqcfQ.mjs +2 -0
  10. package/dist/ScrollPredictor-r4YnqcfQ.mjs.map +1 -0
  11. package/dist/TabPredictor-CohoaRM5.mjs +2 -0
  12. package/dist/TabPredictor-CohoaRM5.mjs.map +1 -0
  13. package/dist/TouchDeviceHandler-CKhsCqLJ.mjs +2 -0
  14. package/dist/TouchDeviceHandler-CKhsCqLJ.mjs.map +1 -0
  15. package/dist/TouchStartPredictor-Dqk28kjI.mjs +2 -0
  16. package/dist/TouchStartPredictor-Dqk28kjI.mjs.map +1 -0
  17. package/dist/ViewportPredictor-BOtudr8f.mjs +2 -0
  18. package/dist/ViewportPredictor-BOtudr8f.mjs.map +1 -0
  19. package/dist/index.d.mts +556 -0
  20. package/dist/index.d.mts.map +1 -0
  21. package/dist/index.mjs +2 -0
  22. package/dist/index.mjs.map +1 -0
  23. package/dist/lineSegmentIntersectsRect-x9nicliA.mjs +2 -0
  24. package/dist/lineSegmentIntersectsRect-x9nicliA.mjs.map +1 -0
  25. package/dist/rectAndHitSlop-T7Z3PZlb.mjs +2 -0
  26. package/dist/rectAndHitSlop-T7Z3PZlb.mjs.map +1 -0
  27. package/package.json +27 -28
  28. package/dist/DesktopHandler-BOXAW4XX.js +0 -1
  29. package/dist/ScrollPredictor-Y7NELMBI.js +0 -1
  30. package/dist/TabPredictor-HA2SV3CY.js +0 -1
  31. package/dist/TouchDeviceHandler-JWBQ2YOV.js +0 -1
  32. package/dist/TouchStartPredictor-ZH3KJG2C.js +0 -1
  33. package/dist/ViewportPredictor-H3GLDETY.js +0 -1
  34. package/dist/chunk-44N4MCQB.js +0 -1
  35. package/dist/chunk-AODZNE3S.js +0 -1
  36. package/dist/chunk-PAYO6NXN.js +0 -1
  37. package/dist/index.d.ts +0 -531
  38. package/dist/index.js +0 -1
package/dist/index.d.ts DELETED
@@ -1,531 +0,0 @@
1
- declare class CircularBuffer<T> {
2
- private buffer;
3
- private head;
4
- private count;
5
- private capacity;
6
- constructor(capacity: number);
7
- add(item: T): void;
8
- getFirst(): T | undefined;
9
- getLast(): T | undefined;
10
- getFirstLast(): [T | undefined, T | undefined];
11
- resize(newCapacity: number): void;
12
- private getAllItems;
13
- clear(): void;
14
- get length(): number;
15
- get size(): number;
16
- get isFull(): boolean;
17
- get isEmpty(): boolean;
18
- }
19
-
20
- type Rect = {
21
- top: number;
22
- left: number;
23
- right: number;
24
- bottom: number;
25
- };
26
- /**
27
- * A callback function that is executed when a foresight interaction
28
- * (e.g., hover, trajectory hit) occurs on a registered element.
29
- */
30
- type ForesightCallback = (element: ForesightElementData) => void;
31
- /**
32
- * Represents the HTML element that is being tracked by the ForesightManager.
33
- * This is typically a standard DOM `Element`.
34
- */
35
- type ForesightElement = Element;
36
- /**
37
- * Represents a mouse position captured at a specific point in time.
38
- * Used for tracking mouse movement history.
39
- */
40
- type MousePosition = {
41
- /** The (x, y) coordinates of the mouse. */
42
- point: Point;
43
- /** The timestamp (e.g., from `performance.now()`) when this position was recorded. */
44
- time: number;
45
- };
46
- type Point = {
47
- x: number;
48
- y: number;
49
- };
50
- type TrajectoryPositions = {
51
- positions: CircularBuffer<MousePosition>;
52
- currentPoint: Point;
53
- predictedPoint: Point;
54
- };
55
- /**
56
- * Internal type representing the calculated boundaries of a foresight element,
57
- * including its original dimensions and the expanded hit area.
58
- */
59
- type ElementBounds = {
60
- /** The expanded rectangle, including hitSlop, used for interaction detection. */
61
- expandedRect: Readonly<Rect>;
62
- /** The original bounding rectangle of the element, as returned by `getBoundingClientRect()`. */
63
- originalRect: DOMRectReadOnly;
64
- /** The hit slop values applied to this element. */
65
- hitSlop: Exclude<HitSlop, number>;
66
- };
67
- type ForesightRegisterResult = {
68
- /** Whether the current device is a touch device. This is important as ForesightJS only works based on cursor movement. If the user is using a touch device you should handle prefetching differently
69
- * @deprecated As of version 3.3, ForesightJS handles touch devices internally with dedicated touch strategies
70
- */
71
- isTouchDevice: boolean;
72
- /** Whether the user has connection limitations (network slower than minimum connection type (default: 3g) or data saver enabled) that should prevent prefetching */
73
- isLimitedConnection: boolean;
74
- /** Whether ForesightJS will actively track this element. False if touch device or limited connection, true otherwise */
75
- isRegistered: boolean;
76
- /** Function to unregister the element
77
- * @deprecated no longer need to call this manually, you can call Foresightmanager.instance.unregister if needed
78
- */
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;
82
- };
83
- /**
84
- * Represents the data associated with a registered foresight element.
85
- */
86
- type ForesightElementData = Required<Pick<ForesightRegisterOptions, "callback" | "name" | "meta">> & {
87
- /** Unique identifier assigned during registration */
88
- id: string;
89
- /** The boundary information for the element. */
90
- elementBounds: ElementBounds;
91
- /**
92
- * Is the element intersecting with the viewport, usefull to track which element we should observe or not
93
- * Can be @undefined in the split second the element is registering
94
- */
95
- isIntersectingWithViewport: boolean;
96
- /**
97
- * The element you registered
98
- */
99
- element: ForesightElement;
100
- /**
101
- * For debugging, check if you are registering the same element multiple times.
102
- */
103
- registerCount: number;
104
- /**
105
- * Callbackinfo for debugging purposes
106
- */
107
- callbackInfo: ElementCallbackInfo;
108
- };
109
- type ElementCallbackInfo = {
110
- /**
111
- * Number of times the callback has been fired for this element
112
- */
113
- callbackFiredCount: number;
114
- /**
115
- * Timestamp when the callback was last fired
116
- */
117
- lastCallbackInvokedAt: number | undefined;
118
- /**
119
- * Timestamp when the last callback was finished
120
- */
121
- lastCallbackCompletedAt: number | undefined;
122
- /**
123
- * Time in milliseconds it took for the last callback to go from invoked to complete.
124
- */
125
- lastCallbackRuntime: number | undefined;
126
- /**
127
- * Status of the last ran callback
128
- */
129
- lastCallbackStatus: callbackStatus;
130
- /**
131
- * Last callback error message
132
- */
133
- lastCallbackErrorMessage: string | undefined | null;
134
- /**
135
- * Time in milliseconds after which the callback can be fired again
136
- */
137
- reactivateAfter: number;
138
- /**
139
- * Whether the callback is currently active (within stale time period)
140
- */
141
- isCallbackActive: boolean;
142
- /**
143
- * If the element is currently running its callback
144
- */
145
- isRunningCallback: boolean;
146
- /**
147
- * Timeout ID for the scheduled reactivation, if any
148
- */
149
- reactivateTimeoutId?: ReturnType<typeof setTimeout>;
150
- };
151
- type callbackStatus = "error" | "success" | undefined;
152
- type MouseCallbackCounts = {
153
- hover: number;
154
- trajectory: number;
155
- };
156
- type TabCallbackCounts = {
157
- reverse: number;
158
- forwards: number;
159
- };
160
- type ScrollDirection = "down" | "up" | "left" | "right" | "none";
161
- type ScrollCallbackCounts = Record<`${Exclude<ScrollDirection, "none">}`, number>;
162
- type CallbackHits = {
163
- total: number;
164
- mouse: MouseCallbackCounts;
165
- tab: TabCallbackCounts;
166
- scroll: ScrollCallbackCounts;
167
- touch: number;
168
- viewport: number;
169
- };
170
- type CallbackHitType = {
171
- kind: "mouse";
172
- subType: keyof MouseCallbackCounts;
173
- } | {
174
- kind: "tab";
175
- subType: keyof TabCallbackCounts;
176
- } | {
177
- kind: "scroll";
178
- subType: keyof ScrollCallbackCounts;
179
- } | {
180
- kind: "touch";
181
- subType?: string;
182
- } | {
183
- kind: "viewport";
184
- subType?: string;
185
- };
186
- /**
187
- * Snapshot of the current ForesightManager state
188
- */
189
- type ForesightManagerData = {
190
- registeredElements: ReadonlyMap<ForesightElement, ForesightElementData>;
191
- globalSettings: Readonly<ForesightManagerSettings>;
192
- globalCallbackHits: Readonly<CallbackHits>;
193
- eventListeners: ReadonlyMap<keyof ForesightEventMap, ForesightEventListener[]>;
194
- currentDeviceStrategy: CurrentDeviceStrategy;
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
- };
207
- };
208
- type TouchDeviceStrategy = "none" | "viewport" | "onTouchStart";
209
- type MinimumConnectionType = "slow-2g" | "2g" | "3g" | "4g";
210
- type BaseForesightManagerSettings = {
211
- /**
212
- * Number of mouse positions to keep in history for trajectory calculation.
213
- * A higher number might lead to smoother but slightly delayed predictions.
214
- *
215
- *
216
- * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
217
- *
218
- *
219
- * **This value is clamped between 2 and 30.**
220
- * @default 8
221
- */
222
- positionHistorySize: number;
223
- /**
224
- *
225
- * @deprecated will be removed from v4.0
226
- * ForesightJS now have its stand-alone devtools library with the debugger built-in
227
- * @link https://github.com/spaansba/ForesightJS-DevTools
228
- */
229
- debug: boolean;
230
- /**
231
- *
232
- * Logs basic information about the ForesightManager and its handlers that doesn't have a dedicated event.
233
- *
234
- * Mostly used by the maintainers of ForesightJS to debug the manager. But might be useful for implementers aswell.
235
- *
236
- * This is not the same as logging events, this can be done with the actual developer tools.
237
- * @link https://foresightjs.com/docs/debugging/devtools
238
- */
239
- enableManagerLogging: boolean;
240
- /**
241
- * How far ahead (in milliseconds) to predict the mouse trajectory.
242
- * A larger value means the prediction extends further into the future. (meaning it will trigger callbacks sooner)
243
- *
244
- * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
245
- *
246
- * **This value is clamped between 10 and 200.**
247
- * @default 120
248
- */
249
- trajectoryPredictionTime: number;
250
- /**
251
- * Whether to enable mouse trajectory prediction.
252
- * If false, only direct hover/interaction is considered.
253
- * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
254
- * @default true
255
- */
256
- enableMousePrediction: boolean;
257
- /**
258
- * Toggles whether keyboard prediction is on
259
- *
260
- * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
261
- * @default true
262
- */
263
- enableTabPrediction: boolean;
264
- /**
265
- * Sets the pixel distance to check from the mouse position in the scroll direction.
266
- *
267
- * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
268
- *
269
- * **This value is clamped between 30 and 300.**
270
- * @default 150
271
- */
272
- scrollMargin: number;
273
- /**
274
- * Toggles whether scroll prediction is on
275
- * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
276
- * @default true
277
- */
278
- enableScrollPrediction: boolean;
279
- /**
280
- * Tab stops away from an element to trigger callback. Only works when @argument enableTabPrediction is true
281
- *
282
- * **This value is clamped between 0 and 20.**
283
- * @default 2
284
- */
285
- tabOffset: number;
286
- /**
287
- * The prefetch strategy used for touch devices.
288
- * - `viewport`: Prefetching is done based on the viewport, meaning elements in the viewport are preloaded.
289
- * - `onTouchStart`: Prefetching is done when the user touches the element
290
- * @default onTouchStart
291
- */
292
- touchDeviceStrategy: TouchDeviceStrategy;
293
- /**
294
- * Network effective connection types that should be considered as limited connections.
295
- * When the user's network matches any of these types, ForesightJS will disable prefetching
296
- * to avoid consuming data on slow or expensive connections.
297
- *
298
- * @link https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/effectiveType
299
- * @default 3g
300
- */
301
- minimumConnectionType: MinimumConnectionType;
302
- };
303
- type CurrentDeviceStrategy = "mouse" | "touch" | "pen";
304
- /**
305
- * Configuration options for the ForesightManager
306
- * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
307
- */
308
- type ForesightManagerSettings = BaseForesightManagerSettings & {
309
- defaultHitSlop: Exclude<HitSlop, number>;
310
- };
311
- /**
312
- * Update options for the ForesightManager
313
- * @link https://foresightjs.com/docs/getting_started/config#available-global-settings
314
- */
315
- type UpdateForsightManagerSettings = BaseForesightManagerSettings & {
316
- defaultHitSlop: HitSlop;
317
- };
318
- /**
319
- * Type used to register elements to the foresight manager
320
- */
321
- type ForesightRegisterOptions = ForesightRegisterOptionsWithoutElement & {
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 = {
335
- callback: ForesightCallback;
336
- hitSlop?: HitSlop;
337
- /**
338
- * @deprecated will be removed in V4.0
339
- */
340
- unregisterOnCallback?: boolean;
341
- name?: string;
342
- /**
343
- * If set by user, stores additional information about the registered element
344
- */
345
- meta?: Record<string, unknown>;
346
- /**
347
- * Time in milliseconds after which the callback can be fired again and we reactivate the element.
348
- * Set to Infinity to prevent callback from firing again after first execution.
349
- * @default Infinity
350
- */
351
- reactivateAfter?: number;
352
- };
353
- /**
354
- * Fully invisible "slop" around the element.
355
- * Basically increases the hover hitbox
356
- */
357
- type HitSlop = Rect | number;
358
- interface ForesightEventMap {
359
- elementRegistered: ElementRegisteredEvent;
360
- elementReactivated: ElementReactivatedEvent;
361
- elementUnregistered: ElementUnregisteredEvent;
362
- elementDataUpdated: ElementDataUpdatedEvent;
363
- callbackInvoked: CallbackInvokedEvent;
364
- callbackCompleted: CallbackCompletedEvent;
365
- mouseTrajectoryUpdate: MouseTrajectoryUpdateEvent;
366
- scrollTrajectoryUpdate: ScrollTrajectoryUpdateEvent;
367
- managerSettingsChanged: ManagerSettingsChangedEvent;
368
- deviceStrategyChanged: DeviceStrategyChangedEvent;
369
- }
370
- type ForesightEvent = "elementRegistered" | "elementReactivated" | "elementUnregistered" | "elementDataUpdated" | "callbackInvoked" | "callbackCompleted" | "mouseTrajectoryUpdate" | "scrollTrajectoryUpdate" | "managerSettingsChanged" | "deviceStrategyChanged";
371
- interface DeviceStrategyChangedEvent extends ForesightBaseEvent {
372
- type: "deviceStrategyChanged";
373
- newStrategy: CurrentDeviceStrategy;
374
- oldStrategy: CurrentDeviceStrategy;
375
- }
376
- interface ElementRegisteredEvent extends ForesightBaseEvent {
377
- type: "elementRegistered";
378
- elementData: ForesightElementData;
379
- }
380
- interface ElementReactivatedEvent extends ForesightBaseEvent {
381
- type: "elementReactivated";
382
- elementData: ForesightElementData;
383
- }
384
- interface ElementUnregisteredEvent extends ForesightBaseEvent {
385
- type: "elementUnregistered";
386
- elementData: ForesightElementData;
387
- unregisterReason: ElementUnregisteredReason;
388
- wasLastRegisteredElement: boolean;
389
- }
390
- /**
391
- * The reason an element was unregistered from ForesightManager's tracking.
392
- * - `callbackHit`: The element was automatically unregistered after its callback fired.
393
- * - `disconnected`: The element was automatically unregistered because it was removed from the DOM.
394
- * - `apiCall`: The developer manually called the `unregister()` function for the element.
395
- * - `devtools`: When clicking the trash icon in the devtools element tab
396
- * - any other string
397
- */
398
- type ElementUnregisteredReason = "disconnected" | "apiCall" | "devtools" | (string & {});
399
- interface ElementDataUpdatedEvent extends Omit<ForesightBaseEvent, "timestamp"> {
400
- type: "elementDataUpdated";
401
- elementData: ForesightElementData;
402
- updatedProps: UpdatedDataPropertyNames[];
403
- }
404
- type UpdatedDataPropertyNames = "bounds" | "visibility";
405
- interface CallbackInvokedEvent extends ForesightBaseEvent {
406
- type: "callbackInvoked";
407
- elementData: ForesightElementData;
408
- hitType: CallbackHitType;
409
- }
410
- interface CallbackCompletedEventBase extends ForesightBaseEvent {
411
- type: "callbackCompleted";
412
- elementData: ForesightElementData;
413
- hitType: CallbackHitType;
414
- elapsed: number;
415
- wasLastActiveElement: boolean;
416
- }
417
- type CallbackCompletedEvent = CallbackCompletedEventBase & {
418
- status: callbackStatus;
419
- errorMessage: string | undefined | null;
420
- };
421
- interface MouseTrajectoryUpdateEvent extends Omit<ForesightBaseEvent, "timestamp"> {
422
- type: "mouseTrajectoryUpdate";
423
- trajectoryPositions: TrajectoryPositions;
424
- predictionEnabled: boolean;
425
- }
426
- interface ScrollTrajectoryUpdateEvent extends Omit<ForesightBaseEvent, "timestamp"> {
427
- type: "scrollTrajectoryUpdate";
428
- currentPoint: Point;
429
- predictedPoint: Point;
430
- scrollDirection: ScrollDirection;
431
- }
432
- interface ManagerSettingsChangedEvent extends ForesightBaseEvent {
433
- type: "managerSettingsChanged";
434
- managerData: ForesightManagerData;
435
- updatedSettings: UpdatedManagerSetting[];
436
- }
437
- type UpdatedManagerSetting = {
438
- [K in keyof ForesightManagerSettings]: {
439
- setting: K;
440
- newValue: ForesightManagerSettings[K];
441
- oldValue: ForesightManagerSettings[K];
442
- };
443
- }[keyof ForesightManagerSettings];
444
- type ForesightEventListener<K extends ForesightEvent = ForesightEvent> = (event: ForesightEventMap[K]) => void;
445
- interface ForesightBaseEvent {
446
- type: ForesightEvent;
447
- timestamp: number;
448
- }
449
-
450
- /**
451
- * Manages the prediction of user intent based on mouse trajectory and element interactions.
452
- *
453
- * ForesightManager is a singleton class responsible for:
454
- * - Registering HTML elements to monitor.
455
- * - Tracking mouse movements and predicting future cursor positions.
456
- * - Detecting when a predicted trajectory intersects with a registered element's bounds.
457
- * - Invoking callbacks associated with elements upon predicted or actual interaction.
458
- * - Optionally unregistering elements after their callback is triggered.
459
- * - Handling global settings for prediction behavior (e.g., history size, prediction time).
460
- * - Automatically updating element bounds on resize using {@link ResizeObserver}.
461
- * - Automatically unregistering elements removed from the DOM using {@link MutationObserver}.
462
- * - Detecting broader layout shifts via {@link MutationObserver} to update element positions.
463
- *
464
- * It should be initialized once using {@link ForesightManager.initialize} and then
465
- * accessed via the static getter {@link ForesightManager.instance}.
466
- */
467
- declare class ForesightManager {
468
- private static manager;
469
- private elements;
470
- private checkableElements;
471
- private idCounter;
472
- private activeElementCount;
473
- private desktopHandler;
474
- private touchDeviceHandler;
475
- private currentlyActiveHandler;
476
- private handlerDependencies;
477
- private isSetup;
478
- private pendingPointerEvent;
479
- private rafId;
480
- private domObserver;
481
- private currentDeviceStrategy;
482
- private eventEmitter;
483
- private _globalCallbackHits;
484
- private _globalSettings;
485
- private constructor();
486
- private getOrCreateDesktopHandler;
487
- private getOrCreateTouchHandler;
488
- static initialize(props?: Partial<UpdateForsightManagerSettings>): ForesightManager;
489
- static get isInitiated(): Readonly<boolean>;
490
- static get instance(): ForesightManager;
491
- private generateId;
492
- private get isUsingDesktopHandler();
493
- addEventListener<K extends ForesightEvent>(eventType: K, listener: ForesightEventListener<K>, options?: {
494
- signal?: AbortSignal;
495
- }): void;
496
- removeEventListener<K extends ForesightEvent>(eventType: K, listener: ForesightEventListener<K>): void;
497
- hasListeners<K extends ForesightEvent>(eventType: K): boolean;
498
- get getManagerData(): Readonly<ForesightManagerData>;
499
- get registeredElements(): ReadonlyMap<ForesightElement, ForesightElementData>;
500
- register(options: ForesightRegisterNodeListOptions): ForesightRegisterResult[];
501
- register(options: ForesightRegisterOptions): ForesightRegisterResult;
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;
507
- private clearReactivateTimeout;
508
- updateCheckableStatus(elementData: ForesightElementData): void;
509
- private callCallback;
510
- private markElementAsRunning;
511
- private executeCallbackAsync;
512
- private finalizeCallback;
513
- private updateHitCounters;
514
- private setDeviceStrategy;
515
- private handlePointerMove;
516
- private initializeGlobalListeners;
517
- private removeGlobalListeners;
518
- private handleDomMutations;
519
- alterGlobalSettings(props?: Partial<UpdateForsightManagerSettings>): void;
520
- private forceUpdateAllElementBounds;
521
- /**
522
- * ONLY use this function when you want to change the rect bounds via code, if the rects are changing because of updates in the DOM do not use this function.
523
- * We need an observer for that
524
- */
525
- private forceUpdateElementBounds;
526
- private devLog;
527
- }
528
-
529
- type HasListenersFunction = <K extends ForesightEvent>(eventType: K) => boolean;
530
-
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 DELETED
@@ -1 +0,0 @@
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};