const loggableElements = [ 'a', 'button', 'form', 'input', 'select', 'textarea', ]; const globals = new Map(); const MAX_SESSION_IDLE_TIME = 10 * 60 * 1000; // 10 minutes const MAX_SESSION_AGE = 4 * 60 * 60 * 1000; // 4 hours export function debugLog(...args: any[]) { if ((window as any)?.statsig_wa?.debug) { console.log(...args); } } export function gatherEventData( target: Element ): { value: string, metadata: Map } { const tagName = target.tagName.toLowerCase(); const metadata = new Map(); const value = tagName; if (tagName === 'form') { metadata.set('action', target.getAttribute('action')); metadata.set('method', target.getAttribute('method') || 'GET'); metadata.set('formName', target.getAttribute('name')); metadata.set('formId', target.getAttribute('id')); } if (['input', 'select', 'textarea'].includes(tagName)) { if (target.getAttribute('type') !== 'password') { metadata.set('content', (target as HTMLInputElement).value); metadata.set('inputName', target.getAttribute('name')); } } const anchor = getAnchorNodeInHierarchy(target); if (anchor) { metadata.set('href', anchor.getAttribute('href')); } if (tagName === 'button' || anchor) { metadata.set('content', (target.textContent || '').trim()); const dataset = getDatasetProperties(anchor || target); dataset.forEach((value, key) => { metadata.set(key, value); }); } return { value, metadata }; } function getDatasetProperties(el: Element) { const dataset = (el as any).dataset; const map = new Map(); if (dataset) { for (const key in dataset) { map.set(`data-${key}`, dataset[key]); } } return map; } export function getAnchorNodeInHierarchy( node: Element | null, ): Element | null { if (!node) { return null; } let parent: Element | null = node; while (parent) { const parentTagName = parent.tagName.toLowerCase(); if (['body', 'document'].includes(parentTagName)) { return null; } if (parent.tagName.toLowerCase() === 'a') { return parent; } parent = parent.parentElement; } return null; } export function getTargetNode(e: Event): Element | null { if (!e) { return null; } let target: any = e.target || e.srcElement; if (!target) { return null; } if (target.nodeType === 3) { target = (target.parentNode || null) as Element | null; } return target; } export function shouldLogEvent(e: Event, el: Element): boolean { if (!e || !el || el.nodeType !== 1) { return false; } const tagName = el.tagName.toLowerCase(); const eventType = e.type.toLowerCase(); switch (tagName) { case 'html': return false; case 'form': return eventType === 'submit'; case 'input': case 'select': case 'textarea': return ['change'].includes(eventType); default: if (eventType === 'click') { if (tagName === 'button') { return true; } const anchor = getAnchorNodeInHierarchy(el); if (anchor) { return true; } } return false; } } export function setLocalValue(key: string, value: string) { if (window && window.localStorage) { window.localStorage.setItem(key, value); } else { globals.set(key, value); console.error('Statsig Web AutoCapture: No window.localStorage'); } } export function getLocalValue(key: string) { if (window && window.localStorage) { return window.localStorage.getItem(key); } return globals.get(key); } export function getRandomId() { if (crypto && crypto.randomUUID) { return crypto.randomUUID(); } const s = () => Math.floor(Math.random() * 0x10000).toString(16).padStart(4, '0'); return `${s()}${s()}-${s()}-4${s().substring(1)}-${s()}-${s()}${s()}${s()}`; } export function getStableID() { const key = 'STATSIG_LOCAL_STORAGE_STABLE_ID'; let sid = getLocalValue(key); if (!sid) { sid = getRandomId(); setLocalValue(key, sid); } return sid; } export function getPreExistingUser() { let statsigInstance = (window as any)?.statsig?.instance; if (!statsigInstance) { statsigInstance = (window as any)?.Statsig?.instance; } if (statsigInstance && statsigInstance.getCurrentUser) { return statsigInstance.getCurrentUser(); } return null; } export function getStatsigUser() { let fragment = getPreExistingUser(); if (!fragment) { const sid = getStableID(); fragment = { customIDs: { stableID: sid, sessionID: getSessionId(), }, } } const userOverride = (window as any)?.statsigUser || {}; return { ...fragment, ...userOverride, custom: { ...fragment.custom, ...userOverride.custom, url: window?.location?.href, page_url: window?.location?.href, language: window?.navigator?.language, useragent: window?.navigator?.userAgent, }, }; } export function createNewSession() { const now = Date.now(); return { id: getRandomId(), startTime: now, lastAccessedTime: now, }; } export function getSafeUrl(): URL { const href = window?.location?.href || ''; let url: URL; try { url = new URL(href); } catch (e) { url = new URL('error:') } return url; } export function getSessionId() { const key = 'STATSIG_LOCAL_STORAGE_SESSION'; let sessionJSON = getLocalValue(key); let session: any = null; const now = Date.now(); try { session = JSON.parse(sessionJSON ?? 'null'); } catch (e) { } if ( !session || !session.lastAccessedTime || !session.startTime || now - session.startTime > MAX_SESSION_AGE || now - session.lastAccessedTime > MAX_SESSION_IDLE_TIME ) { session = createNewSession(); } session.lastAccessed = now; setLocalValue(key, JSON.stringify(session)); return session.id; } export function getSanitizedPageUrl(): string { const url = window?.location?.href?.split(/[?#]/)[0]; return url || ''; }