@croct/sdk 0.21.0 → 0.22.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/apiKey.cjs +3 -3
- package/apiKey.js +3 -3
- 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 +8 -1
- package/container.d.cts +2 -0
- package/container.d.ts +2 -0
- package/container.js +8 -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/apiKey.cjs
CHANGED
|
@@ -26,10 +26,10 @@ const _ApiKey = class _ApiKey {
|
|
|
26
26
|
this.privateKey = privateKey;
|
|
27
27
|
}
|
|
28
28
|
static from(apiKey) {
|
|
29
|
-
if (apiKey
|
|
30
|
-
return apiKey;
|
|
29
|
+
if (typeof apiKey === "string") {
|
|
30
|
+
return _ApiKey.parse(apiKey);
|
|
31
31
|
}
|
|
32
|
-
return
|
|
32
|
+
return apiKey;
|
|
33
33
|
}
|
|
34
34
|
static parse(apiKey) {
|
|
35
35
|
const parts = apiKey.split(":");
|
package/apiKey.js
CHANGED
|
@@ -4,10 +4,10 @@ const _ApiKey = class _ApiKey {
|
|
|
4
4
|
this.privateKey = privateKey;
|
|
5
5
|
}
|
|
6
6
|
static from(apiKey) {
|
|
7
|
-
if (apiKey
|
|
8
|
-
return apiKey;
|
|
7
|
+
if (typeof apiKey === "string") {
|
|
8
|
+
return _ApiKey.parse(apiKey);
|
|
9
9
|
}
|
|
10
|
-
return
|
|
10
|
+
return apiKey;
|
|
11
11
|
}
|
|
12
12
|
static parse(apiKey) {
|
|
13
13
|
const parts = apiKey.split(":");
|
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.0";
|
|
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.0";
|
|
4
|
+
declare const CLIENT_LIBRARY = "Croct SDK JS v0.22.0";
|
|
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.0";
|
|
4
|
+
declare const CLIENT_LIBRARY = "Croct SDK JS v0.22.0";
|
|
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 = Intl.DateTimeFormat().resolvedOptions().timeZone ?? null;
|
|
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);
|
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;
|
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;
|
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 = Intl.DateTimeFormat().resolvedOptions().timeZone ?? null;
|
|
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);
|
|
@@ -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 };
|