1/** 2 * Developer's Notice: 3 * 4 * Note: JS in this file (and this file itself) is not guaranteed backwards compatibility. JS can be added, changed or removed at any time without notice. 5 * For more information see the `Backwards Compatibility Guidelines for Developers` section of the README.md file. 6 */ 7/** 8 * Handles: 9 * - JS Events handling 10 * 11 * @since 7.15.0 12 */ 13var ExactMetrics = function () { 14 /* ExactMetrics JS events tracking works on all major browsers, including IE starting at IE 7, via polyfills for any major JS function used that 15 is not supported by at least 95% of the global and/or US browser marketshare. Currently, IE 7 & 8 which as of 2/14/17 have under 0.25% global marketshare, require 16 us to polyfill Array.prototype.lastIndexOf, and if they continue to drop, we might remove this polyfill at some point. In that case note that events tracking 17 for IE 7/8 will continue to work, with the exception of events tracking of downloads. */ 18 var lastClicked = []; 19 var internalAsOutboundCategory = ''; 20 var beforeUnloadChanged = false; 21 22 this.setLastClicked = function (valuesArray, fieldsArray, tracked) { 23 valuesArray = typeof valuesArray !== 'undefined' ? valuesArray : []; 24 fieldsArray = typeof fieldsArray !== 'undefined' ? fieldsArray : []; 25 tracked = typeof tracked !== 'undefined' ? tracked : false; 26 27 lastClicked.valuesArray = valuesArray; 28 lastClicked.fieldsArray = fieldsArray; 29 }; 30 31 this.getLastClicked = function () { 32 return lastClicked; 33 }; 34 35 this.setInternalAsOutboundCategory = function (category) { 36 internalAsOutboundCategory = category; 37 }; 38 39 this.getInternalAsOutboundCategory = function () { 40 return internalAsOutboundCategory; 41 }; 42 43 this.sendEvent = function (type, action, fieldsArray) { 44 __gtagTrackerSend(type, action, fieldsArray, []); 45 }; 46 47 function __gtagTrackerIsDebug() { 48 if (window.exactmetrics_debug_mode) { 49 return true; 50 } else { 51 return false; 52 } 53 } 54 55 function cloneFields(fields, allowedKeys, disallowedKeys) { 56 var clone = {}; 57 58 for (var key in fields) { 59 if (!fields.hasOwnProperty(key)) { 60 continue 61 } 62 63 if (allowedKeys && allowedKeys.indexOf(key) === -1) { 64 continue 65 } 66 67 if (disallowedKeys && disallowedKeys.indexOf(key) > -1) { 68 continue 69 } 70 71 clone[key] = fields[key]; 72 } 73 74 return clone; 75 } 76 77 function __gtagMaybeTrackerV4(type, action, fieldsArray) { 78 if (!exactmetrics_frontend.v4_id || type !== 'event') { 79 return; 80 } 81 82 var eventCategory = fieldsArray.event_category || ''; 83 84 var fieldsToRemove = [ 85 'event_name', 86 'event_category', 87 'event_label', 88 'value', 89 ]; 90 91 var fields = cloneFields(fieldsArray, null, fieldsToRemove); 92 fields.action = action; 93 fields.send_to = exactmetrics_frontend.v4_id; 94 95 let hitType = eventCategory.replace('-', '_'); 96 if (eventCategory.indexOf('outbound-link') !== -1) { 97 hitType = 'click' 98 } else if (eventCategory === 'download') { 99 hitType = 'file_download' 100 } 101 102 __gtagTracker(type, hitType, fields); 103 } 104 105 function __gtagMaybeTrackerUA(type, action, fieldsArray) { 106 if (!exactmetrics_frontend.ua) { 107 return; 108 } 109 110 var allowedFields = [ 111 'event_category', 112 'event_label', 113 'value', 114 ]; 115 116 var uaFields = cloneFields(fieldsArray, allowedFields); 117 uaFields.send_to = exactmetrics_frontend.ua; 118 119 __gtagTracker(type, action, uaFields); 120 } 121 122 function __gtagTrackerSendDual(type, action, fieldsArray, valuesArray) { 123 type = typeof type !== 'undefined' ? type : 'event'; 124 action = typeof action !== 'undefined' ? action : ''; 125 valuesArray = typeof valuesArray !== 'undefined' ? valuesArray : []; 126 fieldsArray = typeof fieldsArray !== 'undefined' ? fieldsArray : {}; 127 128 __gtagMaybeTrackerUA(type, action, fieldsArray); 129 __gtagMaybeTrackerV4(type, action, fieldsArray); 130 131 lastClicked.valuesArray = valuesArray; 132 lastClicked.fieldsArray = fieldsArray; 133 lastClicked.fieldsArray.event_action = action; 134 lastClicked.tracked = true; 135 __gtagTrackerLog('Tracked: ' + valuesArray.type); 136 __gtagTrackerLog(lastClicked); 137 } 138 139 /** 140 * This attempts to be compatible with the gtag function. 141 * 142 * @see https://developers.google.com/analytics/devguides/collection/gtagjs 143 * @param type string Type of request, event, timing, config. 144 * @param action string Event action or UA for config. 145 * @param fieldsArray object The configuration object. 146 * @param valuesArray object The values for the log. 147 * @private 148 */ 149 function __gtagTrackerSend(type, action, fieldsArray, valuesArray) { 150 type = typeof type !== 'undefined' ? type : 'event'; 151 action = typeof action !== 'undefined' ? action : ''; 152 valuesArray = typeof valuesArray !== 'undefined' ? valuesArray : []; 153 fieldsArray = typeof fieldsArray !== 'undefined' ? fieldsArray : {}; 154 155 __gtagTracker(type, action, fieldsArray); 156 157 lastClicked.valuesArray = valuesArray; 158 lastClicked.fieldsArray = fieldsArray; 159 lastClicked.fieldsArray.event_action = action; 160 lastClicked.tracked = true; 161 __gtagTrackerLog('Tracked: ' + valuesArray.type); 162 __gtagTrackerLog(lastClicked); 163 } 164 165 function __gtagTrackerNotSend(valuesArray) { 166 valuesArray = typeof valuesArray !== 'undefined' ? valuesArray : []; 167 168 lastClicked.valuesArray = valuesArray; 169 lastClicked.fieldsArray = []; 170 lastClicked.tracked = false;
171 __gtagTrackerLog('Not Tracked: ' + valuesArray.exit); 172 __gtagTrackerLog(lastClicked); 173 } 174 175 function __gtagTrackerLog(message) { 176 if (__gtagTrackerIsDebug()) { 177 console.dir(message); 178 } 179 } 180 181 function __gtagTrackerStringTrim(x) { 182 return x.replace(/^\s+|\s+$/gm, ''); 183 } 184 185 function __gtagTrackerGetDomain() { 186 var i = 0, currentdomain = document.domain, p = currentdomain.split('.'), s = '_gd' + ( 187 new Date() 188 ).getTime(); 189 while (i < (p.length - 1) && document.cookie.indexOf(s + '=' + s) == -1) { 190 currentdomain = p.slice(-1 - ( 191 ++i 192 )).join('.'); 193 document.cookie = s + "=" + s + ";domain=" + currentdomain + ";"; 194 } 195 document.cookie = s + "=;expires=Thu, 01 Jan 1970 00:00:01 GMT;domain=" + currentdomain + ";"; 196 return currentdomain; 197 } 198 199 function __gtagTrackerGetExtension(extension) { 200 extension = extension.toString(); 201 extension = extension.substring(0, ( 202 extension.indexOf("#") == -1 203 ) ? extension.length : extension.indexOf("#")); /* Remove the anchor at the end, if there is one */ 204 extension = extension.substring(0, ( 205 extension.indexOf("?") == -1 206 ) ? extension.length : extension.indexOf("?")); /* Remove the query after the file name, if there is one */ 207 extension = extension.substring(extension.lastIndexOf("/") + 1, extension.length); /* Remove everything before the last slash in the path */ 208 if (extension.length > 0 && extension.indexOf('.') !== -1) { /* If there's a period left in the URL, then there's a extension. Else it is not a extension. */ 209 extension = extension.substring(extension.lastIndexOf(".") + 1); /* Remove everything but what's after the first period */ 210 return extension; 211 } else { 212 return ""; 213 } 214 } 215 216 function __gtagTrackerTrackedClick(event) { 217 return event.which == 1 || event.which == 2 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey; 218 } 219 220 function __gtagTrackerGetDownloadExtensions() { 221 var download_extensions = []; 222 if (typeof exactmetrics_frontend.download_extensions == 'string') { 223 download_extensions = exactmetrics_frontend.download_extensions.split(","); 224 } 225 return download_extensions; 226 } 227 228 function __gtagTrackerGetInboundPaths() { 229 var inbound_paths = []; 230 if (typeof exactmetrics_frontend.inbound_paths == 'string') { 231 inbound_paths = JSON.parse(exactmetrics_frontend.inbound_paths); 232 } 233 234 return inbound_paths; 235 } 236 237 function __gtagTrackerTrackedClickType(event) { 238 if (event.which == 1) { 239 return 'event.which=1'; 240 } else if (event.which == 2) { 241 return 'event.which=2'; 242 } else if (event.metaKey) { 243 return 'metaKey'; 244 } else if (event.ctrlKey) { 245 return 'ctrlKey'; 246 } else if (event.shiftKey) { 247 return 'shiftKey'; 248 } else if (event.altKey) { 249 return 'altKey'; 250 } else { 251 return ''; 252 } 253 } 254 255 function __gtagTrackerLinkType(el) { 256 var download_extensions = __gtagTrackerGetDownloadExtensions(); 257 var inbound_paths = __gtagTrackerGetInboundPaths(); 258 var type = 'unknown'; 259 var link = el.href; 260 var extension = __gtagTrackerGetExtension(el.href); 261 var currentdomain = __gtagTrackerGetDomain(); 262 var hostname = el.hostname; 263 var protocol = el.protocol; 264 var pathname = el.pathname; 265 link = link.toString(); 266 var index, len; 267 var category = el.getAttribute("data-vars-ga-category"); 268 269 if (category) { 270 return category; 271 } 272 273 if (link.match(/^javascript\:/i)) { 274 type = 'internal'; /* if it's a JS link, it's internal */ 275 } else if (protocol && protocol.length > 0 && ( 276 __gtagTrackerStringTrim(protocol) == 'tel' || __gtagTrackerStringTrim(protocol) == 'tel:' 277 )) { /* If it's a telephone link */ 278 type = "tel"; 279 } else if (protocol && protocol.length > 0 && ( 280 __gtagTrackerStringTrim(protocol) == 'mailto' || __gtagTrackerStringTrim(protocol) == 'mailto:' 281 )) { /* If it's a email */ 282 type = "mailto"; 283 } else if (hostname && currentdomain && hostname.length > 0 && currentdomain.length > 0 && !hostname.endsWith('.' + currentdomain) && hostname !== currentdomain) { /* If it's a outbound */ 284 type = "external"; 285 } else if (pathname && JSON.stringify(inbound_paths) != "{}" && pathname.length > 0) { /* If it's an internal as outbound */ 286 var inbound_paths_length = inbound_paths.length; 287 for (var inbound_paths_index = 0; inbound_paths_index < inbound_paths_length; inbound_paths_index++) { 288 if (inbound_paths[inbound_paths_index].path && inbound_paths[inbound_paths_index].label && inbound_paths[inbound_paths_index].path.length > 0 && inbound_paths[inbound_paths_index].label.length > 0 && pathname.startsWith(inbound_paths[inbound_paths_index].path)) {
289 type = "internal-as-outbound"; 290 internalAsOutboundCategory = "outbound-link-" + inbound_paths[inbound_paths_index].label; 291 break; 292 } 293 } 294 /* Enable window.exactmetrics_experimental_mode at your own risk. We might eventually remove it. Also you may/can/will burn through GA quota for your property quickly. */ 295 } else if (hostname && window.exactmetrics_experimental_mode && hostname.length > 0 && document.domain.length > 0 && hostname !== document.domain) { /* If it's a cross-hostname link */ 296 type = "cross-hostname"; 297 } 298 299 if (extension && ( 300 type === 'unknown' || 'external' === type 301 ) && download_extensions.length > 0 && extension.length > 0) { /* If it's a download */ 302 for (index = 0, len = download_extensions.length; index < len; ++index) { 303 if (download_extensions[index].length > 0 && ( 304 link.endsWith(download_extensions[index]) || download_extensions[index] == extension 305 )) { 306 type = "download"; 307 break; 308 } 309 } 310 } 311 312 if (type === 'unknown') { 313 type = 'internal'; 314 } 315 return type; 316 } 317 318 function __gtagTrackerLinkTarget(el, event) { 319 320 /* Is actual target set and not _(self|parent|top)? */ 321 var target = ( 322 el.target && !el.target.match(/^_(self|parent|top)$/i) 323 ) ? el.target : false;
324 325 /* Assume a target if Ctrl|shift|meta-click */ 326 if (event.ctrlKey || event.shiftKey || event.metaKey || event.which == 2) { 327 target = "_blank"; 328 } 329 return target; 330 } 331 332 function __gtagTrackerGetTitle(el) { 333 if (el.getAttribute("data-vars-ga-label") && el.getAttribute("data-vars-ga-label").replace(/\n/ig, '')) { 334 return el.getAttribute("data-vars-ga-label").replace(/\n/ig, ''); 335 } else if (el.title && el.title.replace(/\n/ig, '')) { 336 return el.title.replace(/\n/ig, ''); 337 } else if (el.innerText && el.innerText.replace(/\n/ig, '')) { 338 return el.innerText.replace(/\n/ig, ''); 339 } else if (el.getAttribute('aria-label') && el.getAttribute('aria-label').replace(/\n/ig, '')) { 340 return el.getAttribute('aria-label').replace(/\n/ig, ''); 341 } else if (el.alt && el.alt.replace(/\n/ig, '')) { 342 return el.alt.replace(/\n/ig, ''); 343 } else if (el.textContent && el.textContent.replace(/\n/ig, '')) { 344 return el.textContent.replace(/\n/ig, ''); 345 } else { 346 return undefined; 347 } 348 } 349 350 function __gtagTrackerGetInnerTitle(el) { 351 var children = el.children; 352 var count = 0; 353 var child; 354 var value; 355 for (var i = 0; i < children.length; i++) { 356 child = children[i];
357 value = __gtagTrackerGetTitle(child); 358 if (value) { 359 return value; 360 } 361 /* max search 100 elements to ensure performance */ 362 if (count == 99) { 363 return undefined; 364 } 365 count++; 366 } 367 return undefined; 368 } 369 370 function __gtagTrackerClickEvent(event) { 371 var el = event.srcElement || event.target; 372 var valuesArray = []; 373 var fieldsArray; 374 375 /* Start Values Array */ 376 valuesArray.el = el; 377 valuesArray.click_type = __gtagTrackerTrackedClickType(event); 378 379 /* If GA is blocked or not loaded, or not main|middle|touch click then don't track */ 380 if ('undefined' === typeof __gtagTracker || !__gtagTrackerTrackedClick(event)) { 381 valuesArray.exit = 'loaded'; 382 __gtagTrackerNotSend(valuesArray); 383 return; 384 } 385 386 /* Loop up the DOM tree through parent elements if clicked element is not a link (eg: an image inside a link) */ 387 while (el && ( 388 typeof el.tagName == 'undefined' || el.tagName.toLowerCase() != 'a' || !el.href 389 )) { 390 el = el.parentNode; 391 } 392 393 /* if a link with valid href has been clicked */ 394 if (el && el.href && !el.hasAttribute('xlink:href')) { 395 var link = el.href; /* What link are we tracking */ 396 var extension = __gtagTrackerGetExtension(el.href); /* What extension is this link */ 397 var download_extensions = __gtagTrackerGetDownloadExtensions(); /* Let's get the extensions to track */ 398 var inbound_paths = __gtagTrackerGetInboundPaths(); /* Let's get the internal paths to track */ 399 var home_url = exactmetrics_frontend.home_url; /* Let's get the url to compare for external/internal use */ 400 var currentdomain = __gtagTrackerGetDomain(); /* What domain are we on? */ 401 var type = __gtagTrackerLinkType(el); /* What type of link is this? */ 402 var target = __gtagTrackerLinkTarget(el, event); /* Is a new tab/window being opened? */ 403 var action = el.getAttribute("data-vars-ga-action"); 404 var label = el.getAttribute("data-vars-ga-label"); 405 406 /* Element */ 407 valuesArray.el = el; /* el is an a element so we can parse it */ 408 valuesArray.el_href = el.href; /* "http://example.com:3000/pathname/?search=test#hash" */ 409 valuesArray.el_protocol = el.protocol; /* "http:" */ 410 valuesArray.el_hostname = el.hostname; /* "example.com" */ 411 valuesArray.el_port = el.port; /* "3000" */ 412 valuesArray.el_pathname = el.pathname; /* "/pathname/" */ 413 valuesArray.el_search = el.search; /* "?search=test" */ 414 valuesArray.el_hash = el.hash; /* "#hash" */ 415 valuesArray.el_host = el.host; /* "example.com:3000" */ 416 valuesArray.el_classes = el.getAttribute('class') 417 valuesArray.el_id = el.id 418 419 /* Settings */ 420 valuesArray.debug_mode = __gtagTrackerIsDebug(); /* "example.com:3000" */ 421 valuesArray.download_extensions = download_extensions; /* Let's get the extensions to track */ 422 valuesArray.inbound_paths = inbound_paths; /* Let's get the internal paths to track */ 423 valuesArray.home_url = home_url; /* Let's get the url to compare for external/internal use */ 424 425 /* Parsed/Logic */ 426 valuesArray.link = link; /* What link are we tracking */ 427 valuesArray.extension = extension; /* What extension is this link */ 428 valuesArray.type = type; /* What type of link is this */ 429 valuesArray.target = target; /* Is a new tab/window being opened? */ 430 valuesArray.title = __gtagTrackerGetTitle(el); /* Try link title, then text content */ 431 432 /* only find innerTitle if we need one */ 433 if (!valuesArray.label && !valuesArray.title) { 434 valuesArray.title = __gtagTrackerGetInnerTitle(el); 435 } 436 437 /* Let's track everything but internals (that aren't internal-as-externals) and javascript */ 438 if (type !== 'internal' && type !== 'javascript') { 439 440 var __gtagTrackerHitBackRun = false; /* Tracker has not yet run */ 441 442 /* HitCallback to open link in same window after tracker */ 443 var __gtagTrackerHitBack = function () { 444 /* Run the hitback only once */ 445 if (__gtagTrackerHitBackRun) { 446 return; 447 } 448 maybePreventBeforeUnload(); 449 __gtagTrackerHitBackRun = true; 450 window.location.href = link; 451 }; 452 453 var __gtagTrackerNoRedirectExternal = function () { 454 valuesArray.exit = 'external'; 455 __gtagTrackerNotSend(valuesArray); 456 }; 457 458 var __gtagTrackerNoRedirectInboundAsExternal = function () {
459 valuesArray.exit = 'internal-as-outbound'; 460 __gtagTrackerNotSend(valuesArray); 461 }; 462 var __gtagTrackerNoRedirectCrossHostname = function () { 463 valuesArray.exit = 'cross-hostname'; 464 __gtagTrackerNotSend(valuesArray); 465 }; 466 467 if (target || type == 'mailto' || type == 'tel') { /* If target opens a new window then just track */ 468 if (type == 'download') { 469 fieldsArray = { 470 event_category: 'download', 471 event_label: label || valuesArray.title, 472 file_extension: valuesArray.extension, 473 file_name: valuesArray.link.replace(/^.*\//g, ''), 474 link_text: label || valuesArray.title, 475 link_url: link, 476 link_domain: valuesArray.el_hostname, 477 link_classes: valuesArray.el_classes, 478 link_id: valuesArray.el_id, 479 }; 480 } else if (type == 'tel') { 481 fieldsArray = { 482 event_category: 'tel', 483 event_label: label || valuesArray.title.replace('tel:', ''), 484 tel_number: link.replace('tel:', ''), 485 link_text: label || valuesArray.title, 486 link_url: link, 487 link_classes: valuesArray.el_classes, 488 link_id: valuesArray.el_id, 489 }; 490 } else if (type == 'mailto') { 491 fieldsArray = { 492 event_category: 'mailto', 493 event_label: label || valuesArray.title.replace('mailto:', ''), 494 email_address: link.replace('mailto:', ''), 495 link_text: label || valuesArray.title.replace('mailto:', ''), 496 link_url: link, 497 link_classes: valuesArray.el_classes, 498 link_id: valuesArray.el_id, 499 }; 500 } else if (type == 'internal-as-outbound') { 501 fieldsArray = { 502 event_category: internalAsOutboundCategory, 503 event_label: label || valuesArray.title, 504 event_name: 'click', 505 is_affiliate_link: true, 506 affiliate_label: internalAsOutboundCategory.replace('outbound-link-', ''), 507 link_text: label || valuesArray.title, 508 link_url: link, 509 link_domain: valuesArray.el_hostname, 510 link_classes: valuesArray.el_classes, 511 link_id: valuesArray.el_id, 512 outbound: true, 513 }; 514 } else if (type == 'external') { 515 fieldsArray = { 516 event_category: 'outbound-link', 517 event_label: label || valuesArray.title, 518 is_affiliate_link: false, 519 link_text: label || valuesArray.title, 520 link_url: link, 521 link_domain: valuesArray.el_hostname, 522 link_classes: valuesArray.el_classes, 523 link_id: valuesArray.el_id, 524 outbound: true, 525 }; 526 } else if (type == 'cross-hostname') { 527 fieldsArray = { 528 event_category: 'cross-hostname', 529 event_label: label || valuesArray.title, 530 link_text: label || valuesArray.title, 531 link_url: link, 532 link_domain: valuesArray.el_hostname, 533 link_classes: valuesArray.el_classes, 534 link_id: valuesArray.el_id, 535 }; 536 } 537 538 if (fieldsArray) { 539 __gtagTrackerSendDual('event', action || link, fieldsArray, valuesArray); 540 } else { 541 if (type && type != 'internal') { 542 fieldsArray = { 543 event_category: type, 544 event_label: label || valuesArray.title, 545 link_text: label || valuesArray.title, 546 link_url: link, 547 link_domain: valuesArray.el_hostname, 548 link_classes: valuesArray.el_classes, 549 link_id: valuesArray.el_id, 550 }; 551 552 __gtagTrackerSendDual('event', action || link, fieldsArray, valuesArray); 553 } else { 554 valuesArray.exit = 'type'; 555 __gtagTrackerNotSend(valuesArray); 556 } 557 } 558 } else { 559 /* Prevent standard click, track then open */ 560 if (type != 'cross-hostname' && type != 'external' && type != 'internal-as-outboun
560d') { 561 if (!event.defaultPrevented) { 562 if (event.preventDefault) { 563 event.preventDefault(); 564 } else { 565 event.returnValue = false; 566 } 567 } 568 } 569 570 if (type == 'download') { 571 fieldsArray = { 572 event_category: 'download', 573 event_label: label || valuesArray.title, 574 event_callback: __gtagTrackerHitBack, 575 file_extension: valuesArray.extension, 576 file_name: valuesArray.link.replace(/^.*\//g, ''), 577 link_text: label || valuesArray.title, 578 link_url: link, 579 link_domain: valuesArray.el_hostname, 580 link_classes: valuesArray.el_classes, 581 link_id: valuesArray.el_id, 582 }; 583 584 __gtagTrackerSendDual('event', action || link, fieldsArray, valuesArray); 585 } else if (type == 'internal-as-outbound') { 586 beforeUnloadChanged = true; 587 window.onbeforeunload = function (e) { 588 if (!event.defaultPrevented) { 589 if (event.preventDefault) { 590 event.preventDefault(); 591 } else { 592 event.returnValue = false; 593 } 594 } 595 596 fieldsArray = { 597 event_category: internalAsOutboundCategory, 598 event_label: label || valuesArray.title, 599 event_callback: __gtagTrackerHitBack, 600 is_affiliate_link: true, 601 affiliate_label: internalAsOutboundCategory.replace('outbound-link-', ''), 602 link_text: label || valuesArray.title, 603 link_url: link, 604 link_domain: valuesArray.el_hostname, 605 link_classes: valuesArray.el_classes, 606 link_id: valuesArray.el_id, 607 outbound: true, 608 }; 609 610 if (navigator.sendBeacon) { 611 fieldsArray.transport = 'beacon'; 612 } 613 614 __gtagTrackerSendDual('event', action || link, fieldsArray, valuesArray); 615 616 setTimeout(__gtagTrackerHitBack, 1000); 617 }; 618 } else if (type == 'external') { 619 beforeUnloadChanged = true; 620 window.onbeforeunload = function (e) { 621 622 fieldsArray = { 623 event_category: 'outbound-link', 624 event_label: label || valuesArray.title, 625 event_callback: __gtagTrackerHitBack, 626 is_affiliate_link: false, 627 link_text: label || valuesArray.title, 628 link_url: link, 629 link_domain: valuesArray.el_hostname, 630 link_classes: valuesArray.el_classes, 631 link_id: valuesArray.el_id, 632 outbound: true, 633 }; 634 635 if (navigator.sendBeacon) { 636 fieldsArray.transport = 'beacon'; 637 } 638 639 __gtagTrackerSendDual('event', action || link, fieldsArray, valuesArray); 640 }; 641 } else if (type == 'cross-hostname') { 642 beforeUnloadChanged = true; 643 window.onbeforeunload = function (e) { 644 if (!event.defaultPrevented) { 645 if (event.preventDefault) { 646 event.preventDefault(); 647 } else { 648 event.returnValue = false; 649 } 650 } 651 652 fieldsArray = { 653 event_category: 'cross-hostname', 654 event_label: label || valuesArray.title, 655 event_callback: __gtagTrackerHitBack, 656 link_text: label || valuesArray.title, 657 link_url: link, 658 link_domain: valuesArray.el_hostname, 659 link_classes: valuesArray.el_classes, 660 link_id: valuesArray.el_id, 661 }; 662 663 if (navigator.sendBeacon) { 664 fieldsArray.transport = 'beacon'; 665 } 666 667 __gtagTrackerSendDual('event', action || link, fieldsArray, valuesArray); 668 setTimeout(__gtagTrackerHitBack, 1000); 669 }; 670 } else {
671 if (type && type !== 'internal') { 672 fieldsArray = { 673 event_category: type, 674 event_label: label || valuesArray.title, 675 event_callback: __gtagTrackerHitBack, 676 link_text: label || valuesArray.title, 677 link_url: link, 678 link_domain: valuesArray.el_hostname, 679 link_classes: valuesArray.el_classes, 680 link_id: valuesArray.el_id, 681 }; 682 683 __gtagTrackerSendDual('event', action || link, fieldsArray, valuesArray); 684 } else { 685 valuesArray.exit = 'type'; 686 __gtagTrackerNotSend(valuesArray); 687 } 688 } 689 690 if (type != 'external' && type != 'cross-hostname' && type != 'internal-as-outbound') { 691 /* Run event_callback again if GA takes longer than 1 second */ 692 setTimeout(__gtagTrackerHitBack, 1000); 693 } else { 694 if (type == 'external') { 695 setTimeout(__gtagTrackerNoRedirectExternal, 1100); 696 } else if (type == 'cross-hostname') { 697 setTimeout(__gtagTrackerNoRedirectCrossHostname, 1100); 698 } else { 699 setTimeout(__gtagTrackerNoRedirectInboundAsExternal, 1100); 700 } 701 } 702 703 // Clear out the beforeunload event if it was set to avoid sending false events. 704 setTimeout(maybePreventBeforeUnload, 100); 705 } 706 } else { 707 maybePreventBeforeUnload(); 708 valuesArray.exit = 'internal'; 709 __gtagTrackerNotSend(valuesArray); 710 } 711 } else { 712 valuesArray.exit = 'notlink'; 713 __gtagTrackerNotSend(valuesArray); 714 } 715 } 716 717 var prevHash = window.location.hash; 718 719 function __gtagTrackerHashChangeEvent() { 720 /* Todo: Ready this section for JS unit testing */ 721 if (exactmetrics_frontend.hash_tracking === "true" && prevHash != window.location.hash && (exactmetrics_frontend.ua || exactmetrics_frontend.v4_id)) { 722 prevHash = window.location.hash; 723 if (exactmetrics_frontend.ua) { 724 __gtagTracker('config', exactmetrics_frontend.ua, { 725 page_path: location.pathname + location.search + location.hash, 726 }); 727 } 728 729 if (exactmetrics_frontend.v4_id) { 730 __gtagTracker('config', exactmetrics_frontend.v4_id, { 731 page_path: location.pathname + location.search + location.hash, 732 }); 733 } 734 __gtagTrackerLog("Hash change to: " + location.pathname + location.search + location.hash); 735 } else { 736 __gtagTrackerLog("Hash change to (untracked): " + location.pathname + location.search + location.hash); 737 } 738 } 739 740 function maybePreventBeforeUnload() { 741 if (beforeUnloadChanged) { 742 window.onbeforeunload = null; 743 } 744 } 745 746 /* Attach the event to all clicks in the document after page has loaded */ 747 var __gtagTrackerWindow = window; 748 if (__gtagTrackerWindow.addEventListener) { 749 __gtagTrackerWindow.addEventListener( 750 "load", 751 function () { 752 document.body.addEventListener( 753 "click", 754 __gtagTrackerClickEvent, 755 false 756 ); 757 }, 758 false 759 ); 760 window.addEventListener("hashchange", __gtagTrackerHashChangeEvent, false); 761 } else { 762 if (__gtagTrackerWindow.attachEvent) { 763 __gtagTrackerWindow.attachEvent( 764 "onload", 765 function () { 766 document.body.attachEvent("onclick", __gtagTrackerClickEvent); 767 } 768 ); 769 window.attachEvent("onhashchange", __gtagTrackerHashChangeEvent); 770 } 771 } 772 773 if (typeof String.prototype.endsWith !== 'function') { 774 String.prototype.endsWith = function (suffix) { 775 return this.indexOf(suffix, this.length - suffix.length) !== -1; 776 }; 777 } 778 if (typeof String.prototype.startsWith !== 'function') { 779 String.prototype.startsWith = function (prefix) { 780 return this.indexOf(prefix) === 0; 781 }; 782 } 783 784 if (typeof Array.prototype.lastIndexOf !== 'function') { 785 Array.prototype.lastIndexOf = function (searchElement /*, fromIndex*/) { 786 'use strict'; 787 788 if (this === void 0 || this === null) { 789 throw new TypeError(); 790 } 791 792 var n, k, 793 t = Object(this), 794 len = t.length >>> 0; /* jshint ignore:line */ 795 if (len === 0) { 796 return -1; 797 } 798 799 n = len - 1; 800 if (arguments.length > 1) { 801 n = Number(arguments[1]); 802 if (n != n) { 803 n = 0; 804 } else if (n != 0 && n != ( 805 1 / 0 806 ) && n != -( 807 1 / 0 808 )) { /* jshint ignore:line */ 809 n = ( 810 n > 0 || -1 811 ) * Math.floor(Math.abs(n)); 812 } 813 } 814 815 for (k = n >= 0 ? Math.min(n, len - 1) : len - Math.abs(n); k >= 0; k--) { 816 if (k in t && t[k] === searchElement) { 817 return k; 818 } 819 } 820 return -1; 821 }; 822 } 823}; 824var ExactMetricsObject = new ExactMetrics();
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.