1/** 2 * Lightweight session analytics tracker for Paddle the Ohio. 3 * Usage: PTO.Analytics.track('event_type', { optional: 'data' }) 4 */ 5window.PTO = window.PTO || {}; 6 7PTO.Analytics = (function() { 8 var sessionId = ''; 9 var buffer = []; 10 var flushInterval = null; 11 var metadata = {}; 12 13 function generateSessionId() { 14 var arr = new Uint8Array(16); 15 crypto.getRandomValues(arr); 16 return Array.from(arr, function(b) { return b.toString(16).padStart(2, '0'); }).join(''); 17 } 18 19 function getDeviceType() { 20 var w = window.innerWidth; 21 if (w < 768) return 'mobile'; 22 if (w <= 1024) return 'tablet'; 23 return 'desktop'; 24 } 25 26 function getUTMParams() { 27 var params = new URLSearchParams(window.location.search); 28 return { 29 utm_source: params.get('utm_source') || null, 30 utm_medium: params.get('utm_medium') || null, 31 utm_campaign: params.get('utm_campaign') || null 32 }; 33 } 34 35 function buildPayload() { 36 var payload = { 37 sid: sessionId, 38 ref: document.referrer || null, 39 device: metadata.device, 40 sw: window.innerWidth, 41 ua: navigator.userAgent, 42 events: buffer.splice(0) 43 }; 44 var utm = metadata.utm; 45 if (utm.utm_source) payload.utm_source = utm.utm_source; 46 if (utm.utm_medium) payload.utm_medium = utm.utm_medium; 47 if (utm.utm_campaign) payload.utm_campaign = utm.utm_campaign; 48 return payload; 49 } 50 51 function flush() { 52 if (buffer.length === 0) return; 53 var payload = buildPayload(); 54 var json = JSON.stringify(payload); 55 56 if (navigator.sendBeacon) { 57 var blob = new Blob([json], { type: 'application/json' }); 58 navigator.sendBeacon('api/track.php', blob); 59 } else { 60 fetch('api/track.php', { 61 method: 'POST', 62 headers: { 'Content-Type': 'application/json' }, 63 body: json, 64 keepalive: true 65 }).catch(function() {}); 66 } 67 } 68 69 function track(eventType, eventData) { 70 buffer.push({ 71 t: eventType, 72 d: eventData || null, 73 ts: Math.floor(Date.now() / 1000) 74 }); 75 } 76 77 function init() { 78 sessionId = generateSessionId(); 79 metadata.device = getDeviceType(); 80 metadata.utm = getUTMParams(); 81 82 track('page_view'); 83 84 flushInterval = setInterval(flush, 15000); 85 86 document.addEventListener('visibilitychange', function() { 87 if (document.visibilityState === 'hidden') flush(); 88 }); 89 window.addEventListener('pagehide', flush); 90 } 91 92 return { 93 init: init, 94 track: track 95 }; 96})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.