@croct/sdk 0.21.1 → 0.22.1
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/constants.cjs +1 -1
- package/constants.d.cts +2 -2
- package/constants.d.ts +2 -2
- package/constants.js +1 -1
- package/container.cjs +15 -1
- package/container.d.cts +3 -0
- package/container.d.ts +3 -0
- package/container.js +15 -1
- package/facade/evaluatorFacade.cjs +4 -4
- package/facade/evaluatorFacade.d.cts +2 -1
- package/facade/evaluatorFacade.d.ts +2 -1
- package/facade/evaluatorFacade.js +4 -4
- package/facade/sdkFacade.cjs +8 -2
- package/facade/sdkFacade.js +8 -2
- package/interactionMonitor.cjs +181 -0
- package/interactionMonitor.d.cts +64 -0
- package/interactionMonitor.d.ts +64 -0
- package/interactionMonitor.js +158 -0
- package/package.json +1 -1
- package/schema/eventSchemas.cjs +46 -0
- package/schema/eventSchemas.d.cts +3 -1
- package/schema/eventSchemas.d.ts +3 -1
- package/schema/eventSchemas.js +44 -0
- package/schema/index.d.cts +1 -1
- package/schema/index.d.ts +1 -1
- package/sdk.cjs +3 -0
- package/sdk.d.cts +1 -0
- package/sdk.d.ts +1 -0
- package/sdk.js +3 -0
- package/tracker.cjs +9 -1
- package/tracker.d.cts +3 -0
- package/tracker.d.ts +3 -0
- package/tracker.js +9 -1
- package/trackingEvents.cjs +7 -0
- package/trackingEvents.d.cts +27 -2
- package/trackingEvents.d.ts +27 -2
- package/trackingEvents.js +6 -0
package/constants.cjs
CHANGED
|
@@ -25,7 +25,7 @@ __export(constants_exports, {
|
|
|
25
25
|
module.exports = __toCommonJS(constants_exports);
|
|
26
26
|
const BASE_ENDPOINT_URL = "https://api.croct.io";
|
|
27
27
|
const MAX_QUERY_LENGTH = parseInt("<@maxQueryLength@>", 10);
|
|
28
|
-
const VERSION = "0.
|
|
28
|
+
const VERSION = "0.22.1";
|
|
29
29
|
const CLIENT_LIBRARY = `Croct SDK JS v${VERSION}`;
|
|
30
30
|
// Annotate the CommonJS export names for ESM import in node:
|
|
31
31
|
0 && (module.exports = {
|
package/constants.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
declare const BASE_ENDPOINT_URL = "https://api.croct.io";
|
|
2
2
|
declare const MAX_QUERY_LENGTH: number;
|
|
3
|
-
declare const VERSION = "0.
|
|
4
|
-
declare const CLIENT_LIBRARY = "Croct SDK JS v0.
|
|
3
|
+
declare const VERSION = "0.22.1";
|
|
4
|
+
declare const CLIENT_LIBRARY = "Croct SDK JS v0.22.1";
|
|
5
5
|
|
|
6
6
|
export { BASE_ENDPOINT_URL, CLIENT_LIBRARY, MAX_QUERY_LENGTH, VERSION };
|
package/constants.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
declare const BASE_ENDPOINT_URL = "https://api.croct.io";
|
|
2
2
|
declare const MAX_QUERY_LENGTH: number;
|
|
3
|
-
declare const VERSION = "0.
|
|
4
|
-
declare const CLIENT_LIBRARY = "Croct SDK JS v0.
|
|
3
|
+
declare const VERSION = "0.22.1";
|
|
4
|
+
declare const CLIENT_LIBRARY = "Croct SDK JS v0.22.1";
|
|
5
5
|
|
|
6
6
|
export { BASE_ENDPOINT_URL, CLIENT_LIBRARY, MAX_QUERY_LENGTH, VERSION };
|
package/constants.js
CHANGED
package/container.cjs
CHANGED
|
@@ -47,6 +47,12 @@ const _Container = class _Container {
|
|
|
47
47
|
getConfiguration() {
|
|
48
48
|
return this.configuration;
|
|
49
49
|
}
|
|
50
|
+
getTimeZone() {
|
|
51
|
+
if (this.timeZone === void 0) {
|
|
52
|
+
this.timeZone = _Container.detectTimeZone();
|
|
53
|
+
}
|
|
54
|
+
return this.timeZone;
|
|
55
|
+
}
|
|
50
56
|
getEvaluator() {
|
|
51
57
|
if (this.evaluator === void 0) {
|
|
52
58
|
this.evaluator = this.createEvaluator();
|
|
@@ -98,7 +104,8 @@ const _Container = class _Container {
|
|
|
98
104
|
inactivityRetryPolicy: new import_retry.ArbitraryPolicy([3e4, 3e4, 12e4, 12e4, 3e5, 3e5, 9e5]),
|
|
99
105
|
logger: this.getLogger("Tracker"),
|
|
100
106
|
channel: this.getBeaconChannel(),
|
|
101
|
-
eventMetadata: this.configuration.eventMetadata
|
|
107
|
+
eventMetadata: this.configuration.eventMetadata,
|
|
108
|
+
timeZone: this.getTimeZone() ?? void 0
|
|
102
109
|
});
|
|
103
110
|
const queue = this.getBeaconQueue();
|
|
104
111
|
queue.addCallback("halfEmpty", tracker.unsuspend);
|
|
@@ -297,6 +304,13 @@ const _Container = class _Container {
|
|
|
297
304
|
delete this.removeTokenSyncListener;
|
|
298
305
|
logger.debug("Container resources released.");
|
|
299
306
|
}
|
|
307
|
+
static detectTimeZone() {
|
|
308
|
+
const { timeZone } = Intl.DateTimeFormat().resolvedOptions();
|
|
309
|
+
if (timeZone === void 0 || timeZone === "Etc/Unknown") {
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
return timeZone;
|
|
313
|
+
}
|
|
300
314
|
};
|
|
301
315
|
_Container.DEFAULT_FETCH_TIMEOUT = 5e3;
|
|
302
316
|
let Container = _Container;
|
package/container.d.cts
CHANGED
|
@@ -61,8 +61,10 @@ declare class Container {
|
|
|
61
61
|
private beaconQueue?;
|
|
62
62
|
private removeTokenSyncListener?;
|
|
63
63
|
private readonly eventManager;
|
|
64
|
+
private timeZone?;
|
|
64
65
|
constructor(configuration: Configuration);
|
|
65
66
|
getConfiguration(): Configuration;
|
|
67
|
+
getTimeZone(): string | null;
|
|
66
68
|
getEvaluator(): Evaluator;
|
|
67
69
|
private createEvaluator;
|
|
68
70
|
getContentFetcher(): ContentFetcher;
|
|
@@ -89,6 +91,7 @@ declare class Container {
|
|
|
89
91
|
private getSessionStorage;
|
|
90
92
|
getEventManager(): EventManager<SdkEventMap>;
|
|
91
93
|
dispose(): Promise<void>;
|
|
94
|
+
private static detectTimeZone;
|
|
92
95
|
}
|
|
93
96
|
|
|
94
97
|
export { type Configuration, Container };
|
package/container.d.ts
CHANGED
|
@@ -61,8 +61,10 @@ declare class Container {
|
|
|
61
61
|
private beaconQueue?;
|
|
62
62
|
private removeTokenSyncListener?;
|
|
63
63
|
private readonly eventManager;
|
|
64
|
+
private timeZone?;
|
|
64
65
|
constructor(configuration: Configuration);
|
|
65
66
|
getConfiguration(): Configuration;
|
|
67
|
+
getTimeZone(): string | null;
|
|
66
68
|
getEvaluator(): Evaluator;
|
|
67
69
|
private createEvaluator;
|
|
68
70
|
getContentFetcher(): ContentFetcher;
|
|
@@ -89,6 +91,7 @@ declare class Container {
|
|
|
89
91
|
private getSessionStorage;
|
|
90
92
|
getEventManager(): EventManager<SdkEventMap>;
|
|
91
93
|
dispose(): Promise<void>;
|
|
94
|
+
private static detectTimeZone;
|
|
92
95
|
}
|
|
93
96
|
|
|
94
97
|
export { type Configuration, Container };
|
package/container.js
CHANGED
|
@@ -25,6 +25,12 @@ const _Container = class _Container {
|
|
|
25
25
|
getConfiguration() {
|
|
26
26
|
return this.configuration;
|
|
27
27
|
}
|
|
28
|
+
getTimeZone() {
|
|
29
|
+
if (this.timeZone === void 0) {
|
|
30
|
+
this.timeZone = _Container.detectTimeZone();
|
|
31
|
+
}
|
|
32
|
+
return this.timeZone;
|
|
33
|
+
}
|
|
28
34
|
getEvaluator() {
|
|
29
35
|
if (this.evaluator === void 0) {
|
|
30
36
|
this.evaluator = this.createEvaluator();
|
|
@@ -76,7 +82,8 @@ const _Container = class _Container {
|
|
|
76
82
|
inactivityRetryPolicy: new ArbitraryPolicy([3e4, 3e4, 12e4, 12e4, 3e5, 3e5, 9e5]),
|
|
77
83
|
logger: this.getLogger("Tracker"),
|
|
78
84
|
channel: this.getBeaconChannel(),
|
|
79
|
-
eventMetadata: this.configuration.eventMetadata
|
|
85
|
+
eventMetadata: this.configuration.eventMetadata,
|
|
86
|
+
timeZone: this.getTimeZone() ?? void 0
|
|
80
87
|
});
|
|
81
88
|
const queue = this.getBeaconQueue();
|
|
82
89
|
queue.addCallback("halfEmpty", tracker.unsuspend);
|
|
@@ -275,6 +282,13 @@ const _Container = class _Container {
|
|
|
275
282
|
delete this.removeTokenSyncListener;
|
|
276
283
|
logger.debug("Container resources released.");
|
|
277
284
|
}
|
|
285
|
+
static detectTimeZone() {
|
|
286
|
+
const { timeZone } = Intl.DateTimeFormat().resolvedOptions();
|
|
287
|
+
if (timeZone === void 0 || timeZone === "Etc/Unknown") {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
return timeZone;
|
|
291
|
+
}
|
|
278
292
|
};
|
|
279
293
|
_Container.DEFAULT_FETCH_TIMEOUT = 5e3;
|
|
280
294
|
let Container = _Container;
|
|
@@ -60,8 +60,9 @@ class MinimalContextFactory {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
class TabContextFactory {
|
|
63
|
-
constructor(tab) {
|
|
63
|
+
constructor(tab, timeZone) {
|
|
64
64
|
this.tab = tab;
|
|
65
|
+
this.timeZone = timeZone;
|
|
65
66
|
}
|
|
66
67
|
createContext(attributes) {
|
|
67
68
|
const url = new URL(this.tab.url);
|
|
@@ -75,9 +76,8 @@ class TabContextFactory {
|
|
|
75
76
|
page.referrer = referrer;
|
|
76
77
|
}
|
|
77
78
|
context.page = page;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
context.timeZone = timeZone;
|
|
79
|
+
if (this.timeZone !== void 0) {
|
|
80
|
+
context.timeZone = this.timeZone;
|
|
81
81
|
}
|
|
82
82
|
if (attributes !== void 0 && Object.keys(attributes).length > 0) {
|
|
83
83
|
context.attributes = attributes;
|
|
@@ -34,7 +34,8 @@ declare class MinimalContextFactory implements ContextFactory {
|
|
|
34
34
|
}
|
|
35
35
|
declare class TabContextFactory implements ContextFactory {
|
|
36
36
|
private readonly tab;
|
|
37
|
-
|
|
37
|
+
private readonly timeZone?;
|
|
38
|
+
constructor(tab: Tab, timeZone?: string);
|
|
38
39
|
createContext(attributes?: JsonObject): EvaluationContext;
|
|
39
40
|
}
|
|
40
41
|
|
|
@@ -34,7 +34,8 @@ declare class MinimalContextFactory implements ContextFactory {
|
|
|
34
34
|
}
|
|
35
35
|
declare class TabContextFactory implements ContextFactory {
|
|
36
36
|
private readonly tab;
|
|
37
|
-
|
|
37
|
+
private readonly timeZone?;
|
|
38
|
+
constructor(tab: Tab, timeZone?: string);
|
|
38
39
|
createContext(attributes?: JsonObject): EvaluationContext;
|
|
39
40
|
}
|
|
40
41
|
|
|
@@ -36,8 +36,9 @@ class MinimalContextFactory {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
class TabContextFactory {
|
|
39
|
-
constructor(tab) {
|
|
39
|
+
constructor(tab, timeZone) {
|
|
40
40
|
this.tab = tab;
|
|
41
|
+
this.timeZone = timeZone;
|
|
41
42
|
}
|
|
42
43
|
createContext(attributes) {
|
|
43
44
|
const url = new URL(this.tab.url);
|
|
@@ -51,9 +52,8 @@ class TabContextFactory {
|
|
|
51
52
|
page.referrer = referrer;
|
|
52
53
|
}
|
|
53
54
|
context.page = page;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
context.timeZone = timeZone;
|
|
55
|
+
if (this.timeZone !== void 0) {
|
|
56
|
+
context.timeZone = this.timeZone;
|
|
57
57
|
}
|
|
58
58
|
if (attributes !== void 0 && Object.keys(attributes).length > 0) {
|
|
59
59
|
context.attributes = attributes;
|
package/facade/sdkFacade.cjs
CHANGED
|
@@ -132,7 +132,10 @@ class SdkFacade {
|
|
|
132
132
|
if (this.evaluatorFacade === void 0) {
|
|
133
133
|
this.evaluatorFacade = new import_evaluatorFacade.EvaluatorFacade({
|
|
134
134
|
evaluator: this.sdk.evaluator,
|
|
135
|
-
contextFactory: new import_evaluatorFacade.TabContextFactory(
|
|
135
|
+
contextFactory: new import_evaluatorFacade.TabContextFactory(
|
|
136
|
+
this.sdk.context.getTab(),
|
|
137
|
+
this.sdk.timeZone ?? void 0
|
|
138
|
+
),
|
|
136
139
|
cidAssigner: this.sdk.cidAssigner,
|
|
137
140
|
userTokenProvider: this.sdk.userTokenStore
|
|
138
141
|
});
|
|
@@ -143,7 +146,10 @@ class SdkFacade {
|
|
|
143
146
|
if (this.contentFetcherFacade === void 0) {
|
|
144
147
|
this.contentFetcherFacade = new import_contentFetcherFacade.ContentFetcherFacade({
|
|
145
148
|
contentFetcher: this.sdk.contentFetcher,
|
|
146
|
-
contextFactory: new import_evaluatorFacade.TabContextFactory(
|
|
149
|
+
contextFactory: new import_evaluatorFacade.TabContextFactory(
|
|
150
|
+
this.sdk.context.getTab(),
|
|
151
|
+
this.sdk.timeZone ?? void 0
|
|
152
|
+
),
|
|
147
153
|
cidAssigner: this.sdk.cidAssigner,
|
|
148
154
|
previewTokenProvider: this.sdk.previewTokenStore,
|
|
149
155
|
userTokenProvider: this.sdk.userTokenStore
|
package/facade/sdkFacade.js
CHANGED
|
@@ -110,7 +110,10 @@ class SdkFacade {
|
|
|
110
110
|
if (this.evaluatorFacade === void 0) {
|
|
111
111
|
this.evaluatorFacade = new EvaluatorFacade({
|
|
112
112
|
evaluator: this.sdk.evaluator,
|
|
113
|
-
contextFactory: new TabContextFactory(
|
|
113
|
+
contextFactory: new TabContextFactory(
|
|
114
|
+
this.sdk.context.getTab(),
|
|
115
|
+
this.sdk.timeZone ?? void 0
|
|
116
|
+
),
|
|
114
117
|
cidAssigner: this.sdk.cidAssigner,
|
|
115
118
|
userTokenProvider: this.sdk.userTokenStore
|
|
116
119
|
});
|
|
@@ -121,7 +124,10 @@ class SdkFacade {
|
|
|
121
124
|
if (this.contentFetcherFacade === void 0) {
|
|
122
125
|
this.contentFetcherFacade = new ContentFetcherFacade({
|
|
123
126
|
contentFetcher: this.sdk.contentFetcher,
|
|
124
|
-
contextFactory: new TabContextFactory(
|
|
127
|
+
contextFactory: new TabContextFactory(
|
|
128
|
+
this.sdk.context.getTab(),
|
|
129
|
+
this.sdk.timeZone ?? void 0
|
|
130
|
+
),
|
|
125
131
|
cidAssigner: this.sdk.cidAssigner,
|
|
126
132
|
previewTokenProvider: this.sdk.previewTokenStore,
|
|
127
133
|
userTokenProvider: this.sdk.userTokenStore
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
var interactionMonitor_exports = {};
|
|
19
|
+
__export(interactionMonitor_exports, {
|
|
20
|
+
InteractionMonitor: () => InteractionMonitor
|
|
21
|
+
});
|
|
22
|
+
module.exports = __toCommonJS(interactionMonitor_exports);
|
|
23
|
+
var import_eventManager = require('./eventManager.cjs');
|
|
24
|
+
class InteractionMonitor {
|
|
25
|
+
constructor(options = {}) {
|
|
26
|
+
this.eventManager = new import_eventManager.SynchronousEventManager();
|
|
27
|
+
this.lastClickTime = 0;
|
|
28
|
+
this.enabled = false;
|
|
29
|
+
this.clickThrottleInterval = options.clickThrottleInterval ?? 500;
|
|
30
|
+
this.scrollDebounceInterval = options.scrollDebounceInterval ?? 250;
|
|
31
|
+
this.handleClick = this.handleClick.bind(this);
|
|
32
|
+
this.handleScroll = this.handleScroll.bind(this);
|
|
33
|
+
}
|
|
34
|
+
addListener(type, listener) {
|
|
35
|
+
this.eventManager.addListener(type, listener);
|
|
36
|
+
}
|
|
37
|
+
removeListener(type, listener) {
|
|
38
|
+
this.eventManager.removeListener(type, listener);
|
|
39
|
+
}
|
|
40
|
+
isEnabled() {
|
|
41
|
+
return this.enabled;
|
|
42
|
+
}
|
|
43
|
+
enable() {
|
|
44
|
+
if (this.enabled) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
this.enabled = true;
|
|
48
|
+
window.addEventListener("click", this.handleClick, true);
|
|
49
|
+
window.addEventListener("scroll", this.handleScroll, true);
|
|
50
|
+
}
|
|
51
|
+
disable() {
|
|
52
|
+
if (!this.enabled) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
this.enabled = false;
|
|
56
|
+
window.removeEventListener("click", this.handleClick, true);
|
|
57
|
+
window.removeEventListener("scroll", this.handleScroll, true);
|
|
58
|
+
this.flushPendingScroll();
|
|
59
|
+
}
|
|
60
|
+
handleClick(event) {
|
|
61
|
+
const currentTime = Date.now();
|
|
62
|
+
if (currentTime - this.lastClickTime < this.clickThrottleInterval) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
this.lastClickTime = currentTime;
|
|
66
|
+
this.eventManager.dispatch("userClicked", {
|
|
67
|
+
type: "userClicked",
|
|
68
|
+
point: {
|
|
69
|
+
x: Math.max(0, Math.round(event.pageX)),
|
|
70
|
+
y: Math.max(0, Math.round(event.pageY))
|
|
71
|
+
},
|
|
72
|
+
surfaceSize: {
|
|
73
|
+
width: document.documentElement.scrollWidth,
|
|
74
|
+
height: document.documentElement.scrollHeight
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
handleScroll() {
|
|
79
|
+
const currentPosition = {
|
|
80
|
+
x: Math.max(0, Math.round(window.scrollX)),
|
|
81
|
+
y: Math.max(0, Math.round(window.scrollY))
|
|
82
|
+
};
|
|
83
|
+
if (this.scrollState === void 0) {
|
|
84
|
+
this.scrollState = {
|
|
85
|
+
start: currentPosition,
|
|
86
|
+
lastPosition: currentPosition
|
|
87
|
+
};
|
|
88
|
+
} else if (this.hasDirectionChanged(this.scrollState, currentPosition)) {
|
|
89
|
+
const turningPoint = this.scrollState.lastPosition;
|
|
90
|
+
this.flushPendingScroll(turningPoint);
|
|
91
|
+
this.scrollState = {
|
|
92
|
+
start: turningPoint,
|
|
93
|
+
lastPosition: currentPosition
|
|
94
|
+
};
|
|
95
|
+
} else {
|
|
96
|
+
this.scrollState.lastPosition = currentPosition;
|
|
97
|
+
}
|
|
98
|
+
if (this.scrollDebounceTimer !== void 0) {
|
|
99
|
+
window.clearTimeout(this.scrollDebounceTimer);
|
|
100
|
+
}
|
|
101
|
+
this.scrollDebounceTimer = window.setTimeout(
|
|
102
|
+
() => this.flushPendingScroll(),
|
|
103
|
+
this.scrollDebounceInterval
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Determines whether the scroll direction has reversed on either axis.
|
|
108
|
+
*
|
|
109
|
+
* It compares two vectors:
|
|
110
|
+
* - The scroll direction: the overall direction from start to the previous position (start -> previous)
|
|
111
|
+
* - The movement direction: the direction of the latest movement (previous -> current)
|
|
112
|
+
*
|
|
113
|
+
* A reversal is detected when these two vectors point in opposite directions on the same axis.
|
|
114
|
+
* Axes with no movement (sign = 0) are ignored, so scrolling that simply stops on one axis
|
|
115
|
+
* without reversing does not trigger a flush.
|
|
116
|
+
*
|
|
117
|
+
* Example of vertical reversal:
|
|
118
|
+
* start = {y: 0}, previous = {y: 500}, current = {y: 400}
|
|
119
|
+
* scrollDirectionY = sign(500 - 0) = +1 (downward)
|
|
120
|
+
* movementDirectionY = sign(400 - 500) = -1 (upward)
|
|
121
|
+
* +1 !== -1 => reversed
|
|
122
|
+
*
|
|
123
|
+
* Example of no reversal (same direction):
|
|
124
|
+
* start = {y: 0}, previous = {y: 200}, current = {y: 500}
|
|
125
|
+
* scrollDirectionY = sign(200 - 0) = +1 (downward)
|
|
126
|
+
* movementDirectionY = sign(500 - 200) = +1 (downward)
|
|
127
|
+
* +1 === +1 => not reversed
|
|
128
|
+
*
|
|
129
|
+
* Example of horizontal stop ignored:
|
|
130
|
+
* start = {x: 0}, previous = {x: 300}, current = {x: 300}
|
|
131
|
+
* scrollDirectionX = sign(300 - 0) = +1
|
|
132
|
+
* movementDirectionX = sign(300 - 300) = 0 (no movement)
|
|
133
|
+
* movementDirectionX is 0 => ignored, not a reversal
|
|
134
|
+
*/
|
|
135
|
+
hasDirectionChanged(state, current) {
|
|
136
|
+
const { start, lastPosition: previous } = state;
|
|
137
|
+
const scrollDirectionX = Math.sign(previous.x - start.x);
|
|
138
|
+
const scrollDirectionY = Math.sign(previous.y - start.y);
|
|
139
|
+
const movementDirectionX = Math.sign(current.x - previous.x);
|
|
140
|
+
const movementDirectionY = Math.sign(current.y - previous.y);
|
|
141
|
+
return scrollDirectionX !== 0 && movementDirectionX !== 0 && scrollDirectionX !== movementDirectionX || scrollDirectionY !== 0 && movementDirectionY !== 0 && scrollDirectionY !== movementDirectionY;
|
|
142
|
+
}
|
|
143
|
+
flushPendingScroll(end) {
|
|
144
|
+
if (this.scrollDebounceTimer !== void 0) {
|
|
145
|
+
window.clearTimeout(this.scrollDebounceTimer);
|
|
146
|
+
this.scrollDebounceTimer = void 0;
|
|
147
|
+
}
|
|
148
|
+
if (this.scrollState === void 0) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const { start } = this.scrollState;
|
|
152
|
+
const destination = end ?? {
|
|
153
|
+
x: Math.max(0, Math.round(window.scrollX)),
|
|
154
|
+
y: Math.max(0, Math.round(window.scrollY))
|
|
155
|
+
};
|
|
156
|
+
this.scrollState = void 0;
|
|
157
|
+
if (start.x === destination.x && start.y === destination.y) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
this.eventManager.dispatch("userScrolled", {
|
|
161
|
+
type: "userScrolled",
|
|
162
|
+
start,
|
|
163
|
+
end: destination,
|
|
164
|
+
surfaceSize: {
|
|
165
|
+
width: document.documentElement.scrollWidth,
|
|
166
|
+
height: document.documentElement.scrollHeight
|
|
167
|
+
},
|
|
168
|
+
// Uses clientWidth/clientHeight instead of innerWidth/innerHeight to get the
|
|
169
|
+
// layout viewport size, which remains stable regardless of pinch-to-zoom level
|
|
170
|
+
// on mobile devices.
|
|
171
|
+
viewportSize: {
|
|
172
|
+
width: document.documentElement.clientWidth,
|
|
173
|
+
height: document.documentElement.clientHeight
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
179
|
+
0 && (module.exports = {
|
|
180
|
+
InteractionMonitor
|
|
181
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { EventListener } from './eventManager.cjs';
|
|
2
|
+
import { UserClicked, UserScrolled } from './trackingEvents.cjs';
|
|
3
|
+
import './patch.cjs';
|
|
4
|
+
import '@croct/json';
|
|
5
|
+
import './utilityTypes.cjs';
|
|
6
|
+
|
|
7
|
+
type InteractionEventMap = {
|
|
8
|
+
userClicked: UserClicked;
|
|
9
|
+
userScrolled: UserScrolled;
|
|
10
|
+
};
|
|
11
|
+
type Options = {
|
|
12
|
+
clickThrottleInterval?: number;
|
|
13
|
+
scrollDebounceInterval?: number;
|
|
14
|
+
};
|
|
15
|
+
declare class InteractionMonitor {
|
|
16
|
+
private readonly eventManager;
|
|
17
|
+
private readonly clickThrottleInterval;
|
|
18
|
+
private readonly scrollDebounceInterval;
|
|
19
|
+
private lastClickTime;
|
|
20
|
+
private scrollState?;
|
|
21
|
+
private scrollDebounceTimer?;
|
|
22
|
+
private enabled;
|
|
23
|
+
constructor(options?: Options);
|
|
24
|
+
addListener<T extends keyof InteractionEventMap>(type: T, listener: EventListener<InteractionEventMap[T]>): void;
|
|
25
|
+
removeListener<T extends keyof InteractionEventMap>(type: T, listener: EventListener<InteractionEventMap[T]>): void;
|
|
26
|
+
isEnabled(): boolean;
|
|
27
|
+
enable(): void;
|
|
28
|
+
disable(): void;
|
|
29
|
+
private handleClick;
|
|
30
|
+
private handleScroll;
|
|
31
|
+
/**
|
|
32
|
+
* Determines whether the scroll direction has reversed on either axis.
|
|
33
|
+
*
|
|
34
|
+
* It compares two vectors:
|
|
35
|
+
* - The scroll direction: the overall direction from start to the previous position (start -> previous)
|
|
36
|
+
* - The movement direction: the direction of the latest movement (previous -> current)
|
|
37
|
+
*
|
|
38
|
+
* A reversal is detected when these two vectors point in opposite directions on the same axis.
|
|
39
|
+
* Axes with no movement (sign = 0) are ignored, so scrolling that simply stops on one axis
|
|
40
|
+
* without reversing does not trigger a flush.
|
|
41
|
+
*
|
|
42
|
+
* Example of vertical reversal:
|
|
43
|
+
* start = {y: 0}, previous = {y: 500}, current = {y: 400}
|
|
44
|
+
* scrollDirectionY = sign(500 - 0) = +1 (downward)
|
|
45
|
+
* movementDirectionY = sign(400 - 500) = -1 (upward)
|
|
46
|
+
* +1 !== -1 => reversed
|
|
47
|
+
*
|
|
48
|
+
* Example of no reversal (same direction):
|
|
49
|
+
* start = {y: 0}, previous = {y: 200}, current = {y: 500}
|
|
50
|
+
* scrollDirectionY = sign(200 - 0) = +1 (downward)
|
|
51
|
+
* movementDirectionY = sign(500 - 200) = +1 (downward)
|
|
52
|
+
* +1 === +1 => not reversed
|
|
53
|
+
*
|
|
54
|
+
* Example of horizontal stop ignored:
|
|
55
|
+
* start = {x: 0}, previous = {x: 300}, current = {x: 300}
|
|
56
|
+
* scrollDirectionX = sign(300 - 0) = +1
|
|
57
|
+
* movementDirectionX = sign(300 - 300) = 0 (no movement)
|
|
58
|
+
* movementDirectionX is 0 => ignored, not a reversal
|
|
59
|
+
*/
|
|
60
|
+
private hasDirectionChanged;
|
|
61
|
+
private flushPendingScroll;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { InteractionMonitor };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { EventListener } from './eventManager.js';
|
|
2
|
+
import { UserClicked, UserScrolled } from './trackingEvents.js';
|
|
3
|
+
import './patch.js';
|
|
4
|
+
import '@croct/json';
|
|
5
|
+
import './utilityTypes.js';
|
|
6
|
+
|
|
7
|
+
type InteractionEventMap = {
|
|
8
|
+
userClicked: UserClicked;
|
|
9
|
+
userScrolled: UserScrolled;
|
|
10
|
+
};
|
|
11
|
+
type Options = {
|
|
12
|
+
clickThrottleInterval?: number;
|
|
13
|
+
scrollDebounceInterval?: number;
|
|
14
|
+
};
|
|
15
|
+
declare class InteractionMonitor {
|
|
16
|
+
private readonly eventManager;
|
|
17
|
+
private readonly clickThrottleInterval;
|
|
18
|
+
private readonly scrollDebounceInterval;
|
|
19
|
+
private lastClickTime;
|
|
20
|
+
private scrollState?;
|
|
21
|
+
private scrollDebounceTimer?;
|
|
22
|
+
private enabled;
|
|
23
|
+
constructor(options?: Options);
|
|
24
|
+
addListener<T extends keyof InteractionEventMap>(type: T, listener: EventListener<InteractionEventMap[T]>): void;
|
|
25
|
+
removeListener<T extends keyof InteractionEventMap>(type: T, listener: EventListener<InteractionEventMap[T]>): void;
|
|
26
|
+
isEnabled(): boolean;
|
|
27
|
+
enable(): void;
|
|
28
|
+
disable(): void;
|
|
29
|
+
private handleClick;
|
|
30
|
+
private handleScroll;
|
|
31
|
+
/**
|
|
32
|
+
* Determines whether the scroll direction has reversed on either axis.
|
|
33
|
+
*
|
|
34
|
+
* It compares two vectors:
|
|
35
|
+
* - The scroll direction: the overall direction from start to the previous position (start -> previous)
|
|
36
|
+
* - The movement direction: the direction of the latest movement (previous -> current)
|
|
37
|
+
*
|
|
38
|
+
* A reversal is detected when these two vectors point in opposite directions on the same axis.
|
|
39
|
+
* Axes with no movement (sign = 0) are ignored, so scrolling that simply stops on one axis
|
|
40
|
+
* without reversing does not trigger a flush.
|
|
41
|
+
*
|
|
42
|
+
* Example of vertical reversal:
|
|
43
|
+
* start = {y: 0}, previous = {y: 500}, current = {y: 400}
|
|
44
|
+
* scrollDirectionY = sign(500 - 0) = +1 (downward)
|
|
45
|
+
* movementDirectionY = sign(400 - 500) = -1 (upward)
|
|
46
|
+
* +1 !== -1 => reversed
|
|
47
|
+
*
|
|
48
|
+
* Example of no reversal (same direction):
|
|
49
|
+
* start = {y: 0}, previous = {y: 200}, current = {y: 500}
|
|
50
|
+
* scrollDirectionY = sign(200 - 0) = +1 (downward)
|
|
51
|
+
* movementDirectionY = sign(500 - 200) = +1 (downward)
|
|
52
|
+
* +1 === +1 => not reversed
|
|
53
|
+
*
|
|
54
|
+
* Example of horizontal stop ignored:
|
|
55
|
+
* start = {x: 0}, previous = {x: 300}, current = {x: 300}
|
|
56
|
+
* scrollDirectionX = sign(300 - 0) = +1
|
|
57
|
+
* movementDirectionX = sign(300 - 300) = 0 (no movement)
|
|
58
|
+
* movementDirectionX is 0 => ignored, not a reversal
|
|
59
|
+
*/
|
|
60
|
+
private hasDirectionChanged;
|
|
61
|
+
private flushPendingScroll;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { InteractionMonitor };
|