PageSourceSearch

https://app.visitortracking.com/assets/js/tracer.js

js visitortracking.com collected 2026-09-28 05:51:41 UTC 43,006 bytes, 1,004 lines download raw bytes

1var Tracer = function (config) {
2    // Call Initialization on Tracer Call
3    this.init(config);
4};
5
6
7
8Tracer.prototype = {
9
10    // Initialization
11    init: async function (config) {
12
13        var tracer = this;
14        tracer.baseUrlEndpoint = 'https://app.visitortracking.com/api/vtservice/v1/';
15        tracer.enabled = true;
16        tracer.checkSessionValidaity();
17
18        if (Object.keys(config).length === 0) {
19            tracer.enabled = false;
20            alert("Please set websiteId in config.")
21            tracer.log("no config provided. using default..", '');
22        }
23        else if (!config.hasOwnProperty('websiteId')) {
24            tracer.enabled = false;
25            alert("Please set websiteId in config.")
26        }
27        else if (config.hasOwnProperty('websiteId')
28            && (config.websiteId === null || config.websiteId === undefined || config.websiteId === '')) {
29            tracer.enabled = false;
30            alert("Please set websiteId in config.")
31        }
32        tracer.log('calling')
33        await tracer.getTraceStatus(config.websiteId);
34
35        // Argument Assignment          // Type Checks    
36        tracer.websiteId = typeof (config.websiteId) == "string" ? config.websiteId : '',                                                           // Default Values
37            tracer.events = Array.isArray(config.Events) === true ? config.Events : ['mouseup', 'touchend', 'keydown', 'scroll', 'mousemove', 'focus'], //
38            tracer.endpoint = 'visitor/trace',
39            tracer.async = typeof (config.async) == "boolean" ? config.async : true,
40            tracer.debug = typeof (config.debug) == "boolean" ? config.debug : true,
41            tracer.trace = { session: {}, visitor: {}, conversions: null },
42
43            tracer.timeStamp = new Date(),
44            tracer.navigationProcessTimer = 2, //in seconds
45            tracer.navigationInterval = 0,
46            tracer.sessionIdleTimer = 0;
47        tracer.sessionInterval = 30, //in minutes
48            tracer.idleTimer = 0,
49            tracer.idleInterval = 5, //in minutes
50
51            tracer.prevUrl = window.location.href, //undefined
52            tracer.currUrl = window.location.href; //undefined
53        tracer.isRedirect = false;
54        tracer.sendEventConversion = false;
55        tracer.inactiveTime = getLocalStorageNumber("inactiveTime", 0);
56        tracer.lastActiveTime = getLocalStorageNumber("lastActiveTime", Date.now());
57        tracer.idealTimeDuration = 15000; // in milliseconds
58
59        // Trace only if trace is true
60        if (tracer.enabled === true) {
61            tracer.resetIdealTime()
62            tracer.resetSession()
63            tracer.log('inside')
64            // Initialize Visitor
65            tracer.initVisitor();
66
67            // Call Event Binding Method
68            tracer.bindEvents();
69
70            tracer.updateIdealTime();
71
72            // Send trace
73            tracer.sendTrace();
74
75            // Navigation Change
76            tracer.navigationChange();
77        }
78
79        return tracer;
80    },
81
82    // Generate Session Object & Assign to Session Property
83    initVisitor: function () {
84        var tracer = this;
85
86        // Assign Visitor Property
87        tracer.trace.visitor = {
88            externalReferenceId: tracer.websiteId,
89            pseudoId: tracer.visitorId(),
90            pseudoName: tracer.visitorName(),
91        }
92
93        // Initialize Session
94        tracer.initSession();
95
96        return tracer;
97    },
98
99    // Generate Session Object & Assign to Session Property
100    initSession: function () {
101        var tracer = this;
102
103        // Assign Session Property
104        tracer.trace.session = {
105            websiteId: 0,
106            jSessionId: tracer.sessionId(),
107            browser: tracer.browserName(),
108            device: tracer.isMobile() ? 'Mobile' : tracer.isMobileTablet() ? 'Tablet' : 'Desktop',
109            operatingSystem: tracer.osName(),
110            source: document.referrer && document.referrer !== "" 
111                    ? document.referrer 
112                    : tracer.getAppSource(),
113            resolution: (window.screen.width * window.devicePixelRatio + "x" + window.screen.height * window.devicePixelRatio),
114            isActive: true,
115            page: {
116                pageUrl: window.location.href,
117                pageTitle: document.title,
118                jSessionId: tracer.sessionId(),
119                interaction: null,
120                scrollPercentage: tracer.scrollDepth,
121            },
122            endpoint: tracer.endpoint
123        };
124
125        //Init Conversion
126        tracer.initConversionData(tracer.conversions);
127
128        //Set conversion
129        tracer.setConversionData(window.location.href);
130
131        return tracer;
132    },
133
134    // Create Events to Track
135    bindEvents: function () {
136        var tracer = this;
137        // Set Event Capture
138        if (document.readyState !== 'loading') {
139            tracer.attachEvents();
140        } else {
141            document.addEventListener("DOMContentLoaded", function () {
142                tracer.attachEvents();
143            });
144        }
145
146        window.addEventListener('beforeunload', (event) => {
147            if (tracer.enabled == true && (tracer.currUrl == tracer.prevUrl)) {
148
149                //calculate ideal time.
150                tracer.updateIdealTime();
151                localStorage.setItem("lastActiveTime", tracer.lastActiveTime);
152                tracer.trace.session.inactiveTime = tracer.inactiveTime;
153
154                if (navigator.sendBeacon) {
155                    tracer.trace.session.isActive = false;
156                    tracer.resetSession();
157                    tracer.resetIdealTime();
158
159                    //Send trace
160                    //const blob = new Blob([JSON.stringify(tracer.trace)], {type : 'application/json'});
161                    let data = new FormData();
162                    data.append('data', JSON.stringify(tracer.trace));
163                    navigator.sendBeacon(tracer.baseUrlEndpoint + tracer.endpoint + '1', data);
164                }
165            }
166        });
167
168        return tracer;
169    },
170
171    attachEvents() {
172        var tracer = this;
173        tracer.events.forEach(function (e) {
174            document.querySelector('body').addEventListener(e, function (event) {
175
176                //calculate ideal time.
177                tracer.updateIdealTime();
178
179                //If session is inactive then initilize session 
180                if (!tracer.trace.session.isActive) {
181                    tracer.initVisitor();
182                }
183
184                //check session time and reset if passed
185                tracer.timeStamp = new Date();
186                tracer.setEventConversion(e, event);
187                if (e === "mouseup" || e === "touchend") {
188                    if (event.target.nodeName === 'BUTTON') {
189                        tracer.isRedirect = true;
190                        //tracer.addEventTrace(event);
191                    }
192                    else if (event.target.nodeName === 'A') {
193                        tracer.isRedirect = true;
194                        //tracer.addEventTrace(event);
195                    }
196                }
197                else if (e === "keydown") {
198
199                }
200                else if (e === "scroll") {
201
202                }
203                else if (e === "mousemove") {
204                    tracer.resetSession();
205                    tracer.resetIdealTime();
206                }
207                else if (e === "focus") {
208                    tracer.resetSession();
209                    tracer.resetIdealTime();
210                }
211            });
212        });
213    },
214
215    updateIdealTime() {
216        var tracer = this;
217        const now = Date.now();
218        if (tracer.firstTrace) {
219          tracer.lastActiveTime = now;
220        }
221        const timeDifference = now - tracer.lastActiveTime;
222        if (timeDifference > tracer.idealTimeDuration) {
223          tracer.inactiveTime += timeDifference;
224          localStorage.setItem("inactiveTime", tracer.inactiveTime);
225        }
226        tracer.lastActiveTime = now;
227      },
228
229    // Add Interaction Object Triggered By Events to Records Array
230    addEventTrace: function (e) {
231
232        var tracer = this,
233
234            // UI-Interaction Object
235            interaction = {
236                event: e.type,
237                targetTag: e.target?.nodeName,
238                targetId: e.target?.id,
239                targetClasses: e.target?.className,
240                content: e.target?.innerText,
241                clientPosition: {
242                    x: e.clientX,
243                    y: e.clientY
244                },
245                screenPosition: {
246                    x: e.screenX,
247                    y: e.screenY
248                },
249                createdDate: new Date()
250            };
251
252        // Insert into Records Array
253        //tracer.records.push(interaction);
254
255        tracer.log('addEventTrace', tracer.trace);
256
257        if (tracer.trace && tracer.trace.session && tracer.trace.session.page) {
258            tracer.trace.session.page.interaction = interaction;
259
260            // Send data
261            tracer.sendTrace();
262        }
263
264        return tracer;
265    },
266
267    navigationChange: function () {
268        var tracer = this;
269
270        clearInterval(tracer.navigationInterval); //reset the previous timer
271        tracer.navigationInterval = setInterval(function () {
272
273            tracer.currUrl = window.location.href;
274            if (tracer.currUrl != tracer.prevUrl) {
275                tracer.isRedirect = false;
276                // URL changed
277                tracer.prevUrl = tracer.currUrl;
278
279                // Log currUrl if Debugging
280                tracer.log('URL changed to', tracer.currUrl);
281
282                //Initilize page when page is changed
283                if (tracer.trace && tracer.trace.session) {
284                    tracer.trace.session.page = {
285                        pageUrl: window.location.href,
286                        pageTitle: document.title,
287                        jSessionId: tracer.sessionId(),
288                        interaction: null
289                    };
290                }
291
292                //Set conversion
293                tracer.setConversionData(window.location.href);
294
295                // Send data
296                tracer.sendTrace();
297            }
298        }, tracer.navigationProcessTimer * 1000);
299
300        return tracer;
301    },
302
303    resetIdealTime: function () {
304        var tracer = this;
305
306        clearTimeout(tracer.idleTimer); //reset the previous timer
307        tracer.idleTimer = setTimeout(function () { //set another one right away
308
309            tracer.log('Ideal timer', tracer.trace);
310
311            tracer.trace.session.isActive = false;
312
313            tracer.updateIdealTime();
314
315            //Send trace
316            tracer.sendTrace();
317
318        }, tracer.idleInterval * 60 * 1000); //idleInterval minutes
319
320        return tracer;
321    },
322
323    resetSession: function () {
324        var tracer = this;
325
326        clearTimeout(tracer.sessionIdleTimer); //reset the previous timer
327        tracer.sessionIdleTimer = setTimeout(async function () { //set another one right away
328
329            tracer.log('closeSession', tracer.trace);
330
331            tracer.trace.session.isActive = false;
332
333            tracer.updateIdealTime();
334
335            //Send trace
336            tracer.sendTrace();
337
338            localStorage.removeItem("VTSessionId");
339            localStorage.removeItem("VTSessionTime");
340            localStorage.removeItem("VTConversion");
341            localStorage.removeItem("inactiveTime");
342            localStorage.removeItem("lastActiveTime");
343            tracer.trace.conversions = [];
344            tracer.conversions = []
345            await tracer.getTraceStatus(tracer.websiteId);
346            tracer.inactiveTime = 0;
347            tracer.lastActiveTime = Date.now();
348            tracer.firstTrace = true;
349
350        }, tracer.sessionInterval * 60 * 1000); //sessionInterval minutes
351
352        return tracer;
353    },
354
355    sendTrace: async function () {
356        var tracer = this;
357
358        tracer.inactiveTime = getLocalStorageNumber("inactiveTime", 
3580);
359        tracer.trace.session.inactiveTime = getLocalStorageNumber("inactiveTime", 0);
360        if(tracer.firstTrace){
361            tracer.firstTrace = false;
362          }
363
364        if (tracer.enabled === true) {
365            tracer.log('sendTrace new', tracer.trace);
366            if (tracer.isEmptyObject(tracer.trace.conversions)) {
367                tracer.trace.conversions = [];
368            }
369
370            try {
371                const response = await fetch(tracer.baseUrlEndpoint + tracer.endpoint, {
372                    method: 'POST',
373                    headers: {
374                        'Content-Type': 'application/json; charset=UTF-8'
375                    },
376                    body: JSON.stringify(tracer.trace)
377                });
378
379                if (!response.ok) {
380                    throw new Error('Network response was not ok.');
381                }
382
383                const data = await response.json();
384                if (data && data.hasOwnProperty('trace')) {
385                    tracer.enabled = data.trace;
386                }
387            } catch (error) {
388                tracer.log('sendTrace error', error);
389            }
390        }
391
392        return tracer;
393    },
394
395    getTraceStatus: async function (websiteId) {
396        var tracer = this;
397
398        var url = tracer.baseUrlEndpoint + 'website/getsite?id=' + websiteId;
399
400        await fetch(url, {
401            method: 'GET',
402            headers: {
403                'Content-Type': 'application/json; charset=UTF-8'
404            },
405            // Setting the 'credentials' option to 'same-origin' can help with certain types of CORS issues
406            credentials: 'same-origin'
407        })
408            .then(function (response) {
409                if (response.ok) {
410                    return response.json();
411                } else {
412                    throw new Error('Network response was not ok.');
413                }
414            })
415            .then(function (data) {
416                if (data && data.hasOwnProperty('trace')) {
417                    tracer.enabled = data.trace.isActive;
418                    tracer.conversions = data.trace.conversions;
419                }
420            })
421            .catch(function (error) {
422                tracer.enabled = false;
423                tracer.log('sendTrace error', error);
424            });
425
426        return tracer;
427    },
428
429
430    setEventConversion: function (e, event) {
431        var tracer = this;
432        var _conversionData = tracer.getConversionData();
433        if (_conversionData != null) {
434            _conversionData.forEach(element => {
435                //Process Events here
436                var _pageUrl = window.location.href;
437                if (element.type == 'Event') {
438                    var elemSelector = tracer.generateQuerySelector(event.target);
439                    if (e === "mouseup" || e === "touchend" && element.condition == 'click/press' || (e === "keydown" && event.key === "Enter" && element.condition == "click/press")) {
440
441                        const cleanedSelector = trimDotFromEnd(element.eventSelector);
442                        const matchedElement = event.target.closest(cleanedSelector);
443
444                        let clickedHref = null;
445                        let clickedText = null;
446
447                        if (matchedElement) {
448                        const tag = matchedElement.tagName.toLowerCase();
449
450                        if (tag === "a" && matchedElement.href) {
451                            clickedHref = matchedElement.href.replace(/^https?:\/\/[^\/]+/i, "").toLowerCase();
452                            if (!clickedHref.startsWith("/")) {
453                            clickedHref = "/" + clickedHref;
454                            }
455                        } else if (tag === "button") {
456                            clickedText = matchedElement.innerText?.trim().toLowerCase() || null;
457                        }
458                        }
459
460                        if (!element.isComplete && matchedElement) {
461                            const expectedHref = element.hrefLink
462                                ? (element.hrefLink.startsWith("/") ? element.hrefLink : "/" + element.hrefLink).toLowerCase()
463                                : null;
464
465                            const expectedText = element.targetText
466                                ? element.targetText.trim().toLowerCase()
467                                : null;
468
469                            const isOnExpectedPage = _pageUrl.toLowerCase().includes(element.pageUrl.toLowerCase());
470
471                            if (isOnExpectedPage) {
472                                const hrefMatches = expectedHref ? clickedHref === expectedHref : true;
473                                const textMatches = expectedText ? clickedText === expectedText : true;
474
475                                if (hrefMatches && textMatches) {
476                                element.isComplete = true;
477                                tracer.sendEventConversion = true;
478                                }
479                            }
480                        }
481                    }
482                    else if (e === "focus" && element.condition == 'focus') {
483                        if (element.isComplete != true) {
484                            if (event.target.closest(trimDotFromEnd(element.eventSelector)) && _pageUrl.toLowerCase().includes(element.pageUrl.toLowerCase())) {
485                                element.isComplete = true;
486                                tracer.sendEventConversion = true;
487                            }
488                        }
489                    }
490                    else if (e === "submit" && element.condition == 'submit') {
491                        if (element.isComplete != true) {
492                            if (event.target.closest(trimDotFromEnd(element.eventSelector)) && _pageUrl.toLowerCase().includes(element.pageUrl.toLowerCase())) {
493                                element.isComplete = true;
494                                tracer.sendEventConversion = true;
495                            }
496                        }
497                    }
498                }
499            });
500
501            //Set to localstorage after processing
502            localStorage.setItem("VTConversion", JSON.stringify(_conversionData));
503            tracer.trace.conversions = _conversionData;
504
505            if (tracer.sendEventConversion) {
506                tracer.addEventTrace(event);
507                tracer.sendEventConversion = false;
508            }
509        }
510    },
511
512    generateQuerySelector(el) {
513        if (el.tagName.toLowerCase() == "html")
514            return "HTML";
515        var str = el.tagName;
516        str += (el.id != "") ? "#" + el.id : "";
517        if (el.className) {
518            var classes = [];
519            if (typeof el.className == "string") {
520                classes = el.className.split(/\s/);
521                for (var i = 0; i < classes.length; i++) {
522                    if (classes[i] != null && classes[i] != '') {
523                        str += "." + classes[i]
524                    }
525                }
526            }
527        }
528        return str
529    },
530
531    browserName: function () {
532        var tracer = this;
533        var browserName = 'other';
534        (function (agent) {
535            switch (true) {
536                case tracer.isBrave(): browserName = "Brave"; break;
537                case agent.indexOf("yabrowser") > -1: browserName = "Yandex"; break;
538                case agent.indexOf("edge") > -1: browserName = "MS Edge"; break;
539                case agent.indexOf("edg/") > -1: browserName = "Edge ( chromium based)"; break;
540                case agent.indexOf("opr") > -1 && !!window.opr: browserName = "Opera"; break;
541                case agent.indexOf("chrome") > -1 && !!window.chrome: browserName = "Chrome"; break;
542                case agent.indexOf("trident") > -1: browserName = "MS IE"; break;
543                case agent.indexOf("firefox") > -1: browserName = "Mozilla Firefox"; break;
544                case agent.indexOf("safari") > -1: browserName = "Safari"; break;
545                default: break;
546            }
547        })(window.navigator.userAgent.toLowerCase());
548        return browserName;
549    },
550
551    osName: function () {
552        var userAgent = window.navigator.userAgent,
553            platform = window.navigator?.userAgentData?.platform || window.navigator?.platform,
554            macosPlatforms = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K', 'macOS'],
555            windowsPlatforms = ['Win32', 'Win64', 'Windows', 'WinCE'],
556            iosPlatforms = ['iPhone', 'iPad', 'iPod'],
557            os = 'unknown';
558
559        if (macosPlatforms.indexOf(platform) !== -1 || platform.toUpperCase().indexOf('MAC') >= 0) {
560            os = 'Mac OS';
561        } else if (iosPlatforms.indexOf(platform) !== -1) {
562            os = 'iOS';
563        } else if (windowsPlatforms.indexOf(platform) !== -1) {
564            os = 'Windows';
565        } else if (/Android/.test(userAgent)) {
566            os = 'Android';
567        } else if (/Linux/.test(platform)) {
568            os = 'Linux';
569        }
570
571        return os;
572    },
573
574    isMobile: function () {
575        var check = false;
576        (function (a) {
577            if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|
577sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4)))
578                check = true;
579        })(navigator.userAgent || navigator.vendor || window.opera);
580        return check;
581    },
582
583    isMobileTablet: function () {
584        var check = false;
585        (function (a) {
586            if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4)))
587                check = true;
588        })(navigator.userAgent || navigator.vendor || window.opera);
589        return check;
590    },
591    isBrave: function () {
592        if (window.navigator.brave != undefined) {
593            if (window.navigator.brave.isBrave.name == "isBrave") {
594                return true;
595            } else {
596                return false;
597            }
598        } else {
599            return false;
600        }
601    },
602    generateId: function (stringLength = 20) {
603        // Get current timestamp in milliseconds
604        const timestamp = new Date().getTime().toString();
605
606        // Generate random string
607        const randomString = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
608
609        // Combine timestamp and random string to create unique ID
610        const uniqueId = timestamp + randomString;
611
612        // Make sure ID is exactly 50 characters by truncating or padding with zeroes as needed
613        return uniqueId.padEnd(50, '0').substring(0, 50);
614    },
615
616    visitorId: function () {
617        var tracer = this;
618        if (localStorage.getItem("VTVisitiorId")) {
619            return localStorage.getItem("VTVisitiorId");
620        }
621        else {
622            let randomStr = tracer.generateId();
623            localStorage.setItem("VTVisitiorId", randomStr);
624
625            //if visitor id is created new create name too again
626            localStorage.removeItem("VTVisitiorName");
627
628            return randomStr;
629        }
630    },
631
632    visitorName: function () {
633        if (localStorage.getItem("VTVisitiorName")) {
634            return localStorage.getItem("VTVisitiorName");
635        }
636        else {
637            var adjs = ["Autumn", "Hidden", "Bitter", "Misty", "Silent", "Empty", "Dry",
638                "Dark", "Summer", "Icy", "Delicate", "Quiet", "White", "Cool", "Spring",
639                "Winter", "Patient", "Twilight", "Dawn", "Crimson", "Wispy", "Weathered",
640                "Blue", "Billowing", "Broken", "Cold", "Damp", "Falling", "Frosty", "Green",
641                "Long", "Late", "Lingering", "Bold", "Little", "Morning", "Muddy", "Old",
642                "Red", "Rough", "Still", "Small", "Sparkling", "Throbbing", "Shy",
643                "Wandering", "Withered", "Wild", "Black", "Young", "Holy", "Solitary",
644                "Fragrant", "Aged", "Snowy", "Proud", "Floral", "Restless", "Divine",
645                "Polished", "Ancient", "Purple", "Lively", "Nameless"]
646
647                , nouns = ["Waterfall", "River", "Breeze", "Moon", "Rain", "Wind", "Sea",
648                    "Morning", "Snow", "Lake", "Sunset", "Pine", "Shadow", "Leaf", "Dawn",
649                    "Glitter", "Forest", "Hill", "Cloud", "Meadow", "Sun", "Glade", "Bird",
650                    "Brook", "Butterfly", "Bbush", "Dew", "Dust", "Field", "Fire", "Flower",
651                    "Firefly", "Feather", "Grass", "Haze", "Mountain", "Night", "Pond",
652                    "Darkness", "Snowflake", "Silence", "Sound", "Sky", "Shape", "Surf",
653                    "Thunder", "Violet", "Water", "Wildflower", "Wave", "Water", "Resonance",
654                    "Sun", "Wood", "Dream", "Cherry", "Tree", "Fog", "Frost", "Voice", "Paper",
655                    "Frog", "Smoke", "Star"];
656
657            var visitorName = adjs[Math.floor(Math.random() * (adjs.length - 1))] + " " + nouns[Math.floor(Math.random() * (nouns.length - 1))];
658
659            localStorage.setItem("VTVisitiorName", visitorName);
660            return visitorName;
661        }
662    },
663
664    sessionId: function (stringLength = 20) {
665        var tracer = this;
666        if (localStorage.getItem("VTSessionId")) {
667            return localStorage.getItem("VTSessionId");
668        }
669        else {
670            localStorage.removeItem("VTConversion");
671            localStorage.removeItem("inactiveTime");
672            localStorage.removeItem("lastActiveTime");
673            this.initConversionData(tracer.trace.conversions);
674            let randomStr = tracer.generateId();
675            localStorage.setItem("VTSessionId", randomStr);
676            localStorage.setItem("VTSessionTime", new Date());
677            return randomStr;
678        }
679    },
680
681    minutesDiff: function (dateTimeValue2, dateTimeValue1) {
682        var differenceValue = (dateTimeValue2.getTime() - dateTimeValue1.getTime()) / 1000;
683        differenceValue /= 60;
684        return Math.abs(Math.round(differenceValue));
685    },
686
687    log: function (key, message) {
688        tracer = this;
689        // Log Interaction if Debugging
690        if (tracer.debug) {
691            // Log to Console
692            console.log(key + ":\n", message);
693        }
694    },
695
696    checkSessionValidaity: function () {
697        if (localStorage.getItem("VTSessionTime")) {
698            var VTSessionTime = Date.parse(localStorage.getItem("VTSessionTime"));
699            var duration = this.minutesDiff(new Date(), new Date(VTSessionTime));
700            if (30 <= duration) {
701                localStorage.removeItem("VTSessionId");
702                localStorage.removeItem("VTSessionTime");
703                localStorage.removeItem("inactiveTime");
704                localStorage.removeItem("lastActiveTime");
705            }
706        }
707        else {
708            localStorage.removeItem("VTSessionId");
709            localStorage.removeItem("VTSessionTime");
710            localStorage.removeItem("inactiveTime");
711            localStorage.removeItem("lastActiveTime");
712        }
713    },
714
715    initConversionData: function (conversionData) {
716        var tracer = this;
717        if (!tracer.isEmptyObject(conversionData)) {
718            tracer.trace.conversions = conversionData;
719            var localConversions = tracer.trace.conversions;
720            if (localConversions != null && localConversions != undefined) {
721                var _conversionData = tracer.getConversionData();
722                if (_conversionData != null) {
723                    localConversions.forEach(element => {
724                        var itemLocal = _conversionData.find(x => x.id == element.id);
725                        if (itemLocal) {
726                            element.isComplete = itemLocal.isComplete;
727                            if (element.funnel) {
728                                element.funnel.forEach(step => {
729                                    var localStep = itemLocal.funnel.find(y => y.id == step.id);
730                                    if (localStep) {
731                                        step.isComplete = localStep.isComplete;
732                                    }
733                                })
734                            }
735                        }
736                    });
737                    tracer.trace.conversions = localConversions;
738                    localStorage.setItem("VTConversion", JSON.stringify(localConversions));
739                }
740                else {
741                    localStorage.setItem("VTConversion", JSON.stringify(tracer.trace.conversions));
742                }
743            }
744            else {
745                localStorage.setItem("VTConversion", JSON.stringify(tracer.trace.conversions));
746            }
747        }
748    },
749
750    setConversionData(pageUrl) {
751        var tracer = this;
752        var _conversionData = tracer.getConversionData();
753        if (_conversionData != null) {
754            _conversionData.forEach(element => {
755                //If its a conversion not Funnel process here
756                if (element.type == 'PageVisited' && element.isComplete != true) {
757                    if (element.condition.toLowerCase() == "contains") {
758                        element.isComplete = pageUrl.toLowerCase().includes(element.pageUrl.toLowerCase());
759                    }
760                    else if (element.condition.toLowerCase() == "exact") {
761                        if (element.pageUrl.startsWith('*')) {
762                            element.isComplete = pageUrl.toLowerCase().includes(element.pageUrl.toLowerCase().replaceAll("/*/g", ""));
763                        } else if (element.pageUrl.endsWith('*')) {
764                            element.isComplete = pageUrl.toLowerCase().includes(element.pageUrl.toLowerCase().replaceAll("/*/g", ""));
765                        }
766                        else {
767                            element.isComplete = (pageUrl.toLowerCase() == element.pageUrl.toLowerCase());
768                        }
769                    }
770                }
771
772                //Process Funnel here
773                if (element.type == 'EntireFunnel' && element.funnel != null && element.funnel.length > 0) {
774                    var nextStep = tracer.getFunnelNextStep(element.funnel);
775                    if (nextStep != null) {
776                        if (nextStep.isComplete != true) {
777                            if (nextStep.condition.toLowerCase() == "contains") {
778                                nextStep.isComplete = pageUrl.toLowerCase().includes(nextStep.value.toLowerCase());
779                            }
780                            else if (nextStep.condition.toLowerCase() == "exact") {
781                                if (nextStep.value.startsWith('*')) {
782                                    nextStep.isComplete = pageUrl.toLowerCase().includes(nextStep.value.toLowerCase().replaceAll("/*/g", ""));
783                                } else if (nextStep.value.endsWith('*')) {
784                                    nextStep.isComplete = pageUrl.toLowerCase().includes(nextStep.value.toLowerCase().replaceAll("/*/g", ""));
785                                }
786                                else {
787                                    nextStep.isComplete = (pageUrl.toLowerCase() == nextStep.value.toLowerCase());
788                                }
789                            }
790                        }
791                    }
792                    
793                    //Set funnel percent complete
794                    var totalStepsInFunnel = element.funnel.length;
795                    var totalCompletedSteps = element.funnel.filter(x => x.isComplete == true).length;
796                    element.isComplete = (totalStepsInFunnel === totalCompletedSteps);
797                }
798            });
799
800            //Set to localstorage after processing
801            localStorage.setItem("VTConversion", JSON.stringify(_conversionData));
802            tracer.trace.conversions = _conversionData;
803        }
804    },
805
806    getConversionData() {
807        var tracer = this;
808        var _conversionData = localStorage.getItem("VTConversion");
809        if (_conversionData && _conversionData != "undefined") {
810            var dataObject = JSON.parse(_conversionData);;
811            if (!tracer.isEmptyObject(dataObject)) {
812                return dataObject;
813            }
814            else {
815                return null;
816            }
817        }
818        else {
819            return null;
820        }
821    },
822
823    getFunnelNextStep(funnelData) {
824        let nextStep = null;
825        for (let step of funnelData) {
826            if (step.isComplete == false) {
827                return step;
828            }
829        }
830
831        if (funnelData.length > 0) {
832            nextStep = funnelData[0];
833        }
834        return nextStep;
835    },
836
837    isEmptyObject(obj) {
838        for (var prop in obj) {
839            if (typeof Object.hasOwn === "function") {
840                if (Object.hasOwn(obj, prop)) {
841                    return false;
842                }
843            }
844            else {
845                if (Object.prototype.hasOwnProperty.call(obj, prop)) {
846                    return false;
847                }
848            }
849        }
850
851        return true;
852    },
853
854    getAppSource() {
855        const ua = navigator.userAgent.toLowerCase();
856
857        // Known in-app browser identifiers
858        const appMap = [
859            { keyword: "pinterest",   name: "Pinterest" },
860            { keyword: "instagram",   name: "Instagram" },
861            { keyword: "fbav",        name: "Facebook" },
862            { keyword: "fban",        name: "Facebook" },
863            { keyword: "linkedin",    name: "LinkedIn" },
864            { keyword: "twitter",     name: "Twitter" },
865            { keyword: "tiktok",      name: "TikTok" },
866            { keyword: "snapchat",    name: "Snapchat" },
867            { keyword: "whatsapp",    name: "WhatsApp" },
868            { keyword: "messenger",   name: "Messenger" },
869            { keyword: "line",        name: "LINE" },
870            { keyword: "wechat",      name: "WeChat" },
871            { keyword: "kakaotalk",   name: "KakaoTalk" }
872        ];
873
874        for (const app of appMap) {
875            if (ua.includes(app.keyword)) {
876                return app.name;
877            }
878        }
879
880        return "";
881    }
882};
883
884try {
885    setTimeout(() => {
886        init_tracer();
887    }, 200);
888} catch (err) { console.log(err) }
889
890function bindEvent(element, eventName, eventHandler) {
891    if (element.addEventListener) {
892        element.addEventListener(eventName, eventHandler, false);
893    } else if (element.attachEvent) {
894        element.attachEvent('on' + eventName, eventHandler);
895    }
896}
897
898function sendMessageToParent(msg) {
899    // Send a message to the parent
900    // Make sure you are sending a string, and to stringify JSON
901    window.parent.postMessage(msg, '*'); //comment this when using prod and use below
902}
903
904bindEvent(window, 'message', function (e) {
905    if (e.data == 'APE') {
906        document.addEventListener("click", function (event) {
907            if (!event.shiftKey) {
908                event.preventDefault();
909                event.stopPropagation();
910                var elem = event.target;
911                if (elem != null) {
912                    var qStr = generateQuerySelector(elem);
913                    sendMessageToParent(JSON.stringify({ 'caller': 'vtevent01', 'selector': qStr }));
914                }
915            }
916        });
917
918        document.addEventListener('mouseover', function (e) {
919            mouseoverHighlight(e);
920        }, false);
921
922        document.addEventListener('mouseout', function (e) {
923            mouseoutUnHighlight(e);
924        }, false);
925    }
926})
927
928function generateQuerySelector(el) {
929    if (el.tagName.toLowerCase() == "html")
930        return "HTML";
931    var str = el.tagName;
932    str += (el.id != "") ? "#" + el.id : "";
933    if (el.className) {
934        var classes = [];
935        if (typeof el.className == "string") {
936            classes = el.className.split(/\s/);
937            for (var i = 0; i < classes.length; i++) {
938                if (classes[i] != null && classes[i] != '') {
939                    str += "." + classes[i]
940                }
941            }
942        }
943    }
944    return str
945}
946
947function mouseoverHighlight(e) {
948    /* if hovered node is NOT the registered  || event listener...  */
949    if (e.target !== e.currentTarget) {
950        // Reference hovered element
951        var tgt = e.target;
952        tgt.style.outline = '4px solid yellow';
953
954        // Stop the bubbling phase
955        e.stopPropagation();
956    }
957}
958
959function mouseoutUnHighlight(e) {
960    if (e.target !== e.currentTarget) {
961        var tgt = e.target;
962        tgt.style.outline = '0 none transparent';
963    }
964    e.stopPropagation();
965}
966
967function trimDotFromEnd(str) {
968    return str.replace(/\.*$/, '');
969}
970
971const getLocalStorageNumber = (key, defaultValue) => Number(localStorage.getItem(key)) || defaultValue;
972
973bindEvent(window, "scroll", function (e) {
974    var animateScroll = () => {
975        var tracer = this;
976        var maxScrollPercentage = 0;
977        var scrollHeight = document.documentElement.scrollHeight;
978        var scrollTop = window.scrollY;
979        var windowHeight = window.innerHeight;
980        var scrollPercentage = Math.round(
981            ((scrollTop + windowHeight) / scrollHeight) * 100
982        );
983        if (tracer?.tracer && tracer.tracer?.trace && tracer.tracer?.trace?.
983session && tracer.tracer?.trace?.session?.page) {
984            if (sessionStorage.getItem("max") != null || sessionStorage.getItem("max") != undefined) {
985                if (scrollPercentage > sessionStorage.getItem("max")) {
986                    maxScrollPercentage = scrollPercentage;
987                    sessionStorage.setItem("max", maxScrollPercentage);
988                    console.log("new max:", typeof (maxScrollPercentage))
989                    tracer.tracer.trace.session.page.scrollPercentage = maxScrollPercentage;
990                }
991                else {
992                    sessionStorage.setItem("max", scrollPercentage);
993                    tracer.tracer.trace.session.page.scrollPercentage = scrollPercentage;
994                }
995            }
996            else {
997                sessionStorage.setItem("max", scrollPercentage);
998                tracer.tracer.trace.session.page.scrollPercentage = scrollPercentage;
999            }
1000        }
1001    };
1002    animateScroll();
1003});
1004

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.