PageSourceSearch

https://chinlee.com.sg/Scripts/pwa-analytics.js?v=111.20261006-129

js chinlee.com.sg collected 2026-10-08 07:22:09 UTC 6,185 bytes, 204 lines download raw bytes

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.