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 MonsterInsights = function () { 14 /* MonsterInsights 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.monsterinsights_debug_mode ) { 49 return true; 50 } else { 51 return false; 52 } 53 } 54 55 /** 56 * This attempts to be compatible with the gtag function. 57 * 58 * @see https://developers.google.com/analytics/devguides/collection/gtagjs 59 * @param type string Type of request, event, timing, config. 60 * @param action string Event action or UA for config. 61 * @param fieldsArray object The configuration object. 62 * @param valuesArray object The values for the log. 63 * @private 64 */ 65 function __gtagTrackerSend( type, action, fieldsArray, valuesArray ) { 66 type = typeof type !== 'undefined' ? type : 'event'; 67 action = typeof action !== 'undefined' ? action : ''; 68 valuesArray = typeof valuesArray !== 'undefined' ? valuesArray : []; 69 fieldsArray = typeof fieldsArray !== 'undefined' ? fieldsArray : {}; 70 71 __gtagTracker( type, action, fieldsArray ); 72 73 lastClicked.valuesArray = valuesArray; 74 lastClicked.fieldsArray = fieldsArray; 75 lastClicked.fieldsArray.event_action = action; 76 lastClicked.tracked = true; 77 __gtagTrackerLog( 'Tracked: ' + valuesArray.type ); 78 __gtagTrackerLog( lastClicked ); 79 } 80 81 function __gtagTrackerNotSend( valuesArray ) { 82 valuesArray = typeof valuesArray !== 'undefined' ? valuesArray : []; 83 84 lastClicked.valuesArray = valuesArray; 85 lastClicked.fieldsArray = []; 86 lastClicked.tracked = false; 87 __gtagTrackerLog( 'Not Tracked: ' + valuesArray.exit ); 88 __gtagTrackerLog( lastClicked ); 89 } 90 91 function __gtagTrackerLog( message ) { 92 if ( __gtagTrackerIsDebug() ) { 93 console.dir( message ); 94 } 95 } 96 97 function __gtagTrackerStringTrim( x ) { 98 return x.replace( /^\s+|\s+$/gm, '' ); 99 } 100 101 function __gtagTrackerGetDomain() { 102 var i = 0, currentdomain = document.domain, p = currentdomain.split( '.' ), s = '_gd' + ( 103 new Date() 104 ).getTime(); 105 while ( i < ( p.length - 1 ) && document.cookie.indexOf( s + '=' + s ) == - 1 ) { 106 currentdomain = p.slice( - 1 - ( 107 ++ i 108 ) ).join( '.' ); 109 document.cookie = s + "=" + s + ";domain=" + currentdomain + ";"; 110 } 111 document.cookie = s + "=;expires=Thu, 01 Jan 1970 00:00:01 GMT;domain=" + currentdomain + ";"; 112 return currentdomain; 113 } 114 115 function __gtagTrackerGetExtension( extension ) { 116 extension = extension.toString(); 117 extension = extension.substring( 0, ( 118 extension.indexOf( "#" ) == - 1 119 ) ? extension.length : extension.indexOf( "#" ) );
119 /* Remove the anchor at the end, if there is one */ 120 extension = extension.substring( 0, ( 121 extension.indexOf( "?" ) == - 1 122 ) ? extension.length : extension.indexOf( "?" ) ); /* Remove the query after the file name, if there is one */ 123 extension = extension.substring( extension.lastIndexOf( "/" ) + 1, extension.length ); /* Remove everything before the last slash in the path */ 124 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. */ 125 extension = extension.substring( extension.indexOf( "." ) + 1 ); /* Remove everything but what's after the first period */ 126 return extension; 127 } else { 128 return ""; 129 } 130 } 131 132 function __gtagTrackerTrackedClick( event ) { 133 return event.which == 1 || event.which == 2 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey; 134 } 135 136 function __gtagTrackerGetDownloadExtensions() { 137 var download_extensions = []; 138 if ( typeof monsterinsights_frontend.download_extensions == 'string' ) { 139 download_extensions = monsterinsights_frontend.download_extensions.split( "," ); 140 } 141 return download_extensions; 142 } 143 144 function __gtagTrackerGetInboundPaths() { 145 var inbound_paths = []; 146 if ( typeof monsterinsights_frontend.inbound_paths == 'string' ) { 147 inbound_paths = JSON.parse( monsterinsights_frontend.inbound_paths ); 148 } 149 150 return inbound_paths; 151 } 152 153 function __gtagTrackerTrackedClickType( event ) { 154 if ( event.which == 1 ) { 155 return 'event.which=1'; 156 } else if ( event.which == 2 ) { 157 return 'event.which=2'; 158 } else if ( event.metaKey ) { 159 return 'metaKey'; 160 } else if ( event.ctrlKey ) { 161 return 'ctrlKey'; 162 } else if ( event.shiftKey ) { 163 return 'shiftKey'; 164 } else if ( event.altKey ) { 165 return 'altKey'; 166 } else { 167 return ''; 168 } 169 } 170 171 function __gtagTrackerLinkType( el ) { 172 var download_extensions = __gtagTrackerGetDownloadExtensions(); 173 var inbound_paths = __gtagTrackerGetInboundPaths(); 174 var type = 'unknown'; 175 var link = el.href; 176 var extension = __gtagTrackerGetExtension( el.href ); 177 var currentdomain = __gtagTrackerGetDomain(); 178 var hostname = el.hostname; 179 var protocol = el.protocol; 180 var pathname = el.pathname; 181 link = link.toString(); 182 var index, len; 183 var category = el.getAttribute( "data-vars-ga-category" ); 184 185 if ( category ) { 186 return category; 187 } 188 189 if ( link.match( /^javascript\:/i ) ) { 190 type = 'internal'; /* if it's a JS link, it's internal */ 191 } else if ( protocol && protocol.length > 0 && ( 192 __gtagTrackerStringTrim( protocol ) == 'tel' || __gtagTrackerStringTrim( protocol ) == 'tel:' 193 ) ) { /* If it's a telephone link */ 194 type = "tel"; 195 } else if ( protocol && protocol.length > 0 && ( 196 __gtagTrackerStringTrim( protocol ) == 'mailto' || __gtagTrackerStringTrim( protocol ) == 'mailto:' 197 ) ) { /* If it's a email */ 198 type = "mailto"; 199 } else if ( hostname && currentdomain && hostname.length > 0 && currentdomain.length > 0 && !hostname.endsWith( '.' + currentdomain ) && hostname !== currentdomain ) { /* If it's a outbound */ 200 type = "external"; 201 } else if ( pathname && JSON.stringify( inbound_paths ) != "{}" && pathname.length > 0 ) { /* If it's an internal as outbound */ 202 var inbound_paths_length = inbound_paths.length; 203 for ( var inbound_paths_index = 0; inbound_paths_index < inbound_paths_length; inbound_paths_index ++ ) { 204 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 ) ) { 205 type = "internal-as-outbound"; 206 internalAsOutboundCategory = "outbound-link-" + inbound_paths[inbound_paths_index].label; 207 break; 208 } 209 } 210 /* Enable window.monsterinsights_experimental_mode at your own risk. We might eventually remove it. Also you may/can/will burn through GA quota for your property quickly. */ 211 } else if ( hostname && window.monsterinsights_experimental_mode && hostname.length > 0 && document.domain.length > 0 && hostname !== document.domain ) { /* If it's a cross-hostname link */ 212 type = "cross-hostname"; 213 } 214 215 if ( extension && ( 216 type === 'unknown' || 'external' === type 217 ) && download_extensions.length > 0 && extension.length > 0 ) { /* If it's a download */ 218 for ( index = 0, len = download_extensions.length; index < len; ++ index ) { 219 if ( download_extensions[index].length > 0 && ( 220 link.endsWith( download_extensions[index] ) || download_extensions[index] == extension 221 ) ) { 222 type = "download"; 223 break; 224 } 225 } 226 } 227 228 if ( type === 'unknown' ) { 229 type = 'internal'; 230 } 231 return type; 232 } 233 234 function __gtagTrackerLinkTarget( el, event ) { 235 236 /* Is actual target set and not _(self|parent|top)? */ 237 var target = ( 238 el.target && !el.target.match( /^_(self|parent|top)$/i ) 239 ) ? el.target : false;
240 241 /* Assume a target if Ctrl|shift|meta-click */ 242 if ( event.ctrlKey || event.shiftKey || event.metaKey || event.which == 2 ) { 243 target = "_blank"; 244 } 245 return target; 246 } 247 248 function __gtagTrackerGetTitle( el ) { 249 if ( el.getAttribute( "data-vars-ga-label" ) && el.getAttribute( "data-vars-ga-label" ).replace( /\n/ig, '' ) ) { 250 return el.getAttribute( "data-vars-ga-label" ).replace( /\n/ig, '' ); 251 } else if ( el.title && el.title.replace( /\n/ig, '' ) ) { 252 return el.title.replace( /\n/ig, '' ); 253 } else if ( el.innerText && el.innerText.replace( /\n/ig, '' ) ) { 254 return el.innerText.replace( /\n/ig, '' ); 255 } else if ( el.getAttribute( 'aria-label' ) && el.getAttribute( 'aria-label' ).replace( /\n/ig, '' ) ) { 256 return el.getAttribute( 'aria-label' ).replace( /\n/ig, '' ); 257 } else if ( el.alt && el.alt.replace( /\n/ig, '' ) ) { 258 return el.alt.replace( /\n/ig, '' ); 259 } else if ( el.textContent && el.textContent.replace( /\n/ig, '' ) ) { 260 return el.textContent.replace( /\n/ig, '' ); 261 } else { 262 return undefined; 263 } 264 } 265 266 function __gtagTrackerGetInnerTitle( el ) { 267 var children = el.children; 268 var count = 0; 269 var child; 270 var value; 271 for ( var i = 0; i < children.length; i ++ ) { 272 child = children[i]; 273 value = __gtagTrackerGetTitle( child ); 274 if ( value ) { 275 return value; 276 } 277 /* max search 100 elements to ensure performance */ 278 if ( count == 99 ) { 279 return undefined; 280 } 281 count ++; 282 } 283 return undefined; 284 } 285 286 function __gtagTrackerClickEvent( event ) { 287 var el = event.srcElement || event.target; 288 var valuesArray = []; 289 var fieldsArray; 290 291 /* Start Values Array */ 292 valuesArray.el = el; 293 valuesArray.click_type = __gtagTrackerTrackedClickType( event ); 294 295 /* If GA is blocked or not loaded, or not main|middle|touch click then don't track */ 296 if ( 'undefined' === typeof __gtagTracker || ! __gtagTrackerTrackedClick( event ) ) { 297 valuesArray.exit = 'loaded'; 298 __gtagTrackerNotSend( valuesArray ); 299 return; 300 } 301 302 /* Loop up the DOM tree through parent elements if clicked element is not a link (eg: an image inside a link) */ 303 while ( el && ( 304 typeof el.tagName == 'undefined' || el.tagName.toLowerCase() != 'a' || !el.href 305 ) ) { 306 el = el.parentNode; 307 } 308 309 /* if a link with valid href has been clicked */ 310 if ( el && el.href && !el.hasAttribute( 'xlink:href' ) ) { 311 var link = el.href; /* What link are we tracking */ 312 var extension = __gtagTrackerGetExtension( el.href ); /* What extension is this link */ 313 var download_extensions = __gtagTrackerGetDownloadExtensions(); /* Let's get the extensions to track */ 314 var inbound_paths = __gtagTrackerGetInboundPaths(); /* Let's get the internal paths to track */ 315 var home_url = monsterinsights_frontend.home_url; /* Let's get the url to compare for external/internal use */ 316 var currentdomain = __gtagTrackerGetDomain(); /* What domain are we on? */ 317 var type = __gtagTrackerLinkType( el ); /* What type of link is this? */ 318 var target = __gtagTrackerLinkTarget( el, event ); /* Is a new tab/window being opened? */ 319 var action = el.getAttribute( "data-vars-ga-action" ); 320 var label = el.getAttribute( "data-vars-ga-label" ); 321 322 /* Element */ 323 valuesArray.el = el; /* el is an a element so we can parse it */ 324 valuesArray.el_href = el.href; /* "http://example.com:3000/pathname/?search=test#hash" */ 325 valuesArray.el_protocol = el.protocol; /* "http:" */ 326 valuesArray.el_hostname = el.hostname; /* "example.com" */ 327 valuesArray.el_port = el.port; /* "3000" */ 328 valuesArray.el_pathname = el.pathname; /* "/pathname/" */ 329 valuesArray.el_search = el.search; /* "?search=test" */ 330 valuesArray.el_hash = el.hash; /* "#hash" */ 331 valuesArray.el_host = el.host; /* "example.com:3000" */ 332 333 /* Settings */ 334 valuesArray.debug_mode = __gtagTrackerIsDebug(); /* "example.com:3000" */ 335 valuesArray.download_extensions = download_extensions; /* Let's get the extensions to track */ 336 valuesArray.inbound_paths = inbound_paths; /* Let's get the internal paths to track */ 337 valuesArray.home_url = home_url; /* Let's get the url to compare for external/internal use */ 338 339 /* Parsed/Logic */ 340 valuesArray.link = link; /* What link are we tracking */ 341 valuesArray.extension = extension; /* What extension is this link */ 342 valuesArray.type = type; /* What type of link is this */ 343 valuesArray.target = target; /* Is a new tab/window being opened? */ 344 valuesArray.title = __gtagTrackerGetTitle( el ); /* Try link title, then text content */ 345 346 /* only find innerTitle if we need one */ 347 if ( ! valuesArray.label && !valuesArray.title ) { 348 valuesArray.title = __gtagTrackerGetInnerTitle( el ); 349 } 350 351 /* Let's track everything but internals (that aren't internal-as-externals) and javascript */
352 if ( type !== 'internal' && type !== 'javascript' ) { 353 354 var __gtagTrackerHitBackRun = false; /* Tracker has not yet run */ 355 356 /* HitCallback to open link in same window after tracker */ 357 var __gtagTrackerHitBack = function () { 358 /* Run the hitback only once */ 359 if ( __gtagTrackerHitBackRun ) { 360 return; 361 } 362 maybePreventBeforeUnload(); 363 __gtagTrackerHitBackRun = true; 364 window.location.href = link; 365 }; 366 367 var __gtagTrackerNoRedirectExternal = function () { 368 valuesArray.exit = 'external'; 369 __gtagTrackerNotSend( valuesArray ); 370 }; 371 372 var __gtagTrackerNoRedirectInboundAsExternal = function () { 373 valuesArray.exit = 'internal-as-outbound'; 374 __gtagTrackerNotSend( valuesArray ); 375 }; 376 var __gtagTrackerNoRedirectCrossHostname = function () { 377 valuesArray.exit = 'cross-hostname'; 378 __gtagTrackerNotSend( valuesArray ); 379 }; 380 381 if ( target || type == 'mailto' || type == 'tel' ) { /* If target opens a new window then just track */ 382 if ( type == 'download' ) { 383 fieldsArray = { 384 event_category: 'download', 385 event_label: label || valuesArray.title, 386 }; 387 } else if ( type == 'tel' ) { 388 fieldsArray = { 389 event_category: 'tel', 390 event_label: label || valuesArray.title.replace( 'tel:', '' ), 391 }; 392 } else if ( type == 'mailto' ) { 393 console.log( label || valuesArray.title.replace( 'mailto:', '' ) ); 394 fieldsArray = { 395 event_category: 'mailto', 396 event_label: label || valuesArray.title.replace( 'mailto:', '' ), 397 }; 398 } else if ( type == 'internal-as-outbound' ) { 399 fieldsArray = { 400 event_category: internalAsOutboundCategory, 401 event_label: label || valuesArray.title, 402 }; 403 } else if ( type == 'external' ) { 404 fieldsArray = { 405 event_category: 'outbound-link', 406 event_label: label || valuesArray.title, 407 }; 408 } else if ( type == 'cross-hostname' ) { 409 fieldsArray = { 410 event_category: 'cross-hostname', 411 event_label: label || valuesArray.title, 412 }; 413 } 414 415 if ( fieldsArray ) { 416 __gtagTrackerSend( 'event', action || link, fieldsArray, valuesArray ); 417 } else { 418 if ( type && type != 'internal' ) { 419 fieldsArray = { 420 event_category: type, 421 event_label: label || valuesArray.title, 422 }; 423 424 __gtagTrackerSend( 'event', action || link, fieldsArray, valuesArray ); 425 } else { 426 valuesArray.exit = 'type'; 427 __gtagTrackerNotSend( valuesArray ); 428 } 429 } 430 } else { 431 /* Prevent standard click, track then open */ 432 if ( type != 'cross-hostname' && type != 'external' && type != 'internal-as-outbound' ) { 433 if ( !event.defaultPrevented ) { 434 if ( event.preventDefault ) { 435 event.preventDefault(); 436 } else { 437 event.returnValue = false; 438 } 439 } 440 } 441 442 if ( type == 'download' ) { 443 fieldsArray = { 444 event_category: 'download', 445 event_label: label || valuesArray.title, 446 event_callback: __gtagTrackerHitBack, 447 }; 448 449 __gtagTrackerSend( 'event', action || link, fieldsArray, valuesArray ); 450 } else if ( type == 'internal-as-outbound' ) { 451 beforeUnloadChanged = true; 452 window.onbeforeunload = function ( e ) { 453 if ( !event.defaultPrevented ) { 454 if ( event.preventDefault ) { 455 event.preventDefault(); 456 } else { 457 event.returnValue = false; 458 } 459 } 460 461 fieldsArray = { 462 event_category: internalAsOutboundCategory, 463 event_label: label || valuesArray.title, 464 event_callback: __gtagTrackerHitBack, 465 }; 466 467 if ( navigator.sendBeacon ) { 468 fieldsArray.transport = 'beacon'; 469 } 470 471 __gtagTrackerSend( 'event', action || link, fieldsArray, valuesArray ); 472 setTimeout( __gtagTrackerHitBack, 1000 ); 473 }; 474 } else if ( type == 'external' ) { 475 beforeUnloadChanged = true; 476 window.onbeforeunload = function ( e ) { 477 if ( !event.defaultPrevented ) { 478 if ( event.preventDefault ) { 479 event.preventDefault(); 480 } else { 481 event.returnValue = false;
482 } 483 } 484 485 fieldsArray = { 486 event_category: 'outbound-link', 487 event_label: label || valuesArray.title, 488 event_callback: __gtagTrackerHitBack, 489 }; 490 491 if ( navigator.sendBeacon ) { 492 fieldsArray.transport = 'beacon'; 493 } 494 495 __gtagTrackerSend( 'event', action || link, fieldsArray, valuesArray ); 496 setTimeout( __gtagTrackerHitBack, 1000 ); 497 }; 498 } else if ( type == 'cross-hostname' ) { 499 beforeUnloadChanged = true; 500 window.onbeforeunload = function ( e ) { 501 if ( !event.defaultPrevented ) { 502 if ( event.preventDefault ) { 503 event.preventDefault(); 504 } else { 505 event.returnValue = false; 506 } 507 } 508 509 fieldsArray = { 510 event_category: 'cross-hostname', 511 event_label: label || valuesArray.title, 512 event_callback: __gtagTrackerHitBack, 513 }; 514 515 if ( navigator.sendBeacon ) { 516 fieldsArray.transport = 'beacon'; 517 } 518 519 __gtagTrackerSend( 'event', action || link, fieldsArray, valuesArray ); 520 setTimeout( __gtagTrackerHitBack, 1000 ); 521 }; 522 } else { 523 if ( type && type !== 'internal' ) { 524 fieldsArray = { 525 event_category: type, 526 event_label: label || valuesArray.title, 527 event_callback: __gtagTrackerHitBack, 528 }; 529 530 __gtagTrackerSend( 'event', action || link, fieldsArray, valuesArray ); 531 } else { 532 valuesArray.exit = 'type'; 533 __gtagTrackerNotSend( valuesArray ); 534 } 535 } 536 537 if ( type != 'external' && type != 'cross-hostname' && type != 'internal-as-outbound' ) { 538 /* Run event_callback again if GA takes longer than 1 second */ 539 setTimeout( __gtagTrackerHitBack, 1000 ); 540 } else { 541 if ( type == 'external' ) { 542 setTimeout( __gtagTrackerNoRedirectExternal, 1100 ); 543 } else if ( type == 'cross-hostname' ) { 544 setTimeout( __gtagTrackerNoRedirectCrossHostname, 1100 ); 545 } else { 546 setTimeout( __gtagTrackerNoRedirectInboundAsExternal, 1100 ); 547 } 548 } 549 550 // Clear out the beforeunload event if it was set to avoid sending false events. 551 setTimeout( maybePreventBeforeUnload, 100 ); 552 } 553 } else { 554 maybePreventBeforeUnload(); 555 valuesArray.exit = 'internal'; 556 __gtagTrackerNotSend( valuesArray ); 557 } 558 } else { 559 valuesArray.exit = 'notlink'; 560 __gtagTrackerNotSend( valuesArray ); 561 } 562 } 563 564 var prevHash = window.location.hash; 565 566 function __gtagTrackerHashChangeEvent() { 567 /* Todo: Ready this section for JS unit testing */ 568 if ( monsterinsights_frontend.hash_tracking === "true" && prevHash != window.location.hash && monsterinsights_frontend.ua ) { 569 prevHash = window.location.hash; 570 __gtagTracker( 'config', monsterinsights_frontend.ua, { 571 page_path: location.pathname + location.search + location.hash, 572 } ) 573 __gtagTrackerLog( "Hash change to: " + location.pathname + location.search + location.hash ); 574 } else { 575 __gtagTrackerLog( "Hash change to (untracked): " + location.pathname + location.search + location.hash ); 576 } 577 } 578 579 function maybePreventBeforeUnload() { 580 if ( beforeUnloadChanged ) { 581 window.onbeforeunload = null; 582 } 583 } 584 585 /* Attach the event to all clicks in the document after page has loaded */ 586 var __gtagTrackerWindow = window; 587 if ( __gtagTrackerWindow.addEventListener ) { 588 __gtagTrackerWindow.addEventListener( 589 "load", 590 function () { 591 document.body.addEventListener( 592 "click", 593 __gtagTrackerClickEvent, 594 false 595 ); 596 }, 597 false 598 ); 599 window.addEventListener( "hashchange", __gtagTrackerHashChangeEvent, false ); 600 } else { 601 if ( __gtagTrackerWindow.attachEvent ) { 602 __gtagTrackerWindow.attachEvent( 603 "onload", 604 function () { 605 document.body.attachEvent( "onclick", __gtagTrackerClickEvent ); 606 } 607 ); 608 window.attachEvent( "onhashchange", __gtagTrackerHashChangeEvent ); 609 } 610 } 611 612 if ( typeof String.prototype.endsWith !== 'function' ) { 613 String.prototype.endsWith = function ( suffix ) { 614 return this.indexOf( suffix, this.length - suffix.length ) !== - 1; 615 }; 616 } 617 if ( typeof String.prototype.startsWith !== 'function' ) { 618 String.prototype.startsWith = function ( prefix ) { 619 return this.indexOf( prefix ) === 0; 620 }; 621 } 622 623 if ( typeof Array.prototype.lastIndexOf !== 'function' ) { 624 Array.prototype.lastIndexOf = function ( searchElement /*, fromIndex*/ ) { 625 'use strict'; 626 627 if ( this === void 0 || this === null ) { 628 throw new TypeError(); 629 } 630 631 var n, k, 632 t = Object( this ), 633 len = t.length >>> 0; /* jshint ignore:line */ 634 if ( len === 0 ) { 635 return - 1; 636 } 637 638 n = len - 1; 639 if ( arguments.length > 1 ) { 640 n = Number( arguments[1] ); 641 if ( n != n ) { 642 n = 0; 643 } else if ( n != 0 && n != ( 644 1 / 0 645 ) && n != - ( 646 1 / 0 647 ) ) { /* jshint ignore:line */ 648 n = ( 649 n > 0 || - 1 650 ) * Math.floor( Math.abs( n ) ); 651 } 652 } 653 654 for ( k = n >= 0 ? Math.min( n, len - 1 ) : len - Math.abs( n ); k >= 0; k -- ) { 655 if ( k in t && t[k] === searchElement ) { 656 return k; 657 } 658 } 659 return - 1; 660 }; 661 } 662}; 663var MonsterInsightsObject = new MonsterInsights();
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.