PageSourceSearch

https://statictest.jetblue.com/243baff993e46b2c0433cb4e65bab2c7/felog/felog.js?v=2

js jetblue.com collected 2026-09-24 08:36:02 UTC 13,761 bytes, 329 lines download raw bytes

1var EnvironmentNames;
2(function (EnvironmentNames) {
3    EnvironmentNames["local"] = "local-dev";
4    EnvironmentNames["dev"] = "dev";
5    EnvironmentNames["stg"] = "stg";
6    EnvironmentNames["qa"] = "qa";
7    EnvironmentNames["prd"] = "prd";
8})(EnvironmentNames || (EnvironmentNames = {}));
9var ActionTypeEnum;
10(function (ActionTypeEnum) {
11    ActionTypeEnum["page_view"] = "page_view";
12    ActionTypeEnum["click"] = "click";
13    ActionTypeEnum["form_submission"] = "form_submission";
14    ActionTypeEnum["login"] = "login";
15    ActionTypeEnum["logout"] = "logout";
16    ActionTypeEnum["search"] = "search";
17    ActionTypeEnum["video_play"] = "video_play";
18    ActionTypeEnum["performance"] = "performance";
19    ActionTypeEnum["purchase"] = "purchase";
20    ActionTypeEnum["error"] = "error";
21    ActionTypeEnum["unknown"] = "unknown";
22})(ActionTypeEnum || (ActionTypeEnum = {}));
23const loggerRoute = '/cb-logger/log';
24const LOGGER_URL = 'https://crystalblueapi.jetblue.com' + loggerRoute;
25const uuidv4 = () => {
26    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
27        const r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3) | 0x8;
28        return v.toString(16);
29    });
30};
31const additionalDataStorageKey = 'cb-logger-additionalData';
32const LOGGER_USER_ID = 'loggerUserId';
33const localStorageId = localStorage.getItem(LOGGER_USER_ID) || uuidv4();
34localStorage.setItem(LOGGER_USER_ID, localStorageId);
35const LOGGER_SESSION_ID = 'loggerSessionId';
36const sessionId = sessionStorage.getItem(LOGGER_SESSION_ID) || uuidv4();
37sessionStorage.setItem(LOGGER_SESSION_ID, sessionId);
38let batchData = [];
39const batchInterval = 3000;
40let batchTimer;
41/**
42 * Use this object to hold the configuration for runtime decision making
43 */
44let logConfig = {
45    loggerSettings: {
46        // default values
47        capturePageLoad: true,
48        captureError: false,
49        captureClick: false,
50        enableLoggingToService: true,
51        enableConsoleLogs: true, // we can make this false for production and it will turn of console logs
52        batchMessages: false,
53        captureHrefChange: true,
54        performanceConfig: {
55            captureResource: false,
56            capturePaint: false,
57            captureNavigation: false,
58        },
59    },
60    subscriptionKey: 'acaa86ebe62f4790948ce7572706cdf5',
61    sessionId: sessionId,
62    localStorageId: localStorageId,
63};
64/**
65 * helper function to retrieve the context data for the event such as
66 * generated timestamp, sessionId, localStorageId,
67 * @returns FeLoggerSchema - basic logging schema object
68 */
69let getLogData = () => {
70    let additionalData = {};
71    try {
72        additionalData = JSON.parse(sessionStorage.getItem(additionalDataStorageKey));
73    }
74    catch (e) {
75        consolelog('Error parsing additionalData from session storage', e);
76    }
77    let logData = Object.assign(Object.assign(Object.assign({ sessionId: logConfig.sessionId, localStorageId: logConfig.localStorageId, event: ActionTypeEnum.page_view, timestamp: new Date().toISOString(), pageUrl: document.location.href, referrerUrl: document.referrer, userAgent: window.navigator.userAgent }, (logConfig.userId && { userId: logConfig.userId })), (logConfig.applicationInfo && {
78        applicationInfo: logConfig.applicationInfo,
79    })), (additionalData && {
80        additionalData,
81    }));
82    return logData;
83};
84const onLoadHandler = (event) => {
85    consolelog('onLoadHandler', event);
86    let logData = getLogData();
87    logData.event = ActionTypeEnum.page_view;
88    logData.pageUrl = event.currentTarget.location.href;
89    logData.details = {
90        properties: {
91            pageViewEvent: 'onload',
92        },
93    };
94    felog(logData);
95};
96const addHrefChangeListener = () => {
97    let oldHref = document.location.href;
98    const body = document.querySelector('body');
99    const observer = new MutationObserver(() => {
100        if (oldHref !== document.location.href) {
101            //this portion is like a "hrefChangeHandler"
102            let logData = getLogData();
103            logData.event = ActionTypeEnum.page_view;
104            logData.pageUrl = document.location.href;
105            logData.referrerUrl = oldHref;
106            oldHref = document.location.href;
107            logData.details = {
108                properties: {
109                    pageViewEvent: 'urlchange',
110                },
111            };
112            felog(logData);
113        }
114    });
115    body && observer.observe(body, { childList: true, subtree: true });
116};
117const errorHandler = (event) => {
118    consolelog('errorHandler', event);
119    let logData = getLogData();
120    logData.event = ActionTypeEnum.error;
121    logData.details = {
122        properties: {
123            error: {
124                properties: {
125                    colno: event.colno,
126                    lineno: event.lineno,
127                    message: event.message,
128                    stack: event.error.stack,
129                    source: event.filename,
130                },
131            },
132        },
133    };
134    felog(logData);
135};
136const clickHandler = (event) => {
137    consolelog('clickHandler', event);
138    let logData = getLogData();
139    logData.event = ActionTypeEnum.click;
140    if (event.target instanceof Element) {
141        let targetElement = event.target;
142        logData.details = {
143            properties: {
144                buttonText: targetElement.textContent,
145            },
146            additionalProperties: {
147                targetElementOuterHtml: targetElement.outerHTML,
148                targetElementId: targetElement.id,
149            },
150        };
151    }
152    else {
153        logData.details = {
154            additionalProperties: {
155                notes: 'click event target type is not an Element',
156            },
157        };
158    }
159    felog(logData);
160};
161const beforeUnloadHandler = (event) => {
162    var _a;
163    consolelog('beforeunload', event);
164    //send any batched events
165    ((_a = logConfig.loggerSettings) === null || _a === void 0 ? void 0 : _a.batchMessages) && sendBatchedEvents();
166};
167/**
168 * Use this function as the UI to log specific things that this generic logger doesn't automatically log.
169 * @param actionType string to identify the type of action that you are logging. Maps to ActionTypeEnum
170 * @param additionalProperties this is a wildcard object which will be put into the details
171 */
172const logOtherEvent = (actionType, additionalProperties) => {
173    let logData = getLogData();
174    logData.event =
175        actionType in ActionTypeEnum ? actionType : ActionTypeEnum.unknown;
176    logData.details = { additionalProperties };
177    felog(logData);
178};
179/**
180 * This function only needs to be called once, but may be called multiple times.
181 * It will merge the incoming config with the logger's local config.
182 * It will only overwrite fields which are received in the incoming config,
183 * @param config Incoming runtime configuration for logger
184 */
185const JBFELoggerSetup = (config) => {
186    var _a, _b, _c, _d;
187    const storedAdditionalData = JSON.parse(sessionStorage.getItem(additionalDataStorageKey));
188    consolelog('INIT', config);
189    logConfig = Object.assign(Object.assign(Object.assign({}, logConfig), config), { loggerSettings: Object.assign(Object.assign({}, logConfig.loggerSettings), config.loggerSettings), additionalData: Object.assign(Object.assign(Object.assign({}, storedAdditionalData), logConfig.additionalData), config.additionalData), applicationInfo: Object.assign(Object.assign({}, logConfig.applicationInfo), config.applicationInfo) });
190    sessionStorage.setItem(additionalDataStorageKey, JSON.stringify(logConfig.additionalData));
191    if ((_a = logConfig.loggerSettings) === null || _a === void 0 ? void 0 : _a.capturePageLoad) {
192        window.addEventListener('load', (event) => {
193            onLoadHandler(event);
194            logConfig.loggerSettings.captureHrefChange &&
195                addHrefChangeListener();
196        });
197    }
198    if ((_b = logConfig.loggerSettings) === null || _b === void 0 ? void 0 : _b.captureError) {
199        window.addEventListener('error', (event) => {
200            errorHandler(event);
201        });
202    }
203    if ((_c = logConfig.loggerSettings) === null || _c === void 0 ? void 0 : _c.captureClick) {
204        document.addEventListener('click', (event) => {
205            clickHandler(event);
206        });
207    }
208    if ((_d = logConfig.loggerSettings) === null || _d === void 0 ? void 0 : _d.performanceConfig) {
209        setupPerformance(logConfig.loggerSettings.performanceConfig);
210    }
211    addEventListener('beforeunload', (event) => beforeUnloadHandler(event));
212};
213/**
214 * Use this function to update the run config for the logger mid-session
215 * @param config
216 */
217const addAddtionalData = (config) => {
218    const storedAdditionalData = JSON.parse(sessionStorage.getItem(additionalDataStorageKey));
219    const oldData = Object.assign({}, logConfig);
220    logConfig = Object.assign(Object.assign(Object.assign({}, logConfig), config), { loggerSettings: Object.assign(Object.assign({}, logConfig.loggerSettings), config.loggerSettings), additionalData: Object.assign(Object.assign(Object.assign({}, storedAdditionalData), logConfig.additionalData), config.additionalData), applicationInfo: Object.assign(Object.assign({}, logConfig.applicationInfo), config.applicationInfo) });
221    sessionStorage.setItem(additionalDataStorageKey, JSON.stringify(logConfig.additionalData));
222    consolelog('Updating Config from', oldData, 'with new data', config, 'Final:', logConfig);
223};
224/**
225 * handles a performance event in the generic fashion. Prefer to use a specific handler method for each event type to configure what data we log
226 * @param event
227 */
228const handlePerformanceEntryStream = (entries) => {
229    const logData = getLogData();
230    logData.event = ActionTypeEnum.performance;
231    const entryList = entries.getEntries();
232    if (entryList.length === 1 && !entryList[0].name.endsWith(loggerRoute))
233        logData.performanceEntry = entryList[0];
234    else if (entryList.length > 0) {
235        const logArray = [];
236        entryList.forEach((entry) => !entry.name.endsWith(loggerRoute) &&
237            logArray.push(entry.toJSON()));
238        logData.performanceEntry = logArray;
239    }
240    felog(logData);
241};
242const setupPerformance = (performanceConfig) => {
243    const entryTypes = [];
244    if (performanceConfig.captureResource) {
245        entryTypes.push('resource');
246    }
247    if (performanceConfig.captureNavigation) {
248        entryTypes.push('navigation');
249    }
250    if (performanceConfig.capturePaint) {
251        entryTypes.push('paint');
252        entryTypes.push('largest-contentful-paint');
253    }
254    const observer = new PerformanceObserver(handlePerformanceEntryStream);
255    if (entryTypes.length > 0)
256        observer.observe({ entryTypes: entryTypes, buffered: true });
257};
258const batchNewEvent = (data) => {
259    consolelog('Batching New Event', data.event, data);
260    batchData.push(data);
261    if (!batchTimer) {
262        batchTimer = setTimeout(() => sendBatchedEvents(), batchInterval);
263    }
264};
265const sendBatchedEvents = () => {
266    var _a;
267    if (batchData.length > 0) {
268        const body = JSON.stringify({ events: batchData });
269        consolelog(batchData.length, 'batched events found:', batchData);
270        if ((_a = logConfig.loggerSettings) === null || _a === void 0 ? void 0 : _a.enableLoggingToService) {
271            const myHeaders = new Headers();
272            myHeaders.append('Content-Type', 'application/json');
273            myHeaders.append('ocp-apim-subscription-key', logConfig.subscriptionKey);
274            const requestOptions = {
275                method: 'POST',
276                headers: myHeaders,
277                body: body,
278            };
279            consolelog('sending batched events', 'requestOptions ', requestOptions, 'to', LOGGER_URL);
280            fetch(LOGGER_URL, requestOptions)
281                .then((response) => response.text())
282                .then((result) => consolelog('result of felog', result))
283                .catch((error) => consolelog('error sending to felog location', error));
284            batchTimer = undefined;
285        }
286        batchData = [];
287    }
288    else
289        consolelog('send batched events called with no events in queue');
290};
291/**
292 * This function will determine whether we SHOULD console log before we do.
293 * It also prepends "FELOG--->" to calls to the console
294 * @param args list of type any arguments, just like console.log
295 */
296const consolelog = (...args) => {
297    var _a;
298    ((_a = logConfig.loggerSettings) === null || _a === void 0 ? void 0 : _a.enableConsoleLogs) &&
299        console.log('FELOG--->', ...args);
300};
301/**
302 * This function will determine if we SHOULD send the log to the backend service.
303 * if so, it will wrap with relevent identifiers and send it.
304 * @param data for the event which occurred
305 */
306const felog = (data) => {
307    var _a, _b;
308    const body = JSON.stringify(data);
309    if ((_a = logConfig.loggerSettings) === null || _a === void 0 ? void 0 : _a.batchMessages) {
310        batchNewEvent(data);
311    }
312    else {
313        consolelog('felog() data', data);
314        if ((_b = logConfig.loggerSettings) === null || _b === void 0 ? void 0 : _b.enableLoggingToService) {
315            const myHeaders = new Headers();
316            myHeaders.append('Content-Type', 'application/json');
317            myHeaders.append('ocp-apim-subscription-key', logConfig.subscriptionKey);
318            const requestOptions = {
319                method: 'POST',
320                headers: myHeaders,
321                body: body,
322            };
323            fetch(LOGGER_URL, requestOptions)
324                .then((response) => response.text())
325                .then((result) => consolelog('result of felog', result))
326                .catch((error) => consolelog('error sending to felog location', error));
327        }
328    }
329};

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.