1(function () { 2 'use strict'; 3 4 var QUEUE_KEY = 'pwa_analytics_queue'; 5 var ANON_KEY = 'pwa_analytics_anon_id'; 6 var MAX_BATCH = 50; 7 var FLUSH_INTERVAL_MS = 5000; 8 var endpoint = '/Pwa/TrackEvents'; 9 var flushTimer = null; 10 var memoryQueue = []; 11 12 function safeStorageGet(key) { 13 try { 14 return localStorage.getItem(key); 15 } catch (e) { 16 return null; 17 } 18 } 19 20 function safeStorageSet(key, value) { 21 try { 22 localStorage.setItem(key, value); 23 } catch (e) { } 24 } 25 26 function getAnonymousId() { 27 var existing = safeStorageGet(ANON_KEY); 28 if (existing) { 29 return existing; 30 } 31 32 var id = 'anon-' + Math.random().toString(36).slice(2, 10); 33 safeStorageSet(ANON_KEY, id); 34 return id; 35 } 36 37 function normalizeProps(props) { 38 var source = props || {}; 39 return { 40 anonymousId: source.anonymousId || source.anonymous_id || '', 41 outletId: source.outletId || source.outlet_id || '', 42 promptType: source.promptType || source.prompt_type || '', 43 permissionStatus: source.permissionStatus || source.permission_status || '', 44 result: source.result || '', 45 failureReason: source.failureReason || source.failure_reason || '', 46 tableId: source.tableId || source.table_id || '', 47 orderingSessionId: source.orderingSessionId || source.ordering_session_id || '', 48 mismatchType: source.mismatchType || source.mismatch_type || '', 49 entryPoint: source.entryPoint || source.entry_point || '' 50 }; 51 } 52 53 function buildEvent(eventName, props) { 54 var normalized = normalizeProps(props); 55 return { 56 eventId: (typeof crypto !== 'undefined' && crypto.randomUUID) 57 ? crypto.randomUUID() 58 : ('evt-' + Date.now() + '-' + Math.random().toString(36).slice(2, 10)), 59 eventName: eventName, 60 eventTimestamp: new Date().toISOString(), 61 anonymousId: normalized.anonymousId || getAnonymousId(), 62 outletId: normalized.outletId, 63 promptType: normalized.promptType, 64 permissionStatus: normalized.permissionStatus, 65 result: normalized.result, 66 failureReason: normalized.failureReason, 67 tableId: normalized.tableId, 68 orderingSessionId: normalized.orderingSessionId, 69 mismatchType: normalized.mismatchType, 70 entryPoint: normalized.entryPoint 71 }; 72 } 73 74 function readPersistedQueue() { 75 try { 76 var raw = sessionStorage.getItem(QUEUE_KEY); 77 if (!raw) { 78 return []; 79 } 80 var parsed = JSON.parse(raw); 81 return Array.isArray(parsed) ? parsed : []; 82 } catch (e) { 83 return []; 84 } 85 } 86 87 function writePersistedQueue(items) { 88 try { 89 if (!items || items.length === 0) { 90 sessionStorage.removeItem(QUEUE_KEY); 91 return; 92 } 93 sessionStorage.setItem(QUEUE_KEY, JSON.stringify(items)); 94 } catch (e) { } 95 } 96 97 function enqueue(event) { 98 memoryQueue.push(event); 99 if (memoryQueue.length >= MAX_BATCH) { 100 flushQueue(false); 101 return; 102 } 103 104 writePersistedQueue(memoryQueue); 105 scheduleFlush(); 106 } 107 108 function scheduleFlush() { 109 if (flushTimer) { 110 return; 111 } 112 flushTimer = setTimeout(function () { 113 flushTimer = null; 114 flushQueue(false); 115 }, FLUSH_INTERVAL_MS); 116 } 117 118 function postEvents(events, useBeacon) { 119 if (!events || events.length === 0) { 120 return true; 121 } 122 123 var payload = JSON.stringify({ events: events }); 124 var url = endpoint; 125 126 if (useBeacon && navigator.sendBeacon) { 127 try { 128 var blob = new Blob([payload], { type: 'application/json' }); 129 return navigator.sendBeacon(url, blob); 130 } catch (e) { 131 // fall through to fetch 132 } 133 } 134 135 if (window.fetch) { 136 window.fetch(url, { 137 method: 'POST', 138 headers: { 'Content-Type': 'application/json' }, 139 body: payload, 140 credentials: 'same-origin', 141 keepalive: true 142 }).catch(function () { 143 writePersistedQueue(events); 144 }); 145 return true; 146 } 147 148 if (window.jQuery) { 149 window.jQuery.ajax({ 150 type: 'POST', 151 url: url, 152 contentType: 'application/json', 153 data: payload 154 }); 155 return true; 156 } 157 158 writePersistedQueue(events); 159 return false; 160 } 161 162 function flushQueue(useBeacon) { 163 var persisted = readPersistedQueue(); 164 var batch = persisted.concat(memoryQueue); 165 memoryQueue = []; 166 167 if (batch.length === 0) { 168 writePersistedQueue([]); 169 return; 170 } 171 172 while (batch.length > 0) { 173 var chunk = batch.slice(0, MAX_BATCH); 174 batch = batch.slice(MAX_BATCH); 175 postEvents(chunk, useBeacon); 176 } 177 178 writePersistedQueue(batch); 179 } 180 181 function trackPwaEvent(eventName, props) { 182 if (!eventName) { 183 return; 184 } 185 enqueue(buildEvent(eventName, props || {})); 186 } 187 188 window.trackPwaEvent = trackPwaEvent; 189 190 memoryQueue = readPersistedQueue(); 191 if (memoryQueue.length > 0) { 192 scheduleFlush();
193 } 194 195 window.addEventListener('pagehide', function () { 196 flushQueue(true); 197 }); 198 199 document.addEventListener('visibilitychange', function () { 200 if (document.visibilityState === 'hidden') { 201 flushQueue(true); 202 } 203 }); 204})();
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.