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 451 if ( el.attributes.download ) { 452 var linkTag = document.createElement('a'); 453 linkTag.href = el.href; 454 linkTag.download = el.download; 455 linkTag.click(); 456 } else { 457 window.location.href = link; 458 } 459 }; 460 461 var __gtagTrackerNoRedirectExternal = function () { 462 valuesArray.exit = 'external'; 463 __gtagTrackerNotSend(valuesArray); 464 }; 465 466 var __gtagTrackerNoRedirectInboundAsExternal = function () {
467 valuesArray.exit = 'internal-as-outbound'; 468 __gtagTrackerNotSend(valuesArray); 469 }; 470 var __gtagTrackerNoRedirectCrossHostname = function () { 471 valuesArray.exit = 'cross-hostname'; 472 __gtagTrackerNotSend(valuesArray); 473 }; 474 475 if (target || type == 'mailto' || type == 'tel') { /* If target opens a new window then just track */ 476 if (type == 'download') { 477 fieldsArray = { 478 event_category: 'download', 479 event_label: label || valuesArray.title, 480 file_extension: valuesArray.extension, 481 file_name: valuesArray.link.replace(/^.*\//g, ''), 482 link_text: label || valuesArray.title, 483 link_url: link, 484 link_domain: valuesArray.el_hostname, 485 link_classes: valuesArray.el_classes, 486 link_id: valuesArray.el_id, 487 }; 488 } else if (type == 'tel') { 489 fieldsArray = { 490 event_category: 'tel', 491 event_label: label || valuesArray.title.replace('tel:', ''), 492 tel_number: link.replace('tel:', ''), 493 link_text: label || valuesArray.title, 494 link_url: link, 495 link_classes: valuesArray.el_classes, 496 link_id: valuesArray.el_id, 497 }; 498 } else if (type == 'mailto') { 499 fieldsArray = { 500 event_category: 'mailto', 501 event_label: label || valuesArray.title.replace('mailto:', ''), 502 email_address: link.replace('mailto:', ''), 503 link_text: label || valuesArray.title.replace('mailto:', ''), 504 link_url: link, 505 link_classes: valuesArray.el_classes, 506 link_id: valuesArray.el_id, 507 }; 508 } else if (type == 'internal-as-outbound') { 509 fieldsArray = { 510 event_category: internalAsOutboundCategory, 511 event_label: label || valuesArray.title, 512 event_name: 'click', 513 is_affiliate_link: true, 514 affiliate_label: internalAsOutboundCategory.replace('outbound-link-', ''), 515 link_text: label || valuesArray.title, 516 link_url: link, 517 link_domain: valuesArray.el_hostname, 518 link_classes: valuesArray.el_classes, 519 link_id: valuesArray.el_id, 520 outbound: true, 521 }; 522 } else if (type == 'external') { 523 fieldsArray = { 524 event_category: 'outbound-link', 525 event_label: label || valuesArray.title, 526 is_affiliate_link: false, 527 link_text: label || valuesArray.title, 528 link_url: link, 529 link_domain: valuesArray.el_hostname, 530 link_classes: valuesArray.el_classes, 531 link_id: valuesArray.el_id, 532 outbound: true, 533 }; 534 } else if (type == 'cross-hostname') { 535 fieldsArray = { 536 event_category: 'cross-hostname', 537 event_label: label || valuesArray.title, 538 link_text: label || valuesArray.title, 539 link_url: link, 540 link_domain: valuesArray.el_hostname, 541 link_classes: valuesArray.el_classes, 542 link_id: valuesArray.el_id, 543 }; 544 } 545 546 if (fieldsArray) { 547 __gtagTrackerSendDual('event', action || link, fieldsArray, valuesArray); 548 } else { 549 if (type && type != 'internal') { 550 fieldsArray = { 551 event_category: type, 552 event_label: label || valuesArray.title, 553 link_text: label || valuesArray.title, 554 link_url: link, 555 link_domain: valuesArray.el_hostname, 556 link_classes: valuesArray.el_classes, 557 link_id: valuesArray.el_id, 558 }; 559 560 __gtagTrackerSendDual('event', action || link, fieldsArray, valuesArray); 561 } else { 562 valuesArray.exit = 'type'; 563 __gtagTrackerNotSend(valuesArray); 564 } 565 } 566 } else { 567 /* Prevent standard click, track then open */ 568 if (type != 'cross-hostname' && type != 'external' && type != 'internal-as-outboun
568d') { 569 if (!event.defaultPrevented) { 570 if (event.preventDefault) { 571 event.preventDefault(); 572 } else { 573 event.returnValue = false; 574 } 575 } 576 } 577 578 if (type == 'download') { 579 fieldsArray = { 580 event_category: 'download', 581 event_label: label || valuesArray.title, 582 event_callback: __gtagTrackerHitBack, 583 file_extension: valuesArray.extension, 584 file_name: valuesArray.link.replace(/^.*\//g, ''), 585 link_text: label || valuesArray.title, 586 link_url: link, 587 link_domain: valuesArray.el_hostname, 588 link_classes: valuesArray.el_classes, 589 link_id: valuesArray.el_id, 590 }; 591 592 __gtagTrackerSendDual('event', action || link, fieldsArray, valuesArray); 593 } else if (type == 'internal-as-outbound') { 594 beforeUnloadChanged = true; 595 window.onbeforeunload = function (e) { 596 if (!event.defaultPrevented) { 597 if (event.preventDefault) { 598 event.preventDefault(); 599 } else { 600 event.returnValue = false; 601 } 602 } 603 604 fieldsArray = { 605 event_category: internalAsOutboundCategory, 606 event_label: label || valuesArray.title, 607 event_callback: __gtagTrackerHitBack, 608 is_affiliate_link: true, 609 affiliate_label: internalAsOutboundCategory.replace('outbound-link-', ''), 610 link_text: label || valuesArray.title, 611 link_url: link, 612 link_domain: valuesArray.el_hostname, 613 link_classes: valuesArray.el_classes, 614 link_id: valuesArray.el_id, 615 outbound: true, 616 }; 617 618 if (navigator.sendBeacon) { 619 fieldsArray.transport = 'beacon'; 620 } 621 622 __gtagTrackerSendDual('event', action || link, fieldsArray, valuesArray); 623 624 setTimeout(__gtagTrackerHitBack, 1000); 625 }; 626 } else if (type == 'external') { 627 beforeUnloadChanged = true; 628 window.onbeforeunload = function (e) { 629 630 fieldsArray = { 631 event_category: 'outbound-link', 632 event_label: label || valuesArray.title, 633 event_callback: __gtagTrackerHitBack, 634 is_affiliate_link: false, 635 link_text: label || valuesArray.title, 636 link_url: link, 637 link_domain: valuesArray.el_hostname, 638 link_classes: valuesArray.el_classes, 639 link_id: valuesArray.el_id, 640 outbound: true, 641 }; 642 643 if (navigator.sendBeacon) { 644 fieldsArray.transport = 'beacon'; 645 } 646 647 __gtagTrackerSendDual('event', action || link, fieldsArray, valuesArray); 648 }; 649 } else if (type == 'cross-hostname') { 650 beforeUnloadChanged = true; 651 window.onbeforeunload = function (e) { 652 if (!event.defaultPrevented) { 653 if (event.preventDefault) { 654 event.preventDefault(); 655 } else { 656 event.returnValue = false; 657 } 658 } 659 660 fieldsArray = { 661 event_category: 'cross-hostname', 662 event_label: label || valuesArray.title, 663 event_callback: __gtagTrackerHitBack, 664 link_text: label || valuesArray.title, 665 link_url: link, 666 link_domain: valuesArray.el_hostname, 667 link_classes: valuesArray.el_classes, 668 link_id: valuesArray.el_id, 669 }; 670 671 if (navigator.sendBeacon) { 672 fieldsArray.transport = 'beacon'; 673 } 674 675 __gtagTrackerSendDual('event', action || link, fieldsArray, valuesArray); 676 setTimeout(__gtagTrackerHitBack, 1000); 677 }; 678 } else {
679 if (type && type !== 'internal') { 680 fieldsArray = { 681 event_category: type, 682 event_label: label || valuesArray.title, 683 event_callback: __gtagTrackerHitBack, 684 link_text: label || valuesArray.title, 685 link_url: link, 686 link_domain: valuesArray.el_hostname, 687 link_classes: valuesArray.el_classes, 688 link_id: valuesArray.el_id, 689 }; 690 691 __gtagTrackerSendDual('event', action || link, fieldsArray, valuesArray); 692 } else { 693 valuesArray.exit = 'type'; 694 __gtagTrackerNotSend(valuesArray); 695 } 696 } 697 698 if (type != 'external' && type != 'cross-hostname' && type != 'internal-as-outbound') { 699 /* Run event_callback again if GA takes longer than 1 second */ 700 setTimeout(__gtagTrackerHitBack, 1000); 701 } else { 702 if (type == 'external') { 703 setTimeout(__gtagTrackerNoRedirectExternal, 1100); 704 } else if (type == 'cross-hostname') { 705 setTimeout(__gtagTrackerNoRedirectCrossHostname, 1100); 706 } else { 707 setTimeout(__gtagTrackerNoRedirectInboundAsExternal, 1100); 708 } 709 } 710 711 // Clear out the beforeunload event if it was set to avoid sending false events. 712 setTimeout(maybePreventBeforeUnload, 100); 713 } 714 } else { 715 maybePreventBeforeUnload(); 716 valuesArray.exit = 'internal'; 717 __gtagTrackerNotSend(valuesArray); 718 } 719 } else { 720 valuesArray.exit = 'notlink'; 721 __gtagTrackerNotSend(valuesArray); 722 } 723 } 724 725 var prevHash = window.location.hash; 726 727 function __gtagTrackerHashChangeEvent() { 728 /* Todo: Ready this section for JS unit testing */ 729 if (exactmetrics_frontend.hash_tracking === "true" && prevHash != window.location.hash && (exactmetrics_frontend.ua || exactmetrics_frontend.v4_id)) { 730 prevHash = window.location.hash; 731 if (exactmetrics_frontend.ua) { 732 __gtagTracker('config', exactmetrics_frontend.ua, { 733 page_path: location.pathname + location.search + location.hash, 734 }); 735 } 736 737 if (exactmetrics_frontend.v4_id) { 738 __gtagTracker('config', exactmetrics_frontend.v4_id, { 739 page_path: location.pathname + location.search + location.hash, 740 }); 741 } 742 __gtagTrackerLog("Hash change to: " + location.pathname + location.search + location.hash); 743 } else { 744 __gtagTrackerLog("Hash change to (untracked): " + location.pathname + location.search + location.hash); 745 } 746 } 747 748 function maybePreventBeforeUnload() { 749 if (beforeUnloadChanged) { 750 window.onbeforeunload = null; 751 } 752 } 753 754 /* Attach the event to all clicks in the document after page has loaded */ 755 var __gtagTrackerWindow = window; 756 if (__gtagTrackerWindow.addEventListener) { 757 __gtagTrackerWindow.addEventListener( 758 "load", 759 function () { 760 document.body.addEventListener( 761 "click", 762 __gtagTrackerClickEvent, 763 false 764 ); 765 }, 766 false 767 ); 768 window.addEventListener("hashchange", __gtagTrackerHashChangeEvent, false); 769 } else { 770 if (__gtagTrackerWindow.attachEvent) { 771 __gtagTrackerWindow.attachEvent( 772 "onload", 773 function () { 774 document.body.attachEvent("onclick", __gtagTrackerClickEvent); 775 } 776 ); 777 window.attachEvent("onhashchange", __gtagTrackerHashChangeEvent); 778 } 779 } 780 781 if (typeof String.prototype.endsWith !== 'function') { 782 String.prototype.endsWith = function (suffix) { 783 return this.indexOf(suffix, this.length - suffix.length) !== -1; 784 }; 785 } 786 if (typeof String.prototype.startsWith !== 'function') { 787 String.prototype.startsWith = function (prefix) { 788 return this.indexOf(prefix) === 0; 789 }; 790 } 791 792 if (typeof Array.prototype.lastIndexOf !== 'function') { 793 Array.prototype.lastIndexOf = function (searchElement /*, fromIndex*/) { 794 'use strict'; 795 796 if (this === void 0 || this === null) { 797 throw new TypeError(); 798 } 799 800 var n, k, 801 t = Object(this), 802 len = t.length >>> 0; /* jshint ignore:line */ 803 if (len === 0) { 804 return -1; 805 } 806 807 n = len - 1; 808 if (arguments.length > 1) { 809 n = Number(arguments[1]); 810 if (n != n) { 811 n = 0; 812 } else if (n != 0 && n != ( 813 1 / 0 814 ) && n != -( 815 1 / 0 816 )) { /* jshint ignore:line */ 817 n = ( 818 n > 0 || -1 819 ) * Math.floor(Math.abs(n)); 820 } 821 } 822 823 for (k = n >= 0 ? Math.min(n, len - 1) : len - Math.abs(n); k >= 0; k--) { 824 if (k in t && t[k] === searchElement) { 825 return k; 826 } 827 } 828 return -1; 829 }; 830 } 831}; 832var 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.