@kryptonhq/analytics 0.1.1 → 0.1.3
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/dist/browser.global.js +2 -2
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +249 -164
- package/dist/index.mjs +249 -164
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -14,25 +14,31 @@ function generateId() {
|
|
|
14
14
|
}
|
|
15
15
|
function getSessionId() {
|
|
16
16
|
const key = "_krypton_sid";
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
try {
|
|
18
|
+
if (typeof sessionStorage !== "undefined") {
|
|
19
|
+
let sid = sessionStorage.getItem(key);
|
|
20
|
+
if (!sid) {
|
|
21
|
+
sid = generateId();
|
|
22
|
+
sessionStorage.setItem(key, sid);
|
|
23
|
+
}
|
|
24
|
+
return sid;
|
|
22
25
|
}
|
|
23
|
-
|
|
26
|
+
} catch {
|
|
24
27
|
}
|
|
25
28
|
return generateId();
|
|
26
29
|
}
|
|
27
30
|
function getDistinctId() {
|
|
28
31
|
const key = "_krypton_did";
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
try {
|
|
33
|
+
if (typeof localStorage !== "undefined") {
|
|
34
|
+
let did = localStorage.getItem(key);
|
|
35
|
+
if (!did) {
|
|
36
|
+
did = generateId();
|
|
37
|
+
localStorage.setItem(key, did);
|
|
38
|
+
}
|
|
39
|
+
return did;
|
|
34
40
|
}
|
|
35
|
-
|
|
41
|
+
} catch {
|
|
36
42
|
}
|
|
37
43
|
return generateId();
|
|
38
44
|
}
|
|
@@ -86,6 +92,7 @@ var Krypton = class {
|
|
|
86
92
|
this.geoContext = null;
|
|
87
93
|
this.serverConfig = null;
|
|
88
94
|
this.configFetched = false;
|
|
95
|
+
this.logPrefix = "[Krypton SDK]";
|
|
89
96
|
this.config = {
|
|
90
97
|
autoPageview: true,
|
|
91
98
|
heatmap: false,
|
|
@@ -112,6 +119,8 @@ var Krypton = class {
|
|
|
112
119
|
if (this.config.consentRequired && this.config.showConsentBanner && !this.hasStoredConsent()) {
|
|
113
120
|
this.showConsentBanner();
|
|
114
121
|
}
|
|
122
|
+
}).catch((err) => {
|
|
123
|
+
this.warn("Initialization failed", err);
|
|
115
124
|
});
|
|
116
125
|
}
|
|
117
126
|
resolveInitialConsent() {
|
|
@@ -131,14 +140,18 @@ var Krypton = class {
|
|
|
131
140
|
return `_krypton_consent_${this.config.apiKey}`;
|
|
132
141
|
}
|
|
133
142
|
hasStoredConsent() {
|
|
134
|
-
|
|
135
|
-
|
|
143
|
+
try {
|
|
144
|
+
if (typeof localStorage === "undefined") return false;
|
|
145
|
+
return !!localStorage.getItem(this.consentStorageKey());
|
|
146
|
+
} catch {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
136
149
|
}
|
|
137
150
|
loadStoredConsent() {
|
|
138
|
-
if (typeof localStorage === "undefined") return null;
|
|
139
|
-
const raw = localStorage.getItem(this.consentStorageKey());
|
|
140
|
-
if (!raw) return null;
|
|
141
151
|
try {
|
|
152
|
+
if (typeof localStorage === "undefined") return null;
|
|
153
|
+
const raw = localStorage.getItem(this.consentStorageKey());
|
|
154
|
+
if (!raw) return null;
|
|
142
155
|
const parsed = JSON.parse(raw);
|
|
143
156
|
return {
|
|
144
157
|
analytics: !!parsed.analytics,
|
|
@@ -150,8 +163,11 @@ var Krypton = class {
|
|
|
150
163
|
}
|
|
151
164
|
}
|
|
152
165
|
persistConsent() {
|
|
153
|
-
|
|
154
|
-
|
|
166
|
+
try {
|
|
167
|
+
if (typeof localStorage === "undefined") return;
|
|
168
|
+
localStorage.setItem(this.consentStorageKey(), JSON.stringify(this.consent));
|
|
169
|
+
} catch {
|
|
170
|
+
}
|
|
155
171
|
}
|
|
156
172
|
promotePersistentIds() {
|
|
157
173
|
this.distinctId = getDistinctId();
|
|
@@ -168,7 +184,12 @@ var Krypton = class {
|
|
|
168
184
|
async fetchConfig() {
|
|
169
185
|
if (typeof window === "undefined") return;
|
|
170
186
|
const cacheKey = `_krypton_config_${this.config.apiKey}`;
|
|
171
|
-
|
|
187
|
+
let cached = null;
|
|
188
|
+
try {
|
|
189
|
+
cached = sessionStorage.getItem(cacheKey);
|
|
190
|
+
} catch {
|
|
191
|
+
cached = null;
|
|
192
|
+
}
|
|
172
193
|
if (cached) {
|
|
173
194
|
try {
|
|
174
195
|
const parsed = JSON.parse(cached);
|
|
@@ -188,44 +209,63 @@ var Krypton = class {
|
|
|
188
209
|
const data = await res.json();
|
|
189
210
|
this.serverConfig = data;
|
|
190
211
|
this.configFetched = true;
|
|
191
|
-
|
|
212
|
+
try {
|
|
213
|
+
sessionStorage.setItem(cacheKey, JSON.stringify({ ...data, _ts: Date.now() }));
|
|
214
|
+
} catch {
|
|
215
|
+
}
|
|
192
216
|
}
|
|
193
217
|
} catch {
|
|
194
218
|
}
|
|
195
219
|
}
|
|
196
220
|
setupTracking() {
|
|
197
|
-
this.flushTimer = setInterval(() =>
|
|
221
|
+
this.flushTimer = setInterval(() => {
|
|
222
|
+
void this.flush();
|
|
223
|
+
}, this.config.flushInterval);
|
|
198
224
|
if (typeof window === "undefined") return;
|
|
199
|
-
window.addEventListener("beforeunload", () =>
|
|
225
|
+
window.addEventListener("beforeunload", () => {
|
|
226
|
+
void this.flush();
|
|
227
|
+
});
|
|
200
228
|
if (this.config.autoPageview && this.canTrackAnalytics()) {
|
|
201
229
|
this.trackPageview();
|
|
202
230
|
}
|
|
203
231
|
const originalPushState = history.pushState;
|
|
204
232
|
history.pushState = (...args) => {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
233
|
+
try {
|
|
234
|
+
originalPushState.apply(history, args);
|
|
235
|
+
if (this.config.autoPageview && this.canTrackAnalytics()) {
|
|
236
|
+
setTimeout(() => this.trackPageview(), 0);
|
|
237
|
+
}
|
|
238
|
+
if (this.isHeatmapFeatureEnabled()) {
|
|
239
|
+
setTimeout(() => this.captureSnapshot(), 0);
|
|
240
|
+
}
|
|
241
|
+
} catch (err) {
|
|
242
|
+
this.warn("pushState hook failed", err);
|
|
211
243
|
}
|
|
212
244
|
};
|
|
213
245
|
const originalReplaceState = history.replaceState;
|
|
214
246
|
history.replaceState = (...args) => {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
247
|
+
try {
|
|
248
|
+
originalReplaceState.apply(history, args);
|
|
249
|
+
if (this.config.autoPageview && this.canTrackAnalytics()) {
|
|
250
|
+
setTimeout(() => this.trackPageview(), 0);
|
|
251
|
+
}
|
|
252
|
+
if (this.isHeatmapFeatureEnabled()) {
|
|
253
|
+
setTimeout(() => this.captureSnapshot(), 0);
|
|
254
|
+
}
|
|
255
|
+
} catch (err) {
|
|
256
|
+
this.warn("replaceState hook failed", err);
|
|
221
257
|
}
|
|
222
258
|
};
|
|
223
259
|
window.addEventListener("popstate", () => {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
260
|
+
try {
|
|
261
|
+
if (this.config.autoPageview && this.canTrackAnalytics()) {
|
|
262
|
+
setTimeout(() => this.trackPageview(), 0);
|
|
263
|
+
}
|
|
264
|
+
if (this.isHeatmapFeatureEnabled()) {
|
|
265
|
+
setTimeout(() => this.captureSnapshot(), 0);
|
|
266
|
+
}
|
|
267
|
+
} catch (err) {
|
|
268
|
+
this.warn("popstate hook failed", err);
|
|
229
269
|
}
|
|
230
270
|
});
|
|
231
271
|
if (this.isHeatmapFeatureEnabled()) {
|
|
@@ -282,58 +322,68 @@ var Krypton = class {
|
|
|
282
322
|
`;
|
|
283
323
|
const remove = () => {
|
|
284
324
|
banner.remove();
|
|
325
|
+
try {
|
|
326
|
+
if (document.body) document.body.style.cursor = "";
|
|
327
|
+
if (document.documentElement) document.documentElement.style.cursor = "";
|
|
328
|
+
} catch {
|
|
329
|
+
}
|
|
285
330
|
};
|
|
286
331
|
const readValue = (id) => {
|
|
287
332
|
const el = document.getElementById(id);
|
|
288
333
|
return !!el?.checked;
|
|
289
334
|
};
|
|
290
335
|
banner.querySelector("#krypton-consent-save")?.addEventListener("click", () => {
|
|
291
|
-
|
|
336
|
+
const next = {
|
|
292
337
|
analytics: readValue("krypton-consent-analytics"),
|
|
293
338
|
heatmaps: readValue("krypton-consent-heatmaps"),
|
|
294
339
|
geo: readValue("krypton-consent-geo")
|
|
295
|
-
}
|
|
340
|
+
};
|
|
296
341
|
remove();
|
|
342
|
+
setTimeout(() => this.setConsent(next), 0);
|
|
297
343
|
});
|
|
298
344
|
banner.querySelector("#krypton-consent-all")?.addEventListener("click", () => {
|
|
299
|
-
this.setConsent({ analytics: true, heatmaps: true, geo: true });
|
|
300
345
|
remove();
|
|
346
|
+
setTimeout(() => this.setConsent({ analytics: true, heatmaps: true, geo: true }), 0);
|
|
301
347
|
});
|
|
302
348
|
banner.querySelector("#krypton-consent-none")?.addEventListener("click", () => {
|
|
303
|
-
this.setConsent({ analytics: false, heatmaps: false, geo: false });
|
|
304
349
|
remove();
|
|
350
|
+
setTimeout(() => this.setConsent({ analytics: false, heatmaps: false, geo: false }), 0);
|
|
305
351
|
});
|
|
306
352
|
document.body.appendChild(banner);
|
|
307
353
|
}
|
|
308
354
|
/** Set one or more consent categories and apply changes immediately. */
|
|
309
355
|
setConsent(next, persist = true) {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
if (!prev.analytics && this.consent.analytics) {
|
|
320
|
-
this.promotePersistentIds();
|
|
321
|
-
if (this.config.autoPageview) {
|
|
322
|
-
this.trackPageview();
|
|
356
|
+
try {
|
|
357
|
+
const prev = { ...this.consent };
|
|
358
|
+
this.consent = {
|
|
359
|
+
analytics: next.analytics ?? prev.analytics,
|
|
360
|
+
heatmaps: next.heatmaps ?? prev.heatmaps,
|
|
361
|
+
geo: next.geo ?? prev.geo
|
|
362
|
+
};
|
|
363
|
+
if (persist) {
|
|
364
|
+
this.persistConsent();
|
|
323
365
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
this.
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
this.
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
366
|
+
if (!prev.analytics && this.consent.analytics) {
|
|
367
|
+
this.promotePersistentIds();
|
|
368
|
+
if (this.config.autoPageview) {
|
|
369
|
+
this.trackPageview();
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (prev.analytics && !this.consent.analytics) {
|
|
373
|
+
this.eventQueue = [];
|
|
374
|
+
}
|
|
375
|
+
if (!prev.heatmaps && this.isHeatmapFeatureEnabled()) {
|
|
376
|
+
this.setupHeatmap();
|
|
377
|
+
this.captureSnapshot();
|
|
378
|
+
}
|
|
379
|
+
if (prev.heatmaps && !this.consent.heatmaps) {
|
|
380
|
+
this.heatmapQueue = [];
|
|
381
|
+
}
|
|
382
|
+
if (!prev.geo && this.consent.geo) {
|
|
383
|
+
this.captureGeoOnce();
|
|
384
|
+
}
|
|
385
|
+
} catch (err) {
|
|
386
|
+
this.warn("setConsent failed", err);
|
|
337
387
|
}
|
|
338
388
|
}
|
|
339
389
|
getConsent() {
|
|
@@ -351,60 +401,81 @@ var Krypton = class {
|
|
|
351
401
|
}
|
|
352
402
|
/** Identify a user with a custom distinct ID */
|
|
353
403
|
identify(distinctId) {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
404
|
+
try {
|
|
405
|
+
this.distinctId = distinctId;
|
|
406
|
+
if (this.canTrackAnalytics()) {
|
|
407
|
+
try {
|
|
408
|
+
if (typeof localStorage !== "undefined") {
|
|
409
|
+
localStorage.setItem("_krypton_did", distinctId);
|
|
410
|
+
}
|
|
411
|
+
} catch {
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
} catch (err) {
|
|
415
|
+
this.warn("identify failed", err);
|
|
357
416
|
}
|
|
358
417
|
}
|
|
359
418
|
/** Track a pageview */
|
|
360
419
|
trackPageview(properties) {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
420
|
+
try {
|
|
421
|
+
if (!this.canTrackAnalytics()) return;
|
|
422
|
+
this.track("$pageview", {
|
|
423
|
+
...properties
|
|
424
|
+
});
|
|
425
|
+
} catch (err) {
|
|
426
|
+
this.warn("trackPageview failed", err);
|
|
427
|
+
}
|
|
365
428
|
}
|
|
366
429
|
/** Track a custom event */
|
|
367
430
|
track(eventName, properties) {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
this.
|
|
431
|
+
try {
|
|
432
|
+
if (!this.canTrackAnalytics()) return;
|
|
433
|
+
const utm = getUTMParams();
|
|
434
|
+
const mergedProperties = { ...properties || {} };
|
|
435
|
+
if (this.consent.geo && this.geoContext) {
|
|
436
|
+
mergedProperties.geo = this.geoContext;
|
|
437
|
+
}
|
|
438
|
+
const event = {
|
|
439
|
+
project_id: this.config.apiKey,
|
|
440
|
+
distinct_id: this.distinctId,
|
|
441
|
+
event_name: eventName,
|
|
442
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
443
|
+
properties: mergedProperties,
|
|
444
|
+
page_url: typeof window !== "undefined" ? window.location.href : "",
|
|
445
|
+
page_title: typeof document !== "undefined" ? document.title : "",
|
|
446
|
+
referrer: typeof document !== "undefined" ? document.referrer : "",
|
|
447
|
+
utm_source: utm.utm_source || "",
|
|
448
|
+
utm_medium: utm.utm_medium || "",
|
|
449
|
+
utm_campaign: utm.utm_campaign || "",
|
|
450
|
+
utm_term: utm.utm_term || "",
|
|
451
|
+
utm_content: utm.utm_content || "",
|
|
452
|
+
device_type: getDeviceType(),
|
|
453
|
+
browser: this.getBrowser(),
|
|
454
|
+
screen_width: typeof window !== "undefined" ? window.screen.width : 0,
|
|
455
|
+
screen_height: typeof window !== "undefined" ? window.screen.height : 0,
|
|
456
|
+
session_id: this.sessionId
|
|
457
|
+
};
|
|
458
|
+
this.eventQueue.push(event);
|
|
459
|
+
if (this.eventQueue.length >= this.config.batchSize) {
|
|
460
|
+
void this.flush();
|
|
461
|
+
}
|
|
462
|
+
} catch (err) {
|
|
463
|
+
this.warn("track failed", err);
|
|
397
464
|
}
|
|
398
465
|
}
|
|
399
466
|
async flush() {
|
|
400
|
-
|
|
467
|
+
try {
|
|
468
|
+
await Promise.all([this.flushEvents(), this.flushHeatmapEvents()]);
|
|
469
|
+
} catch (err) {
|
|
470
|
+
this.warn("flush failed", err);
|
|
471
|
+
}
|
|
401
472
|
}
|
|
402
473
|
shutdown() {
|
|
403
474
|
if (this.flushTimer) {
|
|
404
475
|
clearInterval(this.flushTimer);
|
|
405
476
|
this.flushTimer = null;
|
|
406
477
|
}
|
|
407
|
-
this.flush();
|
|
478
|
+
void this.flush();
|
|
408
479
|
}
|
|
409
480
|
captureGeoOnce() {
|
|
410
481
|
if (this.geoRequested) return;
|
|
@@ -437,14 +508,8 @@ var Krypton = class {
|
|
|
437
508
|
api_key: this.config.apiKey,
|
|
438
509
|
events
|
|
439
510
|
};
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
method: "POST",
|
|
443
|
-
headers: { "Content-Type": "application/json" },
|
|
444
|
-
body: JSON.stringify(payload),
|
|
445
|
-
keepalive: true
|
|
446
|
-
});
|
|
447
|
-
} catch {
|
|
511
|
+
const ok = await this.safePost(`${this.config.endpoint}/api/v1/ingest/batch`, payload);
|
|
512
|
+
if (!ok) {
|
|
448
513
|
if (this.eventQueue.length < this.config.batchSize * 5) {
|
|
449
514
|
this.eventQueue.unshift(...events);
|
|
450
515
|
}
|
|
@@ -457,14 +522,8 @@ var Krypton = class {
|
|
|
457
522
|
api_key: this.config.apiKey,
|
|
458
523
|
events
|
|
459
524
|
};
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
method: "POST",
|
|
463
|
-
headers: { "Content-Type": "application/json" },
|
|
464
|
-
body: JSON.stringify(payload),
|
|
465
|
-
keepalive: true
|
|
466
|
-
});
|
|
467
|
-
} catch {
|
|
525
|
+
const ok = await this.safePost(`${this.config.endpoint}/api/v1/heatmap`, payload);
|
|
526
|
+
if (!ok) {
|
|
468
527
|
if (this.heatmapQueue.length < this.config.batchSize * 5) {
|
|
469
528
|
this.heatmapQueue.unshift(...events);
|
|
470
529
|
}
|
|
@@ -495,13 +554,7 @@ var Krypton = class {
|
|
|
495
554
|
page_height: document.documentElement.scrollHeight,
|
|
496
555
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
497
556
|
};
|
|
498
|
-
|
|
499
|
-
method: "POST",
|
|
500
|
-
headers: { "Content-Type": "application/json" },
|
|
501
|
-
body: JSON.stringify(payload),
|
|
502
|
-
keepalive: true
|
|
503
|
-
}).catch(() => {
|
|
504
|
-
});
|
|
557
|
+
void this.safePost(`${this.config.endpoint}/api/v1/snapshot`, payload);
|
|
505
558
|
} catch {
|
|
506
559
|
}
|
|
507
560
|
}, 1e3);
|
|
@@ -513,51 +566,83 @@ var Krypton = class {
|
|
|
513
566
|
this.captureSnapshot();
|
|
514
567
|
document.addEventListener("click", (e) => {
|
|
515
568
|
if (!this.isHeatmapFeatureEnabled()) return;
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
project_id: this.config.apiKey,
|
|
519
|
-
distinct_id: this.distinctId,
|
|
520
|
-
session_id: this.sessionId,
|
|
521
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
522
|
-
page_url: window.location.href,
|
|
523
|
-
interaction_type: "click",
|
|
524
|
-
x: e.clientX + window.scrollX,
|
|
525
|
-
y: e.clientY + window.scrollY,
|
|
526
|
-
viewport_width: window.innerWidth,
|
|
527
|
-
viewport_height: window.innerHeight,
|
|
528
|
-
page_width: document.documentElement.scrollWidth,
|
|
529
|
-
page_height: document.documentElement.scrollHeight,
|
|
530
|
-
selector: getSelector(target)
|
|
531
|
-
});
|
|
532
|
-
});
|
|
533
|
-
let maxScrollDepth = 0;
|
|
534
|
-
let scrollTimeout = null;
|
|
535
|
-
window.addEventListener("scroll", () => {
|
|
536
|
-
if (!this.isHeatmapFeatureEnabled()) return;
|
|
537
|
-
const scrollTop = window.scrollY || document.documentElement.scrollTop;
|
|
538
|
-
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
|
|
539
|
-
const depth = docHeight > 0 ? scrollTop / docHeight * 100 : 0;
|
|
540
|
-
if (depth > maxScrollDepth) {
|
|
541
|
-
maxScrollDepth = depth;
|
|
542
|
-
}
|
|
543
|
-
if (scrollTimeout) clearTimeout(scrollTimeout);
|
|
544
|
-
scrollTimeout = setTimeout(() => {
|
|
569
|
+
try {
|
|
570
|
+
const target = e.target;
|
|
545
571
|
this.heatmapQueue.push({
|
|
546
572
|
project_id: this.config.apiKey,
|
|
547
573
|
distinct_id: this.distinctId,
|
|
548
574
|
session_id: this.sessionId,
|
|
549
575
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
550
576
|
page_url: window.location.href,
|
|
551
|
-
interaction_type: "
|
|
552
|
-
|
|
577
|
+
interaction_type: "click",
|
|
578
|
+
x: e.clientX + window.scrollX,
|
|
579
|
+
y: e.clientY + window.scrollY,
|
|
553
580
|
viewport_width: window.innerWidth,
|
|
554
581
|
viewport_height: window.innerHeight,
|
|
555
582
|
page_width: document.documentElement.scrollWidth,
|
|
556
|
-
page_height: document.documentElement.scrollHeight
|
|
583
|
+
page_height: document.documentElement.scrollHeight,
|
|
584
|
+
selector: getSelector(target)
|
|
557
585
|
});
|
|
558
|
-
}
|
|
586
|
+
} catch (err) {
|
|
587
|
+
this.warn("heatmap click capture failed", err);
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
let maxScrollDepth = 0;
|
|
591
|
+
let scrollTimeout = null;
|
|
592
|
+
window.addEventListener("scroll", () => {
|
|
593
|
+
if (!this.isHeatmapFeatureEnabled()) return;
|
|
594
|
+
try {
|
|
595
|
+
const scrollTop = window.scrollY || document.documentElement.scrollTop;
|
|
596
|
+
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
|
|
597
|
+
const depth = docHeight > 0 ? scrollTop / docHeight * 100 : 0;
|
|
598
|
+
if (depth > maxScrollDepth) {
|
|
599
|
+
maxScrollDepth = depth;
|
|
600
|
+
}
|
|
601
|
+
if (scrollTimeout) clearTimeout(scrollTimeout);
|
|
602
|
+
scrollTimeout = setTimeout(() => {
|
|
603
|
+
this.heatmapQueue.push({
|
|
604
|
+
project_id: this.config.apiKey,
|
|
605
|
+
distinct_id: this.distinctId,
|
|
606
|
+
session_id: this.sessionId,
|
|
607
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
608
|
+
page_url: window.location.href,
|
|
609
|
+
interaction_type: "scroll",
|
|
610
|
+
scroll_depth: maxScrollDepth,
|
|
611
|
+
viewport_width: window.innerWidth,
|
|
612
|
+
viewport_height: window.innerHeight,
|
|
613
|
+
page_width: document.documentElement.scrollWidth,
|
|
614
|
+
page_height: document.documentElement.scrollHeight
|
|
615
|
+
});
|
|
616
|
+
}, 500);
|
|
617
|
+
} catch (err) {
|
|
618
|
+
this.warn("heatmap scroll capture failed", err);
|
|
619
|
+
}
|
|
559
620
|
});
|
|
560
621
|
}
|
|
622
|
+
async safePost(url, payload) {
|
|
623
|
+
try {
|
|
624
|
+
const res = await fetch(url, {
|
|
625
|
+
method: "POST",
|
|
626
|
+
headers: { "Content-Type": "application/json" },
|
|
627
|
+
body: JSON.stringify(payload),
|
|
628
|
+
keepalive: true
|
|
629
|
+
});
|
|
630
|
+
if (!res.ok) {
|
|
631
|
+
this.warn(`API request failed (${res.status})`, { url });
|
|
632
|
+
return false;
|
|
633
|
+
}
|
|
634
|
+
return true;
|
|
635
|
+
} catch (err) {
|
|
636
|
+
this.warn("API request failed", { url, err });
|
|
637
|
+
return false;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
warn(message, details) {
|
|
641
|
+
try {
|
|
642
|
+
console.warn(`${this.logPrefix} ${message}`, details ?? "");
|
|
643
|
+
} catch {
|
|
644
|
+
}
|
|
645
|
+
}
|
|
561
646
|
getBrowser() {
|
|
562
647
|
if (typeof navigator === "undefined") return "unknown";
|
|
563
648
|
const ua = navigator.userAgent;
|