1(() => { 2 if (window.__wardenAnalytics || navigator.doNotTrack === '1' || navigator.globalPrivacyControl) return; 3 const endpoint = document.currentScript?.dataset.endpoint; 4 if (!endpoint) return; 5 window.__wardenAnalytics = true; 6 const uuid = () => crypto.randomUUID ? crypto.randomUUID() : Array.from(crypto.getRandomValues(new Uint8Array(16)), v => v.toString(16).padStart(2, '0')).join(''); 7 const read = (key) => { try { return JSON.parse(localStorage.getItem(key)); } catch { return null; } }; 8 const write = (key, value) => { try { localStorage.setItem(key, JSON.stringify(value)); } catch {} }; 9 let identity = read('warden.analytics.visitor'); 10 if (!identity || identity.expires < Date.now() || !/^[a-f0-9-]{32,36}$/.test(identity.id)) { 11 identity = { id: uuid(), expires: Date.now() + 30 * 86400000 }; 12 write('warden.analytics.visitor', identity); 13 } 14 let fallbackSession = null; 15 const session = () => { 16 let current = read('warden.analytics.session') || fallbackSession; 17 if (!current || current.expires < Date.now()) current = { id: uuid() }; 18 current.expires = Date.now() + 1800000; 19 write('warden.analytics.session', current); 20 fallbackSession = current; 21 return current.id; 22 }; 23 let path = ''; 24 const send = (type) => { 25 if (document.visibilityState !== 'visible' || navigator.doNotTrack === '1' || navigator.globalPrivacyControl) return; 26 const changed = path !== location.pathname; 27 if (changed) path = location.pathname; 28 const body = JSON.stringify({ type: changed ? 'view' : type, id: uuid(), visitor: identity.id, session: session(), path, referrer: document.referrer ? new URL(document.referrer).origin : '' }); 29 fetch(endpoint, { method: 'POST', body, credentials: 'omit', keepalive: true, headers: { 'Content-Type': 'text/plain' } }).catch(() => {}); 30 }; 31 send('view'); 32 setInterval(() => send('heartbeat'), 30000); 33 document.addEventListener('visibilitychange', () => send('heartbeat')); 34 window.addEventListener('popstate', () => send('heartbeat')); 35 for (const name of ['pushState', 'replaceState']) { 36 const original = history[name]; 37 history[name] = function (...args) { 38 const result = original.apply(this, args); 39 if (path !== location.pathname) send('heartbeat'); 40 return result; 41 }; 42 } 43})();
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.