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