@zaplier/sdk 1.7.0 → 1.7.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/index.cjs +695 -236
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -6
- package/dist/index.esm.js +695 -236
- package/dist/index.esm.js.map +1 -1
- package/dist/sdk.js +695 -236
- package/dist/sdk.js.map +1 -1
- package/dist/sdk.min.js +1 -1
- package/dist/src/modules/auto-tracker.d.ts +86 -8
- package/dist/src/modules/auto-tracker.d.ts.map +1 -1
- package/dist/src/modules/visitor-persistence.d.ts.map +1 -1
- package/dist/src/sdk.d.ts +1 -6
- package/dist/src/sdk.d.ts.map +1 -1
- package/dist/src/utils/session-utils.d.ts +34 -0
- package/dist/src/utils/session-utils.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -19264,85 +19264,14 @@ class AutoTracker {
|
|
|
19264
19264
|
this.scrollThrottle = 0;
|
|
19265
19265
|
this.isInitialized = false;
|
|
19266
19266
|
this.delegationHandlers = new Map();
|
|
19267
|
-
this.
|
|
19268
|
-
|
|
19269
|
-
|
|
19270
|
-
|
|
19271
|
-
|
|
19272
|
-
|
|
19273
|
-
|
|
19274
|
-
|
|
19275
|
-
const scrollElements = document.querySelectorAll('[data-track-scroll]');
|
|
19276
|
-
if (this.config.debug && scrollElements.length > 0) {
|
|
19277
|
-
console.log(`[AutoTracker] Checking ${scrollElements.length} scroll elements`, {
|
|
19278
|
-
scrollY: window.scrollY,
|
|
19279
|
-
innerHeight: window.innerHeight
|
|
19280
|
-
});
|
|
19281
|
-
}
|
|
19282
|
-
scrollElements.forEach((element) => {
|
|
19283
|
-
let hasTriggered = element.getAttribute('data-scroll-triggered') === 'true';
|
|
19284
|
-
if (hasTriggered)
|
|
19285
|
-
return;
|
|
19286
|
-
const threshold = parseFloat(element.getAttribute("data-scroll-threshold") || "0.5");
|
|
19287
|
-
const rect = element.getBoundingClientRect();
|
|
19288
|
-
const elementHeight = rect.height;
|
|
19289
|
-
const visibleHeight = Math.min(rect.bottom, window.innerHeight) - Math.max(rect.top, 0);
|
|
19290
|
-
const visibilityRatio = Math.max(0, visibleHeight) / elementHeight;
|
|
19291
|
-
if (this.config.debug) {
|
|
19292
|
-
console.log(`[AutoTracker] Element check:`, {
|
|
19293
|
-
elementId: element.id || element.className,
|
|
19294
|
-
visibilityRatio: Math.round(visibilityRatio * 100) / 100,
|
|
19295
|
-
threshold,
|
|
19296
|
-
triggered: hasTriggered
|
|
19297
|
-
});
|
|
19298
|
-
}
|
|
19299
|
-
if (visibilityRatio >= threshold) {
|
|
19300
|
-
element.setAttribute('data-scroll-triggered', 'true');
|
|
19301
|
-
const eventName = element.getAttribute("data-track-scroll");
|
|
19302
|
-
if (!eventName)
|
|
19303
|
-
return;
|
|
19304
|
-
const metadata = this.extractMetadata(element);
|
|
19305
|
-
this.trackEvent(eventName, {
|
|
19306
|
-
type: "scroll",
|
|
19307
|
-
element: element.tagName.toLowerCase(),
|
|
19308
|
-
threshold,
|
|
19309
|
-
scrollDepth: window.scrollY,
|
|
19310
|
-
visibilityRatio: Math.round(visibilityRatio * 100) / 100,
|
|
19311
|
-
...metadata,
|
|
19312
|
-
});
|
|
19313
|
-
if (this.config.debug) {
|
|
19314
|
-
console.log(`[AutoTracker] Scroll tracked: ${eventName}`, {
|
|
19315
|
-
threshold,
|
|
19316
|
-
visibilityRatio,
|
|
19317
|
-
scrollDepth: window.scrollY,
|
|
19318
|
-
...metadata
|
|
19319
|
-
});
|
|
19320
|
-
}
|
|
19321
|
-
}
|
|
19322
|
-
});
|
|
19323
|
-
};
|
|
19324
|
-
this.handleClick = (event) => {
|
|
19325
|
-
if (!this.config.trackClicks)
|
|
19326
|
-
return;
|
|
19327
|
-
const target = event.target;
|
|
19328
|
-
if (!target || !target.hasAttribute("data-track-click"))
|
|
19329
|
-
return;
|
|
19330
|
-
const eventName = target.getAttribute("data-track-click");
|
|
19331
|
-
if (!eventName)
|
|
19332
|
-
return;
|
|
19333
|
-
const metadata = this.extractMetadata(target);
|
|
19334
|
-
this.trackEvent(eventName, {
|
|
19335
|
-
type: "click",
|
|
19336
|
-
element: target.tagName.toLowerCase(),
|
|
19337
|
-
...metadata,
|
|
19338
|
-
});
|
|
19339
|
-
if (this.config.debug) {
|
|
19340
|
-
console.log(`[AutoTracker] Click tracked: ${eventName}`, {
|
|
19341
|
-
element: target.tagName.toLowerCase(),
|
|
19342
|
-
...metadata
|
|
19343
|
-
});
|
|
19344
|
-
}
|
|
19345
|
-
};
|
|
19267
|
+
this.initializationAttempts = 0;
|
|
19268
|
+
this.maxInitializationAttempts = 3;
|
|
19269
|
+
this.diagnostics = {};
|
|
19270
|
+
/**
|
|
19271
|
+
* Cache scroll elements for better performance
|
|
19272
|
+
*/
|
|
19273
|
+
this.cachedScrollElements = null;
|
|
19274
|
+
this.lastScrollElementsCheck = 0;
|
|
19346
19275
|
this.sdkInstance = sdkInstance;
|
|
19347
19276
|
this.config = {
|
|
19348
19277
|
enabled: true,
|
|
@@ -19361,33 +19290,208 @@ class AutoTracker {
|
|
|
19361
19290
|
start() {
|
|
19362
19291
|
if (!this.config.enabled)
|
|
19363
19292
|
return;
|
|
19293
|
+
this.initializationAttempts++;
|
|
19294
|
+
// Enhanced diagnostics
|
|
19295
|
+
this.runDiagnostics();
|
|
19364
19296
|
if (this.config.debug) {
|
|
19365
|
-
console.log("[
|
|
19366
|
-
enabled: this.config.enabled,
|
|
19367
|
-
trackClicks: this.config.trackClicks,
|
|
19368
|
-
trackScrolls: this.config.trackScrolls,
|
|
19369
|
-
trackViews: this.config.trackViews,
|
|
19370
|
-
trackHovers: this.config.trackHovers,
|
|
19371
|
-
trackForms: this.config.trackForms,
|
|
19372
|
-
domReady: document.readyState
|
|
19373
|
-
});
|
|
19297
|
+
console.log("[🔥 AutoTracker ENHANCED] Starting auto tracking", this.diagnostics);
|
|
19374
19298
|
}
|
|
19375
|
-
//
|
|
19376
|
-
|
|
19377
|
-
|
|
19378
|
-
|
|
19299
|
+
// Multiple initialization strategies
|
|
19300
|
+
this.tryInitialization();
|
|
19301
|
+
}
|
|
19302
|
+
/**
|
|
19303
|
+
* Run comprehensive diagnostics
|
|
19304
|
+
*/
|
|
19305
|
+
runDiagnostics() {
|
|
19306
|
+
this.diagnostics = {
|
|
19307
|
+
timestamp: new Date().toISOString(),
|
|
19308
|
+
attempt: this.initializationAttempts,
|
|
19309
|
+
environment: {
|
|
19310
|
+
domReady: document.readyState,
|
|
19311
|
+
reactDetected: this.detectReact(),
|
|
19312
|
+
reactVersion: this.getReactVersion(),
|
|
19313
|
+
userAgent: navigator.userAgent,
|
|
19314
|
+
isMobile: /Mobile|Android|iPhone|iPad/.test(navigator.userAgent),
|
|
19315
|
+
isSafari: /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent),
|
|
19316
|
+
protocol: window.location.protocol,
|
|
19317
|
+
isLocalhost: window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'
|
|
19318
|
+
},
|
|
19319
|
+
dom: {
|
|
19320
|
+
documentElement: !!document.documentElement,
|
|
19321
|
+
body: !!document.body,
|
|
19322
|
+
querySelector: typeof document.querySelector === 'function',
|
|
19323
|
+
addEventListener: typeof document.addEventListener === 'function',
|
|
19324
|
+
elementsFound: this.getElementCounts(),
|
|
19325
|
+
hasReactRoot: this.findReactRoot()
|
|
19326
|
+
},
|
|
19327
|
+
tracking: {
|
|
19328
|
+
sdkInstance: !!this.sdkInstance,
|
|
19329
|
+
trackCustomEvent: typeof this.sdkInstance?.trackCustomEvent === 'function',
|
|
19330
|
+
config: this.config,
|
|
19331
|
+
isInitialized: this.isInitialized,
|
|
19332
|
+
activeHandlers: Array.from(this.delegationHandlers.keys())
|
|
19379
19333
|
}
|
|
19334
|
+
};
|
|
19335
|
+
}
|
|
19336
|
+
/**
|
|
19337
|
+
* Detect React presence
|
|
19338
|
+
*/
|
|
19339
|
+
detectReact() {
|
|
19340
|
+
return !!(window.__REACT_DEVTOOLS_GLOBAL_HOOK__ ||
|
|
19341
|
+
window.React ||
|
|
19342
|
+
document.querySelector('[data-reactroot]') ||
|
|
19343
|
+
document.querySelector('#root') ||
|
|
19344
|
+
document.querySelector('#__next'));
|
|
19345
|
+
}
|
|
19346
|
+
/**
|
|
19347
|
+
* Get React version if available
|
|
19348
|
+
*/
|
|
19349
|
+
getReactVersion() {
|
|
19350
|
+
try {
|
|
19351
|
+
const reactInstance = window.React;
|
|
19352
|
+
return reactInstance?.version || null;
|
|
19353
|
+
}
|
|
19354
|
+
catch {
|
|
19355
|
+
return null;
|
|
19356
|
+
}
|
|
19357
|
+
}
|
|
19358
|
+
/**
|
|
19359
|
+
* Find React root element
|
|
19360
|
+
*/
|
|
19361
|
+
findReactRoot() {
|
|
19362
|
+
const reactRoot = document.querySelector('[data-reactroot]') ||
|
|
19363
|
+
document.querySelector('#root') ||
|
|
19364
|
+
document.querySelector('#__next') ||
|
|
19365
|
+
document.querySelector('div[data-react-helmet]');
|
|
19366
|
+
return reactRoot ? reactRoot.tagName + (reactRoot.id ? '#' + reactRoot.id : '') : null;
|
|
19367
|
+
}
|
|
19368
|
+
/**
|
|
19369
|
+
* Get element counts for all tracking types
|
|
19370
|
+
*/
|
|
19371
|
+
getElementCounts() {
|
|
19372
|
+
return {
|
|
19373
|
+
click: document.querySelectorAll('[data-track-click]').length,
|
|
19374
|
+
scroll: document.querySelectorAll('[data-track-scroll]').length,
|
|
19375
|
+
view: document.querySelectorAll('[data-track-view]').length,
|
|
19376
|
+
hover: document.querySelectorAll('[data-track-hover]').length,
|
|
19377
|
+
form: document.querySelectorAll('[data-track-form]').length
|
|
19378
|
+
};
|
|
19379
|
+
}
|
|
19380
|
+
/**
|
|
19381
|
+
* Try multiple initialization strategies
|
|
19382
|
+
*/
|
|
19383
|
+
tryInitialization() {
|
|
19384
|
+
const strategies = [
|
|
19385
|
+
() => this.immediateInitialization(),
|
|
19386
|
+
() => this.delayedInitialization(100),
|
|
19387
|
+
() => this.delayedInitialization(500),
|
|
19388
|
+
() => this.delayedInitialization(1000),
|
|
19389
|
+
() => this.reactReadyInitialization()
|
|
19390
|
+
];
|
|
19391
|
+
// Try immediate initialization first
|
|
19392
|
+
if (document.readyState === 'complete') {
|
|
19393
|
+
this.attemptStrategy(0, strategies);
|
|
19394
|
+
}
|
|
19395
|
+
else if (document.readyState === 'interactive') {
|
|
19396
|
+
// DOM ready but resources still loading
|
|
19397
|
+
this.attemptStrategy(1, strategies);
|
|
19398
|
+
}
|
|
19399
|
+
else {
|
|
19400
|
+
// DOM still loading
|
|
19380
19401
|
document.addEventListener('DOMContentLoaded', () => {
|
|
19381
|
-
|
|
19382
|
-
console.log("[Zaplier AutoTracker] DOMContentLoaded fired, initializing tracking...");
|
|
19383
|
-
}
|
|
19384
|
-
this.initializeTracking();
|
|
19402
|
+
this.attemptStrategy(0, strategies);
|
|
19385
19403
|
}, { once: true });
|
|
19386
19404
|
}
|
|
19387
|
-
|
|
19405
|
+
}
|
|
19406
|
+
/**
|
|
19407
|
+
* Attempt initialization strategy with fallback
|
|
19408
|
+
*/
|
|
19409
|
+
attemptStrategy(index, strategies) {
|
|
19410
|
+
if (index >= strategies.length || this.isInitialized)
|
|
19411
|
+
return;
|
|
19412
|
+
if (this.config.debug) {
|
|
19413
|
+
console.log(`[🔄 AutoTracker] Trying initialization strategy ${index + 1}/${strategies.length}`);
|
|
19414
|
+
}
|
|
19415
|
+
try {
|
|
19416
|
+
const strategy = strategies[index];
|
|
19417
|
+
if (strategy && typeof strategy === 'function') {
|
|
19418
|
+
strategy();
|
|
19419
|
+
// Verify initialization after a short delay
|
|
19420
|
+
setTimeout(() => {
|
|
19421
|
+
if (!this.verifyInitialization()) {
|
|
19422
|
+
if (this.config.debug) {
|
|
19423
|
+
console.log(`[⚠️ AutoTracker] Strategy ${index + 1} failed verification, trying next...`);
|
|
19424
|
+
}
|
|
19425
|
+
this.attemptStrategy(index + 1, strategies);
|
|
19426
|
+
}
|
|
19427
|
+
else {
|
|
19428
|
+
if (this.config.debug) {
|
|
19429
|
+
console.log(`[✅ AutoTracker] Strategy ${index + 1} successful!`);
|
|
19430
|
+
}
|
|
19431
|
+
}
|
|
19432
|
+
}, 50);
|
|
19433
|
+
}
|
|
19434
|
+
else {
|
|
19435
|
+
// Strategy is undefined or not a function, try next
|
|
19436
|
+
this.attemptStrategy(index + 1, strategies);
|
|
19437
|
+
}
|
|
19438
|
+
}
|
|
19439
|
+
catch (error) {
|
|
19440
|
+
if (this.config.debug) {
|
|
19441
|
+
console.error(`[❌ AutoTracker] Strategy ${index + 1} error:`, error);
|
|
19442
|
+
}
|
|
19443
|
+
this.attemptStrategy(index + 1, strategies);
|
|
19444
|
+
}
|
|
19445
|
+
}
|
|
19446
|
+
/**
|
|
19447
|
+
* Immediate initialization
|
|
19448
|
+
*/
|
|
19449
|
+
immediateInitialization() {
|
|
19450
|
+
this.initializeTracking();
|
|
19451
|
+
}
|
|
19452
|
+
/**
|
|
19453
|
+
* Delayed initialization
|
|
19454
|
+
*/
|
|
19455
|
+
delayedInitialization(delay) {
|
|
19456
|
+
setTimeout(() => {
|
|
19388
19457
|
this.initializeTracking();
|
|
19458
|
+
}, delay);
|
|
19459
|
+
}
|
|
19460
|
+
/**
|
|
19461
|
+
* React-specific initialization timing
|
|
19462
|
+
*/
|
|
19463
|
+
reactReadyInitialization() {
|
|
19464
|
+
// Wait for React to finish rendering
|
|
19465
|
+
if (typeof window.requestAnimationFrame === 'function') {
|
|
19466
|
+
window.requestAnimationFrame(() => {
|
|
19467
|
+
setTimeout(() => {
|
|
19468
|
+
this.initializeTracking();
|
|
19469
|
+
}, 0);
|
|
19470
|
+
});
|
|
19471
|
+
}
|
|
19472
|
+
else {
|
|
19473
|
+
setTimeout(() => {
|
|
19474
|
+
this.initializeTracking();
|
|
19475
|
+
}, 16); // ~60fps fallback
|
|
19389
19476
|
}
|
|
19390
19477
|
}
|
|
19478
|
+
/**
|
|
19479
|
+
* Verify initialization was successful
|
|
19480
|
+
*/
|
|
19481
|
+
verifyInitialization() {
|
|
19482
|
+
const elementsFound = this.getElementCounts();
|
|
19483
|
+
const totalElements = Object.values(elementsFound).reduce((sum, count) => sum + count, 0);
|
|
19484
|
+
const hasActiveHandlers = this.delegationHandlers.size > 0;
|
|
19485
|
+
if (this.config.debug) {
|
|
19486
|
+
console.log('[🔍 AutoTracker] Verification:', {
|
|
19487
|
+
isInitialized: this.isInitialized,
|
|
19488
|
+
elementsFound: totalElements,
|
|
19489
|
+
hasActiveHandlers,
|
|
19490
|
+
handlersActive: Array.from(this.delegationHandlers.keys())
|
|
19491
|
+
});
|
|
19492
|
+
}
|
|
19493
|
+
return this.isInitialized && (totalElements > 0 || hasActiveHandlers);
|
|
19494
|
+
}
|
|
19391
19495
|
initializeTracking() {
|
|
19392
19496
|
if (this.config.debug) {
|
|
19393
19497
|
console.log("[Zaplier AutoTracker] Initializing tracking...");
|
|
@@ -19446,119 +19550,337 @@ class AutoTracker {
|
|
|
19446
19550
|
*/
|
|
19447
19551
|
setupEventDelegation() {
|
|
19448
19552
|
if (this.config.debug) {
|
|
19449
|
-
console.log("[AutoTracker] Setting up event delegation...");
|
|
19553
|
+
console.log("[🚀 AutoTracker] Setting up enhanced event delegation...");
|
|
19450
19554
|
}
|
|
19451
|
-
//
|
|
19555
|
+
// Determine optimal delegation target based on React setup
|
|
19556
|
+
const delegationTarget = this.getOptimalDelegationTarget();
|
|
19557
|
+
if (this.config.debug) {
|
|
19558
|
+
console.log("[🎯 AutoTracker] Using delegation target:", delegationTarget);
|
|
19559
|
+
}
|
|
19560
|
+
// Click delegation with React compatibility
|
|
19452
19561
|
if (this.config.trackClicks) {
|
|
19453
|
-
const clickHandler = this.
|
|
19562
|
+
const clickHandler = this.createReactCompatibleClickHandler();
|
|
19454
19563
|
this.delegationHandlers.set('click', clickHandler);
|
|
19455
|
-
|
|
19564
|
+
delegationTarget.addEventListener('click', clickHandler, this.getEventOptions('click'));
|
|
19565
|
+
if (this.config.debug) {
|
|
19566
|
+
console.log("[👆 AutoTracker] Click delegation handler added");
|
|
19567
|
+
}
|
|
19456
19568
|
}
|
|
19457
|
-
// Scroll delegation
|
|
19569
|
+
// CRITICAL FIX: Scroll delegation with enhanced detection
|
|
19458
19570
|
if (this.config.trackScrolls) {
|
|
19459
|
-
const scrollHandler = this.
|
|
19571
|
+
const scrollHandler = this.createEnhancedScrollHandler();
|
|
19460
19572
|
this.delegationHandlers.set('scroll', scrollHandler);
|
|
19461
|
-
document
|
|
19573
|
+
// Always use document for scroll events (window doesn't work with removeEventListener)
|
|
19574
|
+
document.addEventListener('scroll', scrollHandler, this.getEventOptions('scroll'));
|
|
19575
|
+
if (this.config.debug) {
|
|
19576
|
+
console.log("[📜 AutoTracker] Scroll delegation handler added to document");
|
|
19577
|
+
// Immediate test of element detection
|
|
19578
|
+
setTimeout(() => {
|
|
19579
|
+
const scrollElements = this.getCachedScrollElements();
|
|
19580
|
+
console.log(`[📜 AutoTracker] Initial scroll elements check: ${scrollElements.length} found`);
|
|
19581
|
+
if (scrollElements.length > 0) {
|
|
19582
|
+
Array.from(scrollElements).forEach((element, index) => {
|
|
19583
|
+
console.log(`[📜 AutoTracker] Element ${index + 1}:`, {
|
|
19584
|
+
id: element.id || 'no-id',
|
|
19585
|
+
trackEvent: element.getAttribute('data-track-scroll'),
|
|
19586
|
+
threshold: element.getAttribute('data-scroll-threshold') || '0.5',
|
|
19587
|
+
tagName: element.tagName
|
|
19588
|
+
});
|
|
19589
|
+
});
|
|
19590
|
+
}
|
|
19591
|
+
}, 100);
|
|
19592
|
+
}
|
|
19462
19593
|
}
|
|
19463
19594
|
// Setup debounced mutation observer for dynamic content
|
|
19464
19595
|
this.setupIntelligentMutationObserver();
|
|
19465
19596
|
this.isInitialized = true;
|
|
19597
|
+
if (this.config.debug) {
|
|
19598
|
+
console.log("[✅ AutoTracker] Event delegation setup complete", {
|
|
19599
|
+
clickHandler: this.delegationHandlers.has('click'),
|
|
19600
|
+
scrollHandler: this.delegationHandlers.has('scroll'),
|
|
19601
|
+
totalHandlers: this.delegationHandlers.size
|
|
19602
|
+
});
|
|
19603
|
+
}
|
|
19466
19604
|
}
|
|
19467
19605
|
/**
|
|
19468
|
-
*
|
|
19606
|
+
* Get optimal delegation target based on React setup
|
|
19469
19607
|
*/
|
|
19470
|
-
|
|
19471
|
-
|
|
19472
|
-
|
|
19473
|
-
|
|
19474
|
-
|
|
19475
|
-
|
|
19476
|
-
const trackingElement = target.closest('[data-track-click]');
|
|
19477
|
-
if (!trackingElement)
|
|
19478
|
-
return;
|
|
19479
|
-
const eventName = trackingElement.getAttribute('data-track-click');
|
|
19480
|
-
if (!eventName)
|
|
19481
|
-
return;
|
|
19482
|
-
const metadata = this.extractMetadata(trackingElement);
|
|
19483
|
-
this.trackEvent(eventName, {
|
|
19484
|
-
type: 'click',
|
|
19485
|
-
element: trackingElement.tagName.toLowerCase(),
|
|
19486
|
-
...metadata,
|
|
19487
|
-
});
|
|
19608
|
+
getOptimalDelegationTarget() {
|
|
19609
|
+
// For React 17+, prefer root container over document
|
|
19610
|
+
const reactRoot = document.querySelector('#root') ||
|
|
19611
|
+
document.querySelector('#__next') ||
|
|
19612
|
+
document.querySelector('[data-reactroot]');
|
|
19613
|
+
if (reactRoot && this.diagnostics.environment?.reactDetected) {
|
|
19488
19614
|
if (this.config.debug) {
|
|
19489
|
-
console.log(
|
|
19615
|
+
console.log("[⚛️ AutoTracker] Using React root for delegation:", reactRoot);
|
|
19616
|
+
}
|
|
19617
|
+
return reactRoot;
|
|
19618
|
+
}
|
|
19619
|
+
return document;
|
|
19620
|
+
}
|
|
19621
|
+
/**
|
|
19622
|
+
* Get optimized event options for different browsers
|
|
19623
|
+
*/
|
|
19624
|
+
getEventOptions(eventType) {
|
|
19625
|
+
this.diagnostics.environment?.isMobile;
|
|
19626
|
+
const isSafari = this.diagnostics.environment?.isSafari;
|
|
19627
|
+
let options = {};
|
|
19628
|
+
switch (eventType) {
|
|
19629
|
+
case 'click':
|
|
19630
|
+
// Safari iOS requires non-passive click events for proper delegation
|
|
19631
|
+
options = {
|
|
19632
|
+
passive: !isSafari,
|
|
19633
|
+
capture: false
|
|
19634
|
+
};
|
|
19635
|
+
break;
|
|
19636
|
+
case 'scroll':
|
|
19637
|
+
options = {
|
|
19638
|
+
passive: true,
|
|
19639
|
+
capture: false
|
|
19640
|
+
};
|
|
19641
|
+
break;
|
|
19642
|
+
default:
|
|
19643
|
+
options = { passive: true };
|
|
19644
|
+
}
|
|
19645
|
+
if (this.config.debug) {
|
|
19646
|
+
console.log(`[⚙️ AutoTracker] Event options for ${eventType}:`, options);
|
|
19647
|
+
}
|
|
19648
|
+
return options;
|
|
19649
|
+
}
|
|
19650
|
+
/**
|
|
19651
|
+
* Create React-compatible click handler
|
|
19652
|
+
*/
|
|
19653
|
+
createReactCompatibleClickHandler() {
|
|
19654
|
+
return (event) => {
|
|
19655
|
+
try {
|
|
19656
|
+
if (!this.config.trackClicks)
|
|
19657
|
+
return;
|
|
19658
|
+
// Enhanced target detection for nested React components
|
|
19659
|
+
const target = event.target;
|
|
19660
|
+
if (!target)
|
|
19661
|
+
return;
|
|
19662
|
+
// Multiple strategies to find tracking element
|
|
19663
|
+
let trackingElement = null;
|
|
19664
|
+
// Strategy 1: Direct closest (most common)
|
|
19665
|
+
trackingElement = target.closest('[data-track-click]');
|
|
19666
|
+
// Strategy 2: Check if target itself has the attribute (React edge case)
|
|
19667
|
+
if (!trackingElement && target.hasAttribute && target.hasAttribute('data-track-click')) {
|
|
19668
|
+
trackingElement = target;
|
|
19669
|
+
}
|
|
19670
|
+
// Strategy 3: Walk up DOM tree manually (React portal handling)
|
|
19671
|
+
if (!trackingElement) {
|
|
19672
|
+
trackingElement = this.findTrackingElementManually(target, 'data-track-click');
|
|
19673
|
+
}
|
|
19674
|
+
if (!trackingElement) {
|
|
19675
|
+
if (this.config.debug) {
|
|
19676
|
+
console.log('[🔍 AutoTracker] No tracking element found for click:', {
|
|
19677
|
+
target: target.tagName,
|
|
19678
|
+
hasClosest: typeof target.closest === 'function',
|
|
19679
|
+
hasAttribute: typeof target.hasAttribute === 'function'
|
|
19680
|
+
});
|
|
19681
|
+
}
|
|
19682
|
+
return;
|
|
19683
|
+
}
|
|
19684
|
+
const eventName = trackingElement.getAttribute('data-track-click');
|
|
19685
|
+
if (!eventName)
|
|
19686
|
+
return;
|
|
19687
|
+
const metadata = this.extractMetadata(trackingElement);
|
|
19688
|
+
this.trackEvent(eventName, {
|
|
19689
|
+
type: 'click',
|
|
19490
19690
|
element: trackingElement.tagName.toLowerCase(),
|
|
19491
|
-
|
|
19691
|
+
reactCompatible: true,
|
|
19692
|
+
...metadata,
|
|
19492
19693
|
});
|
|
19694
|
+
if (this.config.debug) {
|
|
19695
|
+
console.log(`[🎯 AutoTracker] Click tracked (React-compatible): ${eventName}`, {
|
|
19696
|
+
element: trackingElement.tagName.toLowerCase(),
|
|
19697
|
+
strategy: 'enhanced_delegation',
|
|
19698
|
+
...metadata
|
|
19699
|
+
});
|
|
19700
|
+
}
|
|
19701
|
+
}
|
|
19702
|
+
catch (error) {
|
|
19703
|
+
if (this.config.debug) {
|
|
19704
|
+
console.error('[❌ AutoTracker] Click handler error:', error);
|
|
19705
|
+
}
|
|
19493
19706
|
}
|
|
19494
19707
|
};
|
|
19495
19708
|
}
|
|
19496
19709
|
/**
|
|
19497
|
-
*
|
|
19710
|
+
* Manually walk up DOM tree to find tracking element
|
|
19498
19711
|
*/
|
|
19499
|
-
|
|
19712
|
+
findTrackingElementManually(element, attribute, maxDepth = 10) {
|
|
19713
|
+
let current = element;
|
|
19714
|
+
let depth = 0;
|
|
19715
|
+
while (current && current !== document.documentElement && depth < maxDepth) {
|
|
19716
|
+
if (current.hasAttribute && current.hasAttribute(attribute)) {
|
|
19717
|
+
return current;
|
|
19718
|
+
}
|
|
19719
|
+
current = current.parentElement;
|
|
19720
|
+
depth++;
|
|
19721
|
+
}
|
|
19722
|
+
return null;
|
|
19723
|
+
}
|
|
19724
|
+
/**
|
|
19725
|
+
* Create enhanced scroll handler with better performance
|
|
19726
|
+
*/
|
|
19727
|
+
createEnhancedScrollHandler() {
|
|
19500
19728
|
return () => {
|
|
19501
|
-
|
|
19502
|
-
|
|
19503
|
-
|
|
19504
|
-
|
|
19505
|
-
|
|
19506
|
-
|
|
19507
|
-
|
|
19508
|
-
|
|
19509
|
-
|
|
19510
|
-
|
|
19511
|
-
|
|
19512
|
-
|
|
19513
|
-
|
|
19514
|
-
|
|
19515
|
-
|
|
19729
|
+
try {
|
|
19730
|
+
if (!this.config.trackScrolls)
|
|
19731
|
+
return;
|
|
19732
|
+
// Adaptive throttling based on scroll frequency
|
|
19733
|
+
const now = Date.now();
|
|
19734
|
+
const timeDelta = now - this.scrollThrottle;
|
|
19735
|
+
const throttleDelay = this.diagnostics.environment?.isMobile ? 150 : 100;
|
|
19736
|
+
if (timeDelta < throttleDelay)
|
|
19737
|
+
return;
|
|
19738
|
+
this.scrollThrottle = now;
|
|
19739
|
+
// Enhanced element finding with caching
|
|
19740
|
+
const scrollElements = this.getCachedScrollElements();
|
|
19741
|
+
if (this.config.debug) {
|
|
19742
|
+
console.log(`[📜 AutoTracker] Enhanced scroll handler triggered`, {
|
|
19743
|
+
scrollElementsFound: scrollElements.length,
|
|
19744
|
+
scrollY: window.scrollY,
|
|
19745
|
+
throttleDelay,
|
|
19746
|
+
timeDelta
|
|
19747
|
+
});
|
|
19748
|
+
}
|
|
19749
|
+
// Process elements with enhanced visibility detection
|
|
19750
|
+
if (scrollElements.length > 0) {
|
|
19751
|
+
scrollElements.forEach((element) => {
|
|
19752
|
+
this.processScrollElementEnhanced(element);
|
|
19753
|
+
});
|
|
19754
|
+
}
|
|
19755
|
+
else if (this.config.debug) {
|
|
19756
|
+
console.log(`[📜 AutoTracker] No scroll elements found to process`);
|
|
19757
|
+
}
|
|
19758
|
+
}
|
|
19759
|
+
catch (error) {
|
|
19760
|
+
if (this.config.debug) {
|
|
19761
|
+
console.error('[❌ AutoTracker] Enhanced scroll handler error:', error);
|
|
19762
|
+
}
|
|
19763
|
+
}
|
|
19516
19764
|
};
|
|
19517
19765
|
}
|
|
19766
|
+
getCachedScrollElements() {
|
|
19767
|
+
const now = Date.now();
|
|
19768
|
+
const cacheExpiry = 2000; // Reduced to 2 seconds for faster detection
|
|
19769
|
+
if (!this.cachedScrollElements || (now - this.lastScrollElementsCheck) > cacheExpiry) {
|
|
19770
|
+
this.cachedScrollElements = document.querySelectorAll('[data-track-scroll]');
|
|
19771
|
+
this.lastScrollElementsCheck = now;
|
|
19772
|
+
if (this.config.debug) {
|
|
19773
|
+
console.log(`[🔄 AutoTracker] Refreshed scroll elements cache: ${this.cachedScrollElements.length} elements found`);
|
|
19774
|
+
// Log each element found for debugging
|
|
19775
|
+
Array.from(this.cachedScrollElements).forEach((element, index) => {
|
|
19776
|
+
console.log(`[🔄 AutoTracker] Cached element ${index + 1}:`, {
|
|
19777
|
+
id: element.id || 'no-id',
|
|
19778
|
+
className: element.className || 'no-class',
|
|
19779
|
+
trackEvent: element.getAttribute('data-track-scroll'),
|
|
19780
|
+
threshold: element.getAttribute('data-scroll-threshold') || '0.5',
|
|
19781
|
+
tagName: element.tagName,
|
|
19782
|
+
triggered: element.getAttribute('data-scroll-triggered') === 'true'
|
|
19783
|
+
});
|
|
19784
|
+
});
|
|
19785
|
+
}
|
|
19786
|
+
}
|
|
19787
|
+
return this.cachedScrollElements;
|
|
19788
|
+
}
|
|
19518
19789
|
/**
|
|
19519
|
-
* Process
|
|
19790
|
+
* Process scroll element with enhanced detection
|
|
19520
19791
|
*/
|
|
19521
|
-
|
|
19522
|
-
|
|
19523
|
-
|
|
19524
|
-
|
|
19525
|
-
|
|
19526
|
-
|
|
19527
|
-
|
|
19528
|
-
|
|
19529
|
-
|
|
19530
|
-
|
|
19531
|
-
|
|
19532
|
-
|
|
19533
|
-
|
|
19534
|
-
|
|
19535
|
-
|
|
19536
|
-
|
|
19537
|
-
|
|
19538
|
-
|
|
19539
|
-
|
|
19792
|
+
processScrollElementEnhanced(element) {
|
|
19793
|
+
try {
|
|
19794
|
+
const hasTriggered = element.getAttribute('data-scroll-triggered') === 'true';
|
|
19795
|
+
if (hasTriggered) {
|
|
19796
|
+
if (this.config.debug) {
|
|
19797
|
+
console.log(`[⏭️ AutoTracker] Element already triggered, skipping:`, element.id || element.className || 'no-id');
|
|
19798
|
+
}
|
|
19799
|
+
return;
|
|
19800
|
+
}
|
|
19801
|
+
const threshold = parseFloat(element.getAttribute('data-scroll-threshold') || '0.5');
|
|
19802
|
+
// Enhanced visibility calculation
|
|
19803
|
+
const rect = element.getBoundingClientRect();
|
|
19804
|
+
const elementHeight = rect.height;
|
|
19805
|
+
const windowHeight = window.innerHeight;
|
|
19806
|
+
// Handle edge cases
|
|
19807
|
+
if (elementHeight <= 0) {
|
|
19808
|
+
if (this.config.debug) {
|
|
19809
|
+
console.log(`[⚠️ AutoTracker] Element has no height, skipping:`, element.id || 'no-id');
|
|
19810
|
+
}
|
|
19811
|
+
return;
|
|
19812
|
+
}
|
|
19813
|
+
const visibleTop = Math.max(rect.top, 0);
|
|
19814
|
+
const visibleBottom = Math.min(rect.bottom, windowHeight);
|
|
19815
|
+
const visibleHeight = Math.max(0, visibleBottom - visibleTop);
|
|
19816
|
+
const visibilityRatio = visibleHeight / elementHeight;
|
|
19540
19817
|
const eventName = element.getAttribute('data-track-scroll');
|
|
19541
|
-
if (!eventName)
|
|
19818
|
+
if (!eventName) {
|
|
19819
|
+
if (this.config.debug) {
|
|
19820
|
+
console.log(`[⚠️ AutoTracker] Element missing data-track-scroll attribute:`, element.id || 'no-id');
|
|
19821
|
+
}
|
|
19542
19822
|
return;
|
|
19543
|
-
|
|
19544
|
-
this.trackEvent(eventName, {
|
|
19545
|
-
type: 'scroll',
|
|
19546
|
-
element: element.tagName.toLowerCase(),
|
|
19547
|
-
threshold,
|
|
19548
|
-
scrollDepth: window.scrollY,
|
|
19549
|
-
visibilityRatio: Math.round(visibilityRatio * 100) / 100,
|
|
19550
|
-
...metadata,
|
|
19551
|
-
});
|
|
19823
|
+
}
|
|
19552
19824
|
if (this.config.debug) {
|
|
19553
|
-
console.log(`[AutoTracker]
|
|
19825
|
+
console.log(`[📊 AutoTracker] Enhanced scroll check for "${eventName}":`, {
|
|
19826
|
+
elementId: element.id || element.className || 'no-id',
|
|
19827
|
+
rect: {
|
|
19828
|
+
top: Math.round(rect.top),
|
|
19829
|
+
bottom: Math.round(rect.bottom),
|
|
19830
|
+
height: Math.round(elementHeight)
|
|
19831
|
+
},
|
|
19832
|
+
window: {
|
|
19833
|
+
height: windowHeight,
|
|
19834
|
+
scrollY: window.scrollY
|
|
19835
|
+
},
|
|
19836
|
+
visibility: {
|
|
19837
|
+
visibleTop,
|
|
19838
|
+
visibleBottom,
|
|
19839
|
+
visibleHeight,
|
|
19840
|
+
ratio: Math.round(visibilityRatio * 1000) / 1000,
|
|
19841
|
+
threshold,
|
|
19842
|
+
triggered: hasTriggered,
|
|
19843
|
+
shouldTrigger: visibilityRatio >= threshold
|
|
19844
|
+
}
|
|
19845
|
+
});
|
|
19846
|
+
}
|
|
19847
|
+
if (visibilityRatio >= threshold) {
|
|
19848
|
+
// Mark as triggered to prevent duplicate events
|
|
19849
|
+
element.setAttribute('data-scroll-triggered', 'true');
|
|
19850
|
+
const metadata = this.extractMetadata(element);
|
|
19851
|
+
const eventData = {
|
|
19852
|
+
type: 'scroll',
|
|
19853
|
+
element: element.tagName.toLowerCase(),
|
|
19554
19854
|
threshold,
|
|
19555
|
-
visibilityRatio,
|
|
19556
19855
|
scrollDepth: window.scrollY,
|
|
19557
|
-
|
|
19856
|
+
visibilityRatio: Math.round(visibilityRatio * 1000) / 1000,
|
|
19857
|
+
enhanced: true,
|
|
19858
|
+
...metadata,
|
|
19859
|
+
};
|
|
19860
|
+
if (this.config.debug) {
|
|
19861
|
+
console.log(`[🎯 AutoTracker] TRIGGERING scroll event "${eventName}":`, eventData);
|
|
19862
|
+
}
|
|
19863
|
+
this.trackEvent(eventName, eventData);
|
|
19864
|
+
if (this.config.debug) {
|
|
19865
|
+
console.log(`[✅ AutoTracker] Scroll event "${eventName}" sent successfully`);
|
|
19866
|
+
}
|
|
19867
|
+
}
|
|
19868
|
+
}
|
|
19869
|
+
catch (error) {
|
|
19870
|
+
if (this.config.debug) {
|
|
19871
|
+
console.error('[❌ AutoTracker] Scroll element processing error:', error, {
|
|
19872
|
+
element: element.id || element.className || 'no-id',
|
|
19873
|
+
tagName: element.tagName
|
|
19558
19874
|
});
|
|
19559
19875
|
}
|
|
19560
19876
|
}
|
|
19561
19877
|
}
|
|
19878
|
+
/**
|
|
19879
|
+
* Legacy method for compatibility
|
|
19880
|
+
*/
|
|
19881
|
+
processScrollElement(element) {
|
|
19882
|
+
this.processScrollElementEnhanced(element);
|
|
19883
|
+
}
|
|
19562
19884
|
/**
|
|
19563
19885
|
* Setup intelligent mutation observer (debounced for performance)
|
|
19564
19886
|
*/
|
|
@@ -19849,43 +20171,90 @@ class AutoTracker {
|
|
|
19849
20171
|
/**
|
|
19850
20172
|
* Enviar evento para o SDK
|
|
19851
20173
|
*/
|
|
20174
|
+
/**
|
|
20175
|
+
* Enhanced event tracking with comprehensive debugging
|
|
20176
|
+
*/
|
|
19852
20177
|
trackEvent(eventName, metadata) {
|
|
20178
|
+
const trackingStart = performance.now();
|
|
19853
20179
|
if (this.config.debug) {
|
|
19854
|
-
console.log("[AutoTracker] trackEvent called:", {
|
|
19855
|
-
|
|
19856
|
-
|
|
19857
|
-
|
|
19858
|
-
|
|
19859
|
-
}
|
|
19860
|
-
return;
|
|
20180
|
+
console.log("[🚀 AutoTracker] Enhanced trackEvent called:", {
|
|
20181
|
+
eventName,
|
|
20182
|
+
metadata,
|
|
20183
|
+
timestamp: new Date().toISOString()
|
|
20184
|
+
});
|
|
19861
20185
|
}
|
|
19862
|
-
|
|
20186
|
+
// Comprehensive validation
|
|
20187
|
+
const validation = this.validateTrackingCall(eventName, metadata);
|
|
20188
|
+
if (!validation.isValid) {
|
|
19863
20189
|
if (this.config.debug) {
|
|
19864
|
-
console.error("[AutoTracker]
|
|
20190
|
+
console.error("[❌ AutoTracker] Validation failed:", validation.errors);
|
|
19865
20191
|
}
|
|
19866
20192
|
return;
|
|
19867
20193
|
}
|
|
19868
20194
|
try {
|
|
19869
20195
|
const eventData = {
|
|
20196
|
+
// Core tracking data
|
|
19870
20197
|
autoTracked: true,
|
|
20198
|
+
enhanced: true,
|
|
19871
20199
|
timestamp: Date.now(),
|
|
19872
20200
|
url: window.location.href,
|
|
20201
|
+
userAgent: navigator.userAgent,
|
|
20202
|
+
// Performance data
|
|
20203
|
+
trackingLatency: Math.round((performance.now() - trackingStart) * 100) / 100,
|
|
20204
|
+
// Environment data
|
|
20205
|
+
isReactApp: this.diagnostics.environment?.reactDetected,
|
|
20206
|
+
isMobile: this.diagnostics.environment?.isMobile,
|
|
20207
|
+
// Event metadata
|
|
19873
20208
|
...metadata,
|
|
19874
20209
|
};
|
|
19875
20210
|
if (this.config.debug) {
|
|
19876
|
-
console.log("[AutoTracker]
|
|
20211
|
+
console.log("[📡 AutoTracker] Sending enhanced event:", { eventName, eventData });
|
|
19877
20212
|
}
|
|
19878
20213
|
const result = this.sdkInstance.trackCustomEvent(eventName, eventData);
|
|
19879
20214
|
if (this.config.debug) {
|
|
19880
|
-
|
|
20215
|
+
const trackingEnd = performance.now();
|
|
20216
|
+
console.log("[✅ AutoTracker] Event sent successfully:", {
|
|
20217
|
+
eventName,
|
|
20218
|
+
result,
|
|
20219
|
+
totalLatency: Math.round((trackingEnd - trackingStart) * 100) / 100 + 'ms'
|
|
20220
|
+
});
|
|
19881
20221
|
}
|
|
19882
20222
|
}
|
|
19883
20223
|
catch (error) {
|
|
19884
20224
|
if (this.config.debug) {
|
|
19885
|
-
console.error("[AutoTracker]
|
|
20225
|
+
console.error("[💥 AutoTracker] Critical tracking error:", {
|
|
20226
|
+
eventName,
|
|
20227
|
+
error: error instanceof Error ? error.message : error,
|
|
20228
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
20229
|
+
metadata
|
|
20230
|
+
});
|
|
19886
20231
|
}
|
|
19887
20232
|
}
|
|
19888
20233
|
}
|
|
20234
|
+
/**
|
|
20235
|
+
* Validate tracking call before sending
|
|
20236
|
+
*/
|
|
20237
|
+
validateTrackingCall(eventName, metadata) {
|
|
20238
|
+
const errors = [];
|
|
20239
|
+
if (!eventName || typeof eventName !== 'string') {
|
|
20240
|
+
errors.push('Event name is required and must be a string');
|
|
20241
|
+
}
|
|
20242
|
+
if (!this.sdkInstance) {
|
|
20243
|
+
errors.push('SDK instance is null or undefined');
|
|
20244
|
+
}
|
|
20245
|
+
if (this.sdkInstance && typeof this.sdkInstance.trackCustomEvent !== 'function') {
|
|
20246
|
+
errors.push(`trackCustomEvent is not a function: ${typeof this.sdkInstance.trackCustomEvent}`);
|
|
20247
|
+
}
|
|
20248
|
+
if (!window || typeof window !== 'object') {
|
|
20249
|
+
errors.push('Window object is not available');
|
|
20250
|
+
}
|
|
20251
|
+
return {
|
|
20252
|
+
isValid: errors.length === 0,
|
|
20253
|
+
errors
|
|
20254
|
+
};
|
|
20255
|
+
}
|
|
20256
|
+
// Legacy handlers removed - now using enhanced event delegation system
|
|
20257
|
+
// All tracking is now handled by createEnhancedScrollHandler and createReactCompatibleClickHandler
|
|
19889
20258
|
/**
|
|
19890
20259
|
* Modern API: Refresh tracking for dynamic content (React/SPA support)
|
|
19891
20260
|
* Industry best practice for SPA route changes
|
|
@@ -19915,26 +20284,83 @@ class AutoTracker {
|
|
|
19915
20284
|
this.refreshTracking();
|
|
19916
20285
|
}
|
|
19917
20286
|
/**
|
|
19918
|
-
* Get diagnostic information for debugging
|
|
20287
|
+
* Get comprehensive diagnostic information for debugging
|
|
19919
20288
|
*/
|
|
19920
20289
|
getDiagnostics() {
|
|
19921
20290
|
return {
|
|
19922
|
-
|
|
19923
|
-
|
|
19924
|
-
|
|
19925
|
-
|
|
19926
|
-
|
|
19927
|
-
|
|
20291
|
+
// Initialization status
|
|
20292
|
+
initialization: {
|
|
20293
|
+
isInitialized: this.isInitialized,
|
|
20294
|
+
attempts: this.initializationAttempts,
|
|
20295
|
+
maxAttempts: this.maxInitializationAttempts,
|
|
20296
|
+
timestamp: new Date().toISOString()
|
|
20297
|
+
},
|
|
20298
|
+
// Event handlers status
|
|
20299
|
+
eventHandlers: {
|
|
20300
|
+
hasClickHandler: this.delegationHandlers.has('click'),
|
|
20301
|
+
hasScrollHandler: this.delegationHandlers.has('scroll'),
|
|
20302
|
+
activeHandlers: Array.from(this.delegationHandlers.keys()),
|
|
20303
|
+
delegationTarget: this.getOptimalDelegationTarget() === document ? 'document' : 'react-root'
|
|
20304
|
+
},
|
|
20305
|
+
// Observers status
|
|
20306
|
+
observers: {
|
|
20307
|
+
observedElements: this.observedElements.size,
|
|
20308
|
+
hasMutationObserver: !!this.mutationObserver,
|
|
20309
|
+
hasIntersectionObserver: !!this.intersectionObserver,
|
|
20310
|
+
scrollElementsCache: !!this.cachedScrollElements,
|
|
20311
|
+
cacheExpiry: new Date(this.lastScrollElementsCheck + 5000).toISOString()
|
|
20312
|
+
},
|
|
20313
|
+
// Configuration
|
|
19928
20314
|
config: this.config,
|
|
19929
|
-
|
|
19930
|
-
|
|
19931
|
-
|
|
19932
|
-
|
|
19933
|
-
|
|
19934
|
-
|
|
19935
|
-
|
|
20315
|
+
// Current environment diagnostics
|
|
20316
|
+
environment: this.diagnostics.environment || {},
|
|
20317
|
+
// DOM state
|
|
20318
|
+
dom: this.diagnostics.dom || {},
|
|
20319
|
+
// SDK state
|
|
20320
|
+
tracking: this.diagnostics.tracking || {},
|
|
20321
|
+
// Real-time element detection
|
|
20322
|
+
elementsFound: this.getElementCounts(),
|
|
20323
|
+
// Performance metrics
|
|
20324
|
+
performance: {
|
|
20325
|
+
lastScrollThrottle: this.scrollThrottle,
|
|
20326
|
+
cacheLastUpdate: this.lastScrollElementsCheck
|
|
20327
|
+
},
|
|
20328
|
+
// Validation status
|
|
20329
|
+
validation: this.validateTrackingCall('test', {}),
|
|
20330
|
+
// Debug recommendations
|
|
20331
|
+
recommendations: this.generateRecommendations()
|
|
19936
20332
|
};
|
|
19937
20333
|
}
|
|
20334
|
+
/**
|
|
20335
|
+
* Generate diagnostic recommendations
|
|
20336
|
+
*/
|
|
20337
|
+
generateRecommendations() {
|
|
20338
|
+
const recommendations = [];
|
|
20339
|
+
const elementsFound = this.getElementCounts();
|
|
20340
|
+
const totalElements = Object.values(elementsFound).reduce((sum, count) => sum + count, 0);
|
|
20341
|
+
if (!this.isInitialized) {
|
|
20342
|
+
recommendations.push('AutoTracker is not initialized. Check console for errors.');
|
|
20343
|
+
}
|
|
20344
|
+
if (totalElements === 0) {
|
|
20345
|
+
recommendations.push('No trackable elements found. Ensure data-track-* attributes are present in the DOM.');
|
|
20346
|
+
}
|
|
20347
|
+
if (!this.sdkInstance) {
|
|
20348
|
+
recommendations.push('SDK instance is missing. Ensure the SDK is properly initialized.');
|
|
20349
|
+
}
|
|
20350
|
+
if (this.sdkInstance && typeof this.sdkInstance.trackCustomEvent !== 'function') {
|
|
20351
|
+
recommendations.push('trackCustomEvent method is not available on SDK instance.');
|
|
20352
|
+
}
|
|
20353
|
+
if (!this.diagnostics.environment?.reactDetected && window.location.hostname.includes('localhost')) {
|
|
20354
|
+
recommendations.push('React not detected. If using React, ensure proper setup and try refreshTracking() after route changes.');
|
|
20355
|
+
}
|
|
20356
|
+
if (this.diagnostics.environment?.isMobile && !this.delegationHandlers.has('scroll')) {
|
|
20357
|
+
recommendations.push('Mobile device detected but scroll tracking not active. Check scroll tracking configuration.');
|
|
20358
|
+
}
|
|
20359
|
+
if (this.initializationAttempts > 1) {
|
|
20360
|
+
recommendations.push(`AutoTracker required ${this.initializationAttempts} initialization attempts. Consider adding delay before enabling.`);
|
|
20361
|
+
}
|
|
20362
|
+
return recommendations;
|
|
20363
|
+
}
|
|
19938
20364
|
/**
|
|
19939
20365
|
* Configurar tracking
|
|
19940
20366
|
*/
|
|
@@ -19954,6 +20380,33 @@ class AutoTracker {
|
|
|
19954
20380
|
}
|
|
19955
20381
|
}
|
|
19956
20382
|
|
|
20383
|
+
/**
|
|
20384
|
+
* Centralized Session Utilities
|
|
20385
|
+
* Standardized session ID generation for consistency across SDK and backend
|
|
20386
|
+
*/
|
|
20387
|
+
/**
|
|
20388
|
+
* Generate standardized session ID
|
|
20389
|
+
* Format: visitorId_YYYYMMDD_HH
|
|
20390
|
+
* Example: f697a6e1-1aae-48db-bc6a-afc1d2c01852_20251231_03
|
|
20391
|
+
*/
|
|
20392
|
+
function generateSessionId(visitorId, date) {
|
|
20393
|
+
const sessionDate = date || new Date();
|
|
20394
|
+
// Format: YYYYMMDD
|
|
20395
|
+
const year = sessionDate.getFullYear();
|
|
20396
|
+
const month = (sessionDate.getMonth() + 1).toString().padStart(2, '0');
|
|
20397
|
+
const day = sessionDate.getDate().toString().padStart(2, '0');
|
|
20398
|
+
const dateStr = `${year}${month}${day}`;
|
|
20399
|
+
// Format: HH (24-hour format)
|
|
20400
|
+
const hour = sessionDate.getHours().toString().padStart(2, '0');
|
|
20401
|
+
return `${visitorId}_${dateStr}_${hour}`;
|
|
20402
|
+
}
|
|
20403
|
+
/**
|
|
20404
|
+
* Generate session ID for current time
|
|
20405
|
+
*/
|
|
20406
|
+
function generateCurrentSessionId(visitorId) {
|
|
20407
|
+
return generateSessionId(visitorId, new Date());
|
|
20408
|
+
}
|
|
20409
|
+
|
|
19957
20410
|
/**
|
|
19958
20411
|
* Visitor Persistence Manager for SDK
|
|
19959
20412
|
* Implements client-side visitor identification with localStorage camouflage
|
|
@@ -20113,9 +20566,23 @@ class VisitorIdentityManager {
|
|
|
20113
20566
|
// REMOVED: generateVisitorId() and generateVisitorIdFromHash() methods
|
|
20114
20567
|
// SDK no longer generates visitor IDs - only backend does
|
|
20115
20568
|
generateSessionId() {
|
|
20116
|
-
|
|
20117
|
-
|
|
20118
|
-
|
|
20569
|
+
// Try to get existing visitor identity from storage
|
|
20570
|
+
const { value: storedData } = PersistenceManager.get(STORAGE_KEYS.prefs);
|
|
20571
|
+
if (storedData) {
|
|
20572
|
+
try {
|
|
20573
|
+
const parsed = JSON.parse(storedData);
|
|
20574
|
+
if (parsed._v && parsed._vb === true) {
|
|
20575
|
+
// We have a valid visitor ID from backend
|
|
20576
|
+
return generateCurrentSessionId(parsed._v);
|
|
20577
|
+
}
|
|
20578
|
+
}
|
|
20579
|
+
catch (e) {
|
|
20580
|
+
// Invalid data, continue to fallback
|
|
20581
|
+
}
|
|
20582
|
+
}
|
|
20583
|
+
// Fallback for cases where visitor ID is not available yet
|
|
20584
|
+
const tempVisitorId = "temp-" + Date.now().toString(36);
|
|
20585
|
+
return generateCurrentSessionId(tempVisitorId);
|
|
20119
20586
|
}
|
|
20120
20587
|
createCamouflageData(visitorId, sessionId, stableCoreHash, fromBackend = false) {
|
|
20121
20588
|
return {
|
|
@@ -20125,7 +20592,7 @@ class VisitorIdentityManager {
|
|
|
20125
20592
|
analytics: true,
|
|
20126
20593
|
cookies: true,
|
|
20127
20594
|
_v: visitorId, // Hidden visitor ID
|
|
20128
|
-
_s: sessionId, // Hidden session ID
|
|
20595
|
+
_s: sessionId, // Hidden session ID
|
|
20129
20596
|
_sc: stableCoreHash, // Hidden stable core
|
|
20130
20597
|
_vb: fromBackend ? true : undefined, // Flag: visitor ID from backend (v1.7.0+)
|
|
20131
20598
|
ts: Date.now(),
|
|
@@ -20169,7 +20636,9 @@ class VisitorIdentityManager {
|
|
|
20169
20636
|
const { value: storedData, method } = PersistenceManager.get(STORAGE_KEYS.prefs);
|
|
20170
20637
|
if (storedData) {
|
|
20171
20638
|
const existingIdentity = this.extractFromCamouflage(storedData);
|
|
20172
|
-
if (existingIdentity &&
|
|
20639
|
+
if (existingIdentity &&
|
|
20640
|
+
existingIdentity.stableCoreHash === stableCoreHash &&
|
|
20641
|
+
existingIdentity.visitorId) {
|
|
20173
20642
|
// Update last seen time only if we have a valid visitor ID from backend
|
|
20174
20643
|
const updatedCamouflage = this.createCamouflageData(existingIdentity.visitorId, existingIdentity.sessionId, stableCoreHash);
|
|
20175
20644
|
PersistenceManager.set(STORAGE_KEYS.prefs, JSON.stringify(updatedCamouflage));
|
|
@@ -20189,7 +20658,7 @@ class VisitorIdentityManager {
|
|
|
20189
20658
|
stableCoreHash,
|
|
20190
20659
|
deviceFingerprint: params.deviceFingerprint,
|
|
20191
20660
|
persistenceMethod: "memory", // Start with memory, upgrade after backend response
|
|
20192
|
-
confidence: 0.
|
|
20661
|
+
confidence: 0.9,
|
|
20193
20662
|
createdAt: Date.now(),
|
|
20194
20663
|
lastSeen: Date.now(),
|
|
20195
20664
|
reused: false,
|
|
@@ -20204,7 +20673,12 @@ class VisitorIdentityManager {
|
|
|
20204
20673
|
async updateVisitorIdFromBackend(visitorId, stableCoreHash, sessionId) {
|
|
20205
20674
|
try {
|
|
20206
20675
|
// Create or update camouflaged data with backend visitor ID
|
|
20207
|
-
|
|
20676
|
+
// Regenerate session ID with proper visitor ID if needed
|
|
20677
|
+
let currentSessionId = sessionId || this.getCurrentSessionId();
|
|
20678
|
+
// If we have a temp session ID or no session ID, generate a new one with the proper visitor ID
|
|
20679
|
+
if (!currentSessionId || currentSessionId.includes("temp-")) {
|
|
20680
|
+
currentSessionId = generateCurrentSessionId(visitorId);
|
|
20681
|
+
}
|
|
20208
20682
|
const camouflageData = this.createCamouflageData(visitorId, currentSessionId, stableCoreHash, true // Mark as coming from backend
|
|
20209
20683
|
);
|
|
20210
20684
|
const { success, method } = PersistenceManager.set(STORAGE_KEYS.prefs, JSON.stringify(camouflageData));
|
|
@@ -20216,7 +20690,7 @@ class VisitorIdentityManager {
|
|
|
20216
20690
|
return false;
|
|
20217
20691
|
}
|
|
20218
20692
|
catch (error) {
|
|
20219
|
-
console.error(
|
|
20693
|
+
console.error("[VisitorIdentityManager] Failed to update visitor ID from backend:", error);
|
|
20220
20694
|
return false;
|
|
20221
20695
|
}
|
|
20222
20696
|
}
|
|
@@ -20670,36 +21144,17 @@ class ZaplierSDK {
|
|
|
20670
21144
|
}
|
|
20671
21145
|
}
|
|
20672
21146
|
/**
|
|
20673
|
-
* Generate session ID
|
|
21147
|
+
* Generate session ID using standardized format
|
|
20674
21148
|
*/
|
|
20675
21149
|
generateSessionId() {
|
|
20676
|
-
//
|
|
20677
|
-
|
|
20678
|
-
|
|
20679
|
-
// Use page load time + browser fingerprint for session uniqueness within same page load
|
|
20680
|
-
return pageLoadTime.toString(36) + browserFingerprint.substring(0, 8);
|
|
20681
|
-
}
|
|
20682
|
-
/**
|
|
20683
|
-
* Get stable browser characteristics for session ID generation
|
|
20684
|
-
* Uses only stable, privacy-safe values that don't change during session
|
|
20685
|
-
*/
|
|
20686
|
-
getBrowserFingerprint() {
|
|
20687
|
-
const components = [
|
|
20688
|
-
navigator.userAgent || "",
|
|
20689
|
-
navigator.platform || "",
|
|
20690
|
-
screen.width || 0,
|
|
20691
|
-
screen.height || 0,
|
|
20692
|
-
new Date(2024, 0, 1).getTimezoneOffset(),
|
|
20693
|
-
navigator.language || "",
|
|
20694
|
-
navigator.hardwareConcurrency || 0,
|
|
20695
|
-
];
|
|
20696
|
-
const combined = components.join("|");
|
|
20697
|
-
let hash = 0;
|
|
20698
|
-
for (let i = 0; i < combined.length; i++) {
|
|
20699
|
-
const char = combined.charCodeAt(i);
|
|
20700
|
-
hash = ((hash << 5) - hash + char) & 0xffffffff;
|
|
21150
|
+
// Use centralized session generation with current visitor ID
|
|
21151
|
+
if (this.backendVisitorId) {
|
|
21152
|
+
return generateCurrentSessionId(this.backendVisitorId);
|
|
20701
21153
|
}
|
|
20702
|
-
|
|
21154
|
+
// Fallback for cases where backendVisitorId is not available yet
|
|
21155
|
+
// This should be rare and will be updated when visitor ID is available
|
|
21156
|
+
const tempVisitorId = this.visitorId || 'temp-' + Date.now().toString(36);
|
|
21157
|
+
return generateCurrentSessionId(tempVisitorId);
|
|
20703
21158
|
}
|
|
20704
21159
|
/**
|
|
20705
21160
|
* Initialize Anti-Adblock Manager
|
|
@@ -20958,6 +21413,10 @@ class ZaplierSDK {
|
|
|
20958
21413
|
if (response.sessionId) {
|
|
20959
21414
|
this.sessionId = response.sessionId;
|
|
20960
21415
|
}
|
|
21416
|
+
else if (this.backendVisitorId && (!this.sessionId || this.sessionId.includes('temp-'))) {
|
|
21417
|
+
// Generate new session ID now that we have the proper visitor ID
|
|
21418
|
+
this.sessionId = generateCurrentSessionId(this.backendVisitorId);
|
|
21419
|
+
}
|
|
20961
21420
|
return response;
|
|
20962
21421
|
}
|
|
20963
21422
|
catch (error) {
|