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