frontend.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. /**
  2. * Developer's Notice:
  3. *
  4. * Note: JS in this file (and this file itself) is not garunteed 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 6.0.12
  12. */
  13. var 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. this.setLastClicked = function(valuesArray,fieldsArray,tracked){
  20. valuesArray = typeof valuesArray !== 'undefined' ? valuesArray : [];
  21. fieldsArray = typeof fieldsArray !== 'undefined' ? fieldsArray : [];
  22. tracked = typeof tracked !== 'undefined' ? tracked : false;
  23. lastClicked.valuesArray = valuesArray;
  24. lastClicked.fieldsArray = fieldsArray;
  25. };
  26. this.getLastClicked = function () {
  27. return lastClicked;
  28. };
  29. function __gaTrackerIsDebug () {
  30. if ( monsterinsights_frontend.is_debug_mode === "true" || window.monsterinsights_debug_mode ) {
  31. return true;
  32. } else {
  33. return false;
  34. }
  35. }
  36. function __gaTrackerSend ( valuesArray, fieldsArray ) {
  37. valuesArray = typeof valuesArray !== 'undefined' ? valuesArray : [];
  38. fieldsArray = typeof fieldsArray !== 'undefined' ? fieldsArray : {};
  39. __gaTracker( 'send', fieldsArray );
  40. lastClicked.valuesArray = valuesArray;
  41. lastClicked.fieldsArray = fieldsArray;
  42. lastClicked.tracked = true;
  43. __gaTrackerLog( 'Tracked: ' + valuesArray.type );
  44. __gaTrackerLog( lastClicked );
  45. }
  46. function __gaTrackerNotSend ( valuesArray ) {
  47. valuesArray = typeof valuesArray !== 'undefined' ? valuesArray : [];
  48. lastClicked.valuesArray = valuesArray;
  49. lastClicked.fieldsArray = [];
  50. lastClicked.tracked = false;
  51. __gaTrackerLog( 'Not Tracked: ' + valuesArray.exit );
  52. __gaTrackerLog( lastClicked );
  53. }
  54. function __gaTrackerLog ( message ) {
  55. if ( __gaTrackerIsDebug() ) {
  56. console.dir( message );
  57. }
  58. }
  59. function __gaTrackerStringTrim( x ) {
  60. return x.replace(/^\s+|\s+$/gm,'');
  61. }
  62. function __gaTrackerGetDomain() {
  63. var i=0,currentdomain=document.domain,p=currentdomain.split('.'),s='_gd'+(new Date()).getTime();
  64. while(i<(p.length-1) && document.cookie.indexOf(s+'='+s)==-1){
  65. currentdomain = p.slice(-1-(++i)).join('.');
  66. document.cookie = s+"="+s+";domain="+currentdomain+";";
  67. }
  68. document.cookie = s+"=;expires=Thu, 01 Jan 1970 00:00:01 GMT;domain="+currentdomain+";";
  69. return currentdomain;
  70. }
  71. function __gaTrackerGetExtension( extension ) {
  72. extension = extension.toString();
  73. extension = extension.substring( 0, (extension.indexOf( "#" ) == -1 ) ? extension.length : extension.indexOf( "#" ) ); /* Remove the anchor at the end, if there is one */
  74. extension = extension.substring( 0, (extension.indexOf( "?" ) == -1 ) ? extension.length : extension.indexOf( "?" ) ); /* Remove the query after the file name, if there is one */
  75. extension = extension.substring( extension.lastIndexOf( "/" ) + 1, extension.length ); /* Remove everything before the last slash in the path */
  76. 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.
  77. extension = extension.substring( extension.indexOf( "." ) + 1 ); /* Remove everything but what's after the first period */
  78. return extension;
  79. } else {
  80. return "";
  81. }
  82. }
  83. function __gaTrackerLoaded() {
  84. return typeof(__gaTracker) !== 'undefined' && __gaTracker && __gaTracker.hasOwnProperty( "loaded" ) && __gaTracker.loaded == true; // jshint ignore:line
  85. }
  86. function __gaTrackerTrackedClick( event ) {
  87. return event.which == 1 || event.which == 2 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
  88. }
  89. function __gaTrackerGetDownloadExtensions() {
  90. var download_extensions = [];
  91. if ( typeof monsterinsights_frontend.download_extensions == 'string' ) {
  92. download_extensions = monsterinsights_frontend.download_extensions.split(",");
  93. }
  94. return download_extensions;
  95. }
  96. function __gaTrackerGetInboundPaths() {
  97. var inbound_paths = [];
  98. if ( typeof monsterinsights_frontend.inbound_paths == 'string' ) {
  99. inbound_paths = monsterinsights_frontend.inbound_paths.split(",");
  100. }
  101. return inbound_paths;
  102. }
  103. function __gaTrackerTrackedClickType( event ) {
  104. if ( event.which == 1 ) {
  105. return 'event.which=1';
  106. } else if ( event.which == 2 ) {
  107. return 'event.which=2';
  108. } else if ( event.metaKey ){
  109. return 'metaKey';
  110. } else if ( event.ctrlKey ) {
  111. return 'ctrlKey';
  112. } else if ( event.shiftKey ) {
  113. return 'shiftKey';
  114. } else if ( event.altKey ) {
  115. return 'altKey';
  116. } else {
  117. return '';
  118. }
  119. }
  120. function __gaTrackerLinkType( el ) {
  121. var download_extensions = __gaTrackerGetDownloadExtensions();
  122. var inbound_paths = __gaTrackerGetInboundPaths();
  123. var type = 'unknown';
  124. var link = el.href;
  125. var extension = __gaTrackerGetExtension( el.href );
  126. var currentdomain = __gaTrackerGetDomain();
  127. var hostname = el.hostname;
  128. var protocol = el.protocol;
  129. var pathname = el.pathname;
  130. link = link.toString();
  131. var index, len;
  132. if ( link.match( /^javascript\:/i ) ) {
  133. type = 'internal'; // if it's a JS link, it's internal
  134. } else if ( protocol && protocol.length > 0 && ( __gaTrackerStringTrim( protocol ) == 'tel' || __gaTrackerStringTrim( protocol ) == 'tel:' ) ) { /* If it's a telephone link */
  135. type = "tel";
  136. } else if ( protocol && protocol.length > 0 && ( __gaTrackerStringTrim( protocol ) == 'mailto' || __gaTrackerStringTrim( protocol ) == 'mailto:' ) ) { /* If it's a email */
  137. type = "mailto";
  138. } else if ( hostname && currentdomain && hostname.length > 0 && currentdomain.length > 0 && ! hostname.endsWith( currentdomain ) ) { /* If it's a outbound */
  139. type = "external";
  140. } else if ( pathname && inbound_paths.length > 0 && pathname.length > 0 ) { /* If it's an internal as outbound */
  141. for ( index = 0, len = inbound_paths.length; index < len; ++index ) {
  142. if ( inbound_paths[ index ].length > 0 && pathname.startsWith( inbound_paths[ index ] ) ) {
  143. type = "internal-as-outbound";
  144. break;
  145. }
  146. }
  147. /* 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. */
  148. } else if ( hostname && window.monsterinsights_experimental_mode && hostname.length > 0 && document.domain.length > 0 && hostname !== document.domain ) { /* If it's a cross-hostname link */
  149. type = "cross-hostname";
  150. }
  151. if ( extension && ( type === 'unknown' || 'external' === type ) && download_extensions.length > 0 && extension.length > 0 ) { /* If it's a download */
  152. for ( index = 0, len = download_extensions.length; index < len; ++index ) {
  153. if ( download_extensions[ index ].length > 0 && ( link.endsWith( download_extensions[ index ] ) || download_extensions[ index ] == extension ) ) {
  154. type = "download";
  155. break;
  156. }
  157. }
  158. }
  159. if ( type === 'unknown' ) {
  160. type = 'internal';
  161. }
  162. return type;
  163. }
  164. function __gaTrackerLinkTarget( el, event ) {
  165. /* Is actual target set and not _(self|parent|top)? */
  166. var target = ( el.target && !el.target.match( /^_(self|parent|top)$/i ) ) ? el.target : false;
  167. /* Assume a target if Ctrl|shift|meta-click */
  168. if ( event.ctrlKey || event.shiftKey || event.metaKey || event.which == 2 ) {
  169. target = "_blank";
  170. }
  171. return target;
  172. }
  173. function __gaTrackerClickEvent( event ) {
  174. var el = event.srcElement || event.target;
  175. var valuesArray = [];
  176. var fieldsArray;
  177. // Start Values Array
  178. valuesArray.el = el;
  179. valuesArray.ga_loaded = __gaTrackerLoaded();
  180. valuesArray.click_type = __gaTrackerTrackedClickType( event );
  181. /* If GA is blocked or not loaded, or not main|middle|touch click then don't track */
  182. if ( ! __gaTrackerLoaded() || ! __gaTrackerTrackedClick( event ) ) {
  183. valuesArray.exit = 'loaded';
  184. __gaTrackerNotSend( valuesArray );
  185. return;
  186. }
  187. /* Loop up the DOM tree through parent elements if clicked element is not a link (eg: an image inside a link) */
  188. while ( el && (typeof el.tagName == 'undefined' || el.tagName.toLowerCase() != 'a' || ! el.href ) ) {
  189. el = el.parentNode;
  190. }
  191. /* if a link with valid href has been clicked */
  192. if ( el && el.href && ! el.hasAttribute('xlink:href') ) {
  193. var link = el.href; /* What link are we tracking */
  194. var extension = __gaTrackerGetExtension( el.href ); /* What extension is this link */
  195. var download_extensions = __gaTrackerGetDownloadExtensions(); /* Let's get the extensions to track */
  196. var inbound_paths = __gaTrackerGetInboundPaths(); /* Let's get the internal paths to track */
  197. var home_url = monsterinsights_frontend.home_url; /* Let's get the url to compare for external/internal use */
  198. var track_download_as = monsterinsights_frontend.track_download_as; /* should downloads be tracked as events or pageviews */
  199. var internal_label = "outbound-link-" + monsterinsights_frontend.internal_label; /* What is the prefix for internal-as-external links */
  200. var currentdomain = __gaTrackerGetDomain(); /* What domain are we on? */
  201. var type = __gaTrackerLinkType( el ); /* What type of link is this? */
  202. var target = __gaTrackerLinkTarget( el, event ); /* Is a new tab/window being opened? */
  203. /* Element */
  204. valuesArray.el = el; /* el is an a element so we can parse it */
  205. valuesArray.el_href = el.href; /* "http://example.com:3000/pathname/?search=test#hash" */
  206. valuesArray.el_protocol = el.protocol; /* "http:" */
  207. valuesArray.el_hostname = el.hostname; /* "example.com" */
  208. valuesArray.el_port = el.port; /* "3000" */
  209. valuesArray.el_pathname = el.pathname; /* "/pathname/" */
  210. valuesArray.el_search = el.search; /* "?search=test" */
  211. valuesArray.el_hash = el.hash; /* "#hash" */
  212. valuesArray.el_host = el.host; /* "example.com:3000" */
  213. /* Settings */
  214. valuesArray.debug_mode = __gaTrackerIsDebug(); /* "example.com:3000" */
  215. valuesArray.download_extensions = download_extensions; /* Let's get the extensions to track */
  216. valuesArray.inbound_paths = inbound_paths; /* Let's get the internal paths to track */
  217. valuesArray.home_url = home_url; /* Let's get the url to compare for external/internal use */
  218. valuesArray.track_download_as = track_download_as; /* should downloads be tracked as events or pageviews */
  219. valuesArray.internal_label = internal_label; /* What is the prefix for internal-as-external links */
  220. /* Parsed/Logic */
  221. valuesArray.link = link; /* What link are we tracking */
  222. valuesArray.extension = extension; /* What extension is this link */
  223. valuesArray.type = type; /* What type of link is this */
  224. valuesArray.target = target; /* Is a new tab/window being opened? */
  225. valuesArray.title = el.title || el.textContent || el.innerText; /* Try link title, then text content */
  226. /* Let's track everything but internals (that aren't internal-as-externals) and javascript */
  227. if ( type !== 'internal' && type !== 'javascript' ) {
  228. var __gaTrackerHitBackRun = false; /* Tracker has not yet run */
  229. /* HitCallback to open link in same window after tracker */
  230. var __gaTrackerHitBack = function() {
  231. /* Run the hitback only once */
  232. if ( __gaTrackerHitBackRun ){
  233. return;
  234. }
  235. __gaTrackerHitBackRun = true;
  236. window.location.href = link;
  237. };
  238. var __gaTrackerNoRedirectExternal = function() {
  239. valuesArray.exit = 'external';
  240. __gaTrackerNotSend( valuesArray );
  241. };
  242. var __gaTrackerNoRedirectInboundAsExternal = function() {
  243. valuesArray.exit = 'internal-as-outbound';
  244. __gaTrackerNotSend( valuesArray );
  245. };
  246. var __gaTrackerNoRedirectCrossHostname = function() {
  247. valuesArray.exit = 'cross-hostname';
  248. __gaTrackerNotSend( valuesArray );
  249. };
  250. if ( target || type == 'mailto' || type == 'tel' ) { /* If target opens a new window then just track */
  251. if ( type == 'download' ) {
  252. if ( track_download_as == 'pageview' ) {
  253. fieldsArray = {
  254. hitType : 'pageview',
  255. page : link,
  256. };
  257. __gaTrackerSend( valuesArray, fieldsArray );
  258. } else {
  259. fieldsArray = {
  260. hitType : 'event',
  261. eventCategory : 'download',
  262. eventAction : link,
  263. eventLabel : valuesArray.title,
  264. };
  265. __gaTrackerSend( valuesArray, fieldsArray );
  266. }
  267. } else if ( type == 'tel' ) {
  268. fieldsArray = {
  269. hitType : 'event',
  270. eventCategory : 'tel',
  271. eventAction : link,
  272. eventLabel : valuesArray.title.replace('tel:', ''),
  273. };
  274. __gaTrackerSend( valuesArray, fieldsArray );
  275. } else if ( type == 'mailto' ) {
  276. fieldsArray = {
  277. hitType : 'event',
  278. eventCategory : 'mailto',
  279. eventAction : link,
  280. eventLabel : valuesArray.title.replace('mailto:', ''),
  281. };
  282. __gaTrackerSend( valuesArray, fieldsArray );
  283. } else if ( type == 'internal-as-outbound' ) {
  284. fieldsArray = {
  285. hitType : 'event',
  286. eventCategory : internal_label,
  287. eventAction : link,
  288. eventLabel : valuesArray.title,
  289. };
  290. __gaTrackerSend( valuesArray, fieldsArray );
  291. } else if ( type == 'external' ) {
  292. fieldsArray = {
  293. hitType: 'event',
  294. eventCategory:'outbound-link',
  295. eventAction: link,
  296. eventLabel: valuesArray.title,
  297. };
  298. __gaTrackerSend( valuesArray, fieldsArray );
  299. } else if ( type == 'cross-hostname' ) {
  300. fieldsArray = {
  301. hitType: 'event',
  302. eventCategory:'cross-hostname',
  303. eventAction: link,
  304. eventLabel: valuesArray.title,
  305. };
  306. __gaTrackerSend( valuesArray, fieldsArray );
  307. } else {
  308. valuesArray.exit = 'type';
  309. __gaTrackerNotSend( valuesArray );
  310. }
  311. } else {
  312. /* Prevent standard click, track then open */
  313. if ( type != 'cross-hostname' && type != 'external' && type != 'internal-as-outbound' ) {
  314. if (! event.defaultPrevented ) {
  315. if ( event.preventDefault ) {
  316. event.preventDefault();
  317. } else {
  318. event.returnValue = false;
  319. }
  320. }
  321. }
  322. if ( type == 'download' ) {
  323. if ( track_download_as == 'pageview' ) {
  324. fieldsArray = {
  325. hitType : 'pageview',
  326. page : link,
  327. hitCallback : __gaTrackerHitBack,
  328. };
  329. __gaTrackerSend( valuesArray, fieldsArray );
  330. } else {
  331. fieldsArray = {
  332. hitType : 'event',
  333. eventCategory : 'download',
  334. eventAction : link,
  335. eventLabel : valuesArray.title,
  336. hitCallback : __gaTrackerHitBack,
  337. };
  338. __gaTrackerSend( valuesArray, fieldsArray );
  339. }
  340. } else if ( type == 'internal-as-outbound' ) {
  341. window.onbeforeunload = function(e) {
  342. if (! event.defaultPrevented ) {
  343. if ( event.preventDefault ) {
  344. event.preventDefault();
  345. } else {
  346. event.returnValue = false;
  347. }
  348. }
  349. fieldsArray = {
  350. hitType : 'event',
  351. eventCategory : internal_label,
  352. eventAction : link,
  353. eventLabel : valuesArray.title,
  354. hitCallback : __gaTrackerHitBack,
  355. };
  356. if ( navigator.sendBeacon ) {
  357. fieldsArray.transport = 'beacon';
  358. }
  359. __gaTrackerSend( valuesArray, fieldsArray );
  360. setTimeout( __gaTrackerHitBack, 1000 );
  361. };
  362. } else if ( type == 'external' ) {
  363. window.onbeforeunload = function(e) {
  364. if (! event.defaultPrevented ) {
  365. if ( event.preventDefault ) {
  366. event.preventDefault();
  367. } else {
  368. event.returnValue = false;
  369. }
  370. }
  371. fieldsArray = {
  372. hitType : 'event',
  373. eventCategory : 'outbound-link',
  374. eventAction : link,
  375. eventLabel : valuesArray.title,
  376. hitCallback : __gaTrackerHitBack,
  377. };
  378. if ( navigator.sendBeacon ) {
  379. fieldsArray.transport = 'beacon';
  380. }
  381. __gaTrackerSend( valuesArray, fieldsArray );
  382. setTimeout( __gaTrackerHitBack, 1000 );
  383. };
  384. } else if ( type == 'cross-hostname' ) {
  385. window.onbeforeunload = function(e) {
  386. if (! event.defaultPrevented ) {
  387. if ( event.preventDefault ) {
  388. event.preventDefault();
  389. } else {
  390. event.returnValue = false;
  391. }
  392. }
  393. fieldsArray = {
  394. hitType : 'event',
  395. eventCategory : 'cross-hostname',
  396. eventAction : link,
  397. eventLabel : valuesArray.title,
  398. hitCallback : __gaTrackerHitBack,
  399. };
  400. if ( navigator.sendBeacon ) {
  401. fieldsArray.transport = 'beacon';
  402. }
  403. __gaTrackerSend( valuesArray, fieldsArray );
  404. setTimeout( __gaTrackerHitBack, 1000 );
  405. };
  406. } else {
  407. valuesArray.exit = 'type';
  408. __gaTrackerNotSend( valuesArray );
  409. }
  410. if ( type != 'external' && type != 'cross-hostname' && type != 'internal-as-outbound' ) {
  411. /* Run hitCallback again if GA takes longer than 1 second */
  412. setTimeout( __gaTrackerHitBack, 1000 );
  413. } else {
  414. if ( type == 'external' ) {
  415. setTimeout( __gaTrackerNoRedirectExternal, 1100 );
  416. } else if ( type == 'cross-hostname' ) {
  417. setTimeout( __gaTrackerNoRedirectCrossHostname, 1100 );
  418. } else {
  419. setTimeout( __gaTrackerNoRedirectInboundAsExternal, 1100 );
  420. }
  421. }
  422. }
  423. } else {
  424. valuesArray.exit = 'internal';
  425. __gaTrackerNotSend( valuesArray );
  426. }
  427. } else {
  428. valuesArray.exit = 'notlink';
  429. __gaTrackerNotSend( valuesArray );
  430. }
  431. }
  432. var prevHash = window.location.hash;
  433. function __gaTrackerHashChangeEvent() {
  434. // Todo: Ready this section for JS unit testing
  435. if ( monsterinsights_frontend.hash_tracking === "true" && prevHash != window.location.hash ) {
  436. prevHash = window.location.hash;
  437. __gaTracker('set', 'page', location.pathname + location.search + location.hash );
  438. __gaTracker('send', 'pageview' );
  439. __gaTrackerLog( "Hash change to: " + location.pathname + location.search + location.hash );
  440. } else {
  441. __gaTrackerLog( "Hash change to (untracked): " + location.pathname + location.search + location.hash );
  442. }
  443. }
  444. /* Attach the event to all clicks in the document after page has loaded */
  445. var __gaTrackerWindow = window;
  446. if ( __gaTrackerWindow.addEventListener ) {
  447. __gaTrackerWindow.addEventListener(
  448. "load",
  449. function() {
  450. document.body.addEventListener(
  451. "click",
  452. __gaTrackerClickEvent,
  453. false
  454. );
  455. },
  456. false
  457. );
  458. window.addEventListener("hashchange", __gaTrackerHashChangeEvent, false );
  459. } else {
  460. if ( __gaTrackerWindow.attachEvent ) {
  461. __gaTrackerWindow.attachEvent(
  462. "onload",
  463. function() {
  464. document.body.attachEvent( "onclick", __gaTrackerClickEvent);
  465. }
  466. );
  467. window.attachEvent( "onhashchange", __gaTrackerHashChangeEvent);
  468. }
  469. }
  470. if (typeof String.prototype.endsWith !== 'function') {
  471. String.prototype.endsWith = function(suffix) {
  472. return this.indexOf(suffix, this.length - suffix.length) !== -1;
  473. };
  474. }
  475. if (typeof String.prototype.startsWith !== 'function') {
  476. String.prototype.startsWith = function(prefix) {
  477. return this.indexOf(prefix) === 0;
  478. };
  479. }
  480. if ( typeof Array.prototype.lastIndexOf !== 'function' ) {
  481. Array.prototype.lastIndexOf = function(searchElement /*, fromIndex*/) {
  482. 'use strict';
  483. if (this === void 0 || this === null) {
  484. throw new TypeError();
  485. }
  486. var n, k,
  487. t = Object(this),
  488. len = t.length >>> 0; // jshint ignore:line
  489. if (len === 0) {
  490. return -1;
  491. }
  492. n = len - 1;
  493. if (arguments.length > 1) {
  494. n = Number(arguments[1]);
  495. if (n != n) {
  496. n = 0;
  497. }
  498. else if (n != 0 && n != (1 / 0) && n != -(1 / 0)) { // jshint ignore:line
  499. n = (n > 0 || -1) * Math.floor(Math.abs(n));
  500. }
  501. }
  502. for (k = n >= 0 ? Math.min(n, len - 1) : len - Math.abs(n); k >= 0; k--) {
  503. if (k in t && t[k] === searchElement) {
  504. return k;
  505. }
  506. }
  507. return -1;
  508. };
  509. }
  510. };
  511. var MonsterInsightsObject = new MonsterInsights();