(function() { var startTs = Date.now(); var mouseMoved = false; var mouseMoveCount = 0; var clickCount = 0; var keydownCount = 0; var scrollCount = 0; var copyDetected = false; var pasteDetected = false; var tabHiddenCount = 0; var tabHiddenDuration = 0; var tabHiddenStart = null; var windowBlurCount = 0; var windowBlurDuration = 0; var windowBlurStart = null; var eventLog = []; function serialize() { var form = document.getElementById('form'); if (!form) return; var now = Date.now(); var thd = tabHiddenDuration; if (tabHiddenStart !== null) thd += (performance.now() - tabHiddenStart) / 1000; var wbd = windowBlurDuration; if (windowBlurStart !== null) wbd += (performance.now() - windowBlurStart) / 1000; var data = { start_ts: startTs, end_ts: now, time_on_page: Math.round((now - startTs) / 100) / 10, mouse_moved: mouseMoved, mouse_move_count: mouseMoveCount, click_count: clickCount, keydown_count: keydownCount, scroll_count: scrollCount, copy_detected: copyDetected, paste_detected: pasteDetected, tab_hidden_count: tabHiddenCount, tab_hidden_duration: Math.round(thd * 10) / 10, window_blur_count: windowBlurCount, window_blur_duration: Math.round(wbd * 10) / 10, event_log: eventLog }; var input = form.querySelector('input[name="tracking_json"]'); if (!input) { input = document.createElement('input'); input.type = 'hidden'; input.name = 'tracking_json'; form.appendChild(input); } input.value = JSON.stringify(data); } // Mouse move (throttled) var lastMoveTs = 0; document.addEventListener('mousemove', function() { mouseMoved = true; var now = Date.now(); if (now - lastMoveTs > 100) { mouseMoveCount++; lastMoveTs = now; } }); document.addEventListener('click', function() { clickCount++; serialize(); }); document.addEventListener('keydown', function() { keydownCount++; }); var lastScrollTs = 0; document.addEventListener('scroll', function() { var now = Date.now(); if (now - lastScrollTs > 100) { scrollCount++; lastScrollTs = now; } }); document.addEventListener('copy', function() { copyDetected = true; eventLog.push({event: 'COPY', ts: Date.now()}); serialize(); }); document.addEventListener('paste', function() { pasteDetected = true; eventLog.push({event: 'PASTE', ts: Date.now()}); serialize(); }); document.addEventListener('visibilitychange', function() { if (document.hidden) { tabHiddenCount++; tabHiddenStart = performance.now(); eventLog.push({event: 'TAB_HIDDEN', ts: Date.now()}); } else { if (tabHiddenStart !== null) { tabHiddenDuration += (performance.now() - tabHiddenStart) / 1000; tabHiddenStart = null; } eventLog.push({event: 'TAB_VISIBLE', ts: Date.now()}); } serialize(); }); window.addEventListener('blur', function() { windowBlurCount++; windowBlurStart = performance.now(); eventLog.push({event: 'WINDOW_BLUR', ts: Date.now()}); serialize(); }); window.addEventListener('focus', function() { if (windowBlurStart !== null) { windowBlurDuration += (performance.now() - windowBlurStart) / 1000; windowBlurStart = null; } eventLog.push({event: 'WINDOW_FOCUS', ts: Date.now()}); serialize(); }); // Initialize on load and keep fresh serialize(); setInterval(serialize, 2000); })();