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