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.