1/* Minification failed. Returning unminified contents. 2(154,51-52): run-time error JS1014: Invalid character: ` 3(155,21-22): run-time error JS1100: Expected ',': : 4(622,33-34): run-time error JS1014: Invalid character: ` 5(622,35-36): run-time error JS1004: Expected ';': { 6(622,50-51): run-time error JS1195: Expected expression: / 7(622,55-56): run-time error JS1014: Invalid character: ` 8(623,9-10): run-time error JS1002: Syntax error: } 9(624,33-34): run-time error JS1014: Invalid character: ` 10(624,44-45): run-time error JS1004: Expected ';': { 11(624,59-60): run-time error JS1014: Invalid character: ` 12(625,9-10): run-time error JS1002: Syntax error: } 13(633,29-30): run-time error JS1004: Expected ';': { 14(634,16-17): run-time error JS1014: Invalid character: ` 15(634,22-23): run-time error JS1004: Expected ';': : 16(638,16-17): run-time error JS1014: Invalid character: ` 17(638,22-23): run-time error JS1004: Expected ';': : 18(688,19-20): run-time error JS1014: Invalid character: ` 19(688,25-26): run-time error JS1004: Expected ';': : 20(1097,1-2): run-time error JS1002: Syntax error: } 21(1098,12-13): run-time error JS1195: Expected expression: ) 22(1098,14-15): run-time error JS1004: Expected ';': { 23(1145,2-3): run-time error JS1195: Expected expression: ) 24(1146,12-13): run-time error JS1195: Expected expression: ) 25(1146,14-15): run-time error JS1004: Expected ';': { 26(1292,2-3): run-time error JS1195: Expected expression: ) 27(1314,16-17): run-time error JS1004: Expected ';': { 28(1572,2-3): run-time error JS1195: Expected expression: ) 29(1574,12-13): run-time error JS1195: Expected expression: ) 30(1574,14-15): run-time error JS1004: Expected ';': { 31(1625,2-3): run-time error JS1195: Expected expression: ) 32(1625,5-6): run-time error JS1197: Too many errors. The file might not be a JavaScript file: ; 33(634,9-22): run-time error JS1018: 'return' statement outside of function: return `https 34 */ 35(function () { 36 const TEMPLATE_TOP_MENU = "/public/templates/mx-top-menu/mx-top-menu.html"; 37 const TEMPLATE_USER = "/public/templates/mx-top-menu/mx-top-menu-user.html"; 38 const TEMPLATE_ANONYMOUS = "#mx-tm-template-anonymous"; 39 const LOCATION_KEY = MXT.currentLocation || 'supertool'; 40 41 let _menu_element = "#mx-top-menu"; 42 let _user_element = "#mx-tm-user"; 43 44 let _active_menus = {}; 45 let _active_primary = undefined; 46 47 // When jquery is ready 48 window.addEventListener('load', function () { 49 // If we are in the top window, initialize the menu 50 // otherwise, this is being loaded in an iframe and we don't want to initialize the menu 51 if (window.self === window.top) { 52 Init(); 53 $('.mx-tm-cloak').removeClass('mx-tm-cloak'); 54 } else { 55 $('#mx-top-menu').hide(); 56 $('footer').hide(); 57 } 58 }); 59 60 function Init() { 61 SetActiveMenus(); 62 LoadTemplate({ 63 destination_selector: _menu_element, 64 template_url: TEMPLATE_TOP_MENU, 65 data: { 66 menus: _active_menus, 67 config: { 68 isWorking: false, 69 logo_destination: '/SuperTool.aspx', 70 loading: false, 71 logged_in: true, 72 show_second_bar: true, 73 active_nav: undefined 74 } 75 } 76 }, function () { 77 LoadUser(); 78 // Hamburger submenu toggle for Resources 79 $(document).on('click', '.mx-tm-hamburger-menu .has-submenu-link', function(e) { 80 e.preventDefault(); 81 e.stopPropagation(); 82 var $submenu = $(this).siblings('.submenu'); 83 $('.mx-tm-hamburger-menu .submenu').not($submenu).slideUp(200); 84 $submenu.slideToggle(200); 85 }); 86 }); 87 } 88 89 function LoadTemplate(item, callback) { 90 if (item.template_url) { 91 if (item.template_url[0] === '#') { 92 RenderContent(item.destination_selector, $(item.template_url).html(), item.data); 93 if (callback) { callback() }; 94 } else { 95 $.ajax(item.template_url, { 96 async: false, 97 cache: true, 98 dataType: 'html', 99 type: 'GET', 100 success: function (content) { 101 RenderContent(item.destination_selector, content, item.data); 102 if (callback) { callback() }; 103 }, 104 error: function (xhr, status, error) { 105 console.log('Error loading template: ' + item.template_url); 106 } 107 }); 108 } 109 } 110 } 111
112 function RenderContent(selector, template_html, data) { 113 // Uses jquery's 'template' system to set the HTML in it's cacheing system, if it's not already there. 114 $.templates({ tmpl: template_html }); 115 116 // Use jsRender to combine the HTML template and data, then insert the rendered template into the selector element. 117 $(selector).html($.render.tmpl(data)); 118 } 119 120 function LoadUser() { 121 // Render current cookie while we wait 122 LoadUserTemplate(JSON.parse(MXT.getCookie('_mxt_u'))); 123 MXT.OnUserLoaded(function (event, content) { 124 LoadUserTemplate(content); 125 }); 126 } 127 128 function LoadUserTemplate(content) { 129 LoadTemplate({ 130 destination_selector: _user_element, 131 template_url: (content && content.IsLoggedIn) ? TEMPLATE_USER : TEMPLATE_ANONYMOUS, 132 data: { 133 user: content, 134 user_menu: _active_menus.user 135 } 136 }); 137 } 138 139 function SetActiveMenus() { 140 let path = window.location.pathname.toLowerCase(); 141 142 _active_menus.user = GetUserMenu(); 143 _active_menus.primary = GetPrimaryMenu(LOCATION_KEY); 144 _active_menus.site = GetSiteMenu(_active_primary.site); 145 _active_menus.secondary = GetSecondaryMenu(path, _active_primary.id); 146 } 147 148 function GetUserMenu() { 149 let user_menu_config = [ 150 { 151 id: 'settings', 152 text: 'Settings', 153 url: '/user' 154 }, 155 { 156 id: 'billing', 157 text: 'Billing', 158 url: '/user/billing.aspx' 159 }, 160 { 161 id: 'support', 162 text: 'Support', 163 url: 'https://knowledgebase.mxtoolbox.com/home' 164 } 165 ]; 166 return user_menu_config; 167 } 168 169 function GetPrimaryMenu(location_key) { 170 let primary_menu_config = [ 171 { 172 id: 'pricing', 173 text: 'Pricing', 174 url: '/c/products/matrixdos?feature=no-feature-selected&source=pricing', 175 grep: '/c/products', 176 site: 'supertool' 177 }, 178 { 179 id: 'supertool', 180 text: 'Tools', 181 url: '/SuperTool.aspx', 182 grep: ['','/SuperTool.aspx'], 183 site: 'supertool' 184 }, 185 { 186 id: 'delivery', 187 text: 'Delivery Center', 188 url: (MXT && MXT.DELIVERY_HOST) ? `https://${MXT.DELIVERY_HOST}` : 'https://delivery.mxtoolbox.com', 189 site: 'dc2' 190 }, 191 { 192 id: 'monitoring', 193 text: 'Monitoring', 194 url: '/monitoring', 195 site: 'monitoring' 196 }, 197 { 198 id: 'products', 199 text: 'Products', 200 url: '/c/mxtoolboxproducts', 201 site: 'supertool' 202 }, 203 { 204 id: 'resources', 205 text: 'Resources', 206 url: 'https://blog.mxtoolbox.com?source=greynav', 207 site: 'supertool', 208 resources: [ 209 { text: 'API Reference', url: '/api/api-reference' }, 210 { text: 'Blog', url: 'https://blog.mxtoolbox.com/?source=greynav' } 211 ] 212 } 213 ]; 214 215 // Set active property on primary menu items 216 primary_menu_config.forEach(function(item) { 217 item.active = item.id === location_key; 218 if (item.active) { 219 _active_primary = item; 220 } 221 }); 222 223 if (location_key === 'user' || location_key === 'support') { 224 _active_primary = { 225 id: 'user', 226 site: 'supertool' 227 }; 228 } 229 230 return primary_menu_config; 231 } 232 233 function GetSiteMenu(site_key) { 234 let site_menu_config = { 235 'supertool' : { 236 class: 'fa-duotone fa-toolbox', 237 name: 'SuperTool', 238 }, 239 'monitoring' : { 240 class: 'fa-duotone fa-display-chart-up', 241 name: 'Monitoring', 242 }, 243 'dc2' : { 244 class: 'fa-duotone fa-envelope', 245 name: 'Delivery Center', 246 } 247 }; 248 249 return site_menu_config[site_key]; 250 } 251 252 function GetSecondaryMenu(path, primary_key) { 253 let secondary_menu_config; 254 255 switch (primary_key) { 256 case 'pricing': 257 secondary_menu_config = GetSecondaryMenu_Products(); 258 break; 259 case 'tools': 260 secondary_menu_config = GetSecondaryMenu_Supertool(); 261 break; 262 case 'delivery': 263 secondary_menu_config = GetSecondaryMenu_Delivery(); 264 break; 265 case 'monitoring': 266 secondary_menu_config = GetSecondaryMenu_Monitoring(); 267 break; 268 case 'products': 269 secondary_menu_config = GetSecondaryMenu_Products(); 270 break; 271 case 'blog': 272 secondary_menu_config = GetSecondaryMenu_Blog(); 273 break;
274 case 'support': 275 secondary_menu_config = GetSecondaryMenu_Support(); 276 break; 277 case 'user': 278 secondary_menu_config = GetSecondaryMenu_User(); 279 break; 280 default: 281 secondary_menu_config = GetSecondaryMenu_Supertool(); 282 } 283 284 secondary_menu_config.left.forEach(fn_set_active(path)); 285 secondary_menu_config.right.forEach(fn_set_active(path)); 286 287 return secondary_menu_config; 288 } 289 290 function GetSecondaryMenu_Supertool() { 291 let secondary_menu_config = { 292 'site': 'supertool', 293 'left': [ 294 { 295 id: 'supertool', 296 text: 'SuperTool', 297 url: '/SuperTool.aspx', 298 grep: ['','/SuperTool.aspx'] 299 }, 300 { 301 id: 'mxlookup', 302 text: 'MX Lookup', 303 url: '/MXLookup.aspx' 304 }, 305 { 306 id: 'blacklists', 307 text: 'Blacklists', 308 url: '/blacklists.aspx' 309 }, 310 { 311 id: 'dmarc', 312 text: 'DMARC', 313 url: '/dmarc.aspx' 314 }, 315 { 316 id: 'diagnostics', 317 text: 'Diagnostics', 318 url: '/diagnostic.aspx' 319 }, 320 { 321 id: 'domain', 322 text: 'Email Health', 323 url: '/emailhealth' 324 }, 325 { 326 id: 'dnslookup', 327 text: 'DNS Lookup', 328 url: '/DnsLookup.aspx' 329 }, 330 { 331 id: 'headers', 332 text: 'Analyze Headers', 333 url: '/EmailHeaders.aspx' 334 } 335 ], 336 'right': [ 337 { 338 id: 'alltools', 339 text: 'All Tools', 340 url: '/NetworkTools.aspx' 341 } 342 ] 343 }; 344 return secondary_menu_config; 345 } 346 347 function GetSecondaryMenu_User() { 348 let secondary_menu_config = { 349 'site': 'monitoring', 350 'left': [ 351 { 352 text: 'Account Settings', 353 url: '/user/account' 354 }, 355 { 356 text: 'Summary Emails', 357 url: '/user/email' 358 }, 359 { 360 text: '2 Step Verification', 361 url: '/user/twostepverification', 362 class: 'mfa' 363 }, 364 { 365 text: 'Ignored Problems', 366 url: '/user/problems' 367 }, 368 { 369 text: 'Billing', 370 url: '/user/billing.aspx' 371 }, { 372 text: 'API', 373 url: '/api/api-reference' 374 } 375 ], 376 'right': [ 377 { 378 text: 'General Settings', 379 url: '/user/general', 380 class: 'general' 381 } 382 ] 383 }; 384 return secondary_menu_config; 385 } 386 387 function GetSecondaryMenu_Monitoring() { 388 let secondary_menu_config = { 389 'site': 'monitoring', 390 'left': [ 391 { 392 id: 'status', 393 text: 'Status', 394 url: '/monitoring/status' 395 }, 396 { 397 id: 'notifications', 398 text: 'Notifications', 399 url: '/monitoring/notifications' 400 }, 401 { 402 id: 'tag', 403 text: 'Tags', 404 url: '/monitoring/tag/' 405 }, 406 { 407 id: 'history', 408 text: 'History', 409 url: '/monitoring/history' 410 }, 411 { 412 id: 'availability', 413 text: 'Availability', 414 url: '/monitoring/availability' 415 }, 416 { 417 id: 'outages', 418 text: 'Outages', 419 url: '/monitoring/outages' 420 }, 421 { 422 id: 'addmonitor', 423 text: 'Add Monitor', 424 url: '/monitoring/add' 425 } 426 ], 427 'right': [ 428 { 429 id: 'alldomains', 430 text: 'All Monitors', 431 url: '/monitoring' 432 } 433 ] 434 }; 435 return secondary_menu_config; 436 } 437 438 function GetSecondaryMenu_Products() { 439 let secondary_menu_config = { 440 'site': 'supertool', 441 'left': [ 442 { 443 id: 'blsolutions', 444 text: 'Blacklist Solutions', 445 url: '/c/products/matrixdos?source=blackbar', 446 grep: '/c/products/matrixdos' 447 }, 448 { 449 id: 'deliverycenter', 450 text: 'Delivery Center', 451 url: '/c/products/deliverycenter?source=blackbar', 452 grep: '/c/products/deliverycenter' 453 }, 454 { 455 id: 'deliverycenterplus', 456 text: 'Delivery Center Plus', 457 url: '/c/products/deliverycenterplus?source=blackbar', 458 grep: '/c/products/deliverycenterplus' 459 }, 460 { 461 id: 'managedservices', 462 text: 'Managed Services', 463 url: '/c/products/deliverymanaged?source=blackbar', 464 grep: '/c/products/deliverymanaged' 465 }, 466 { 467 id: 'mailflow', 468 text: 'Mailflow Monitoring', 469 url: '/c/products/mailflowmonitoring?source=blackbar', 470 grep: '/c/products/mailflowmonitoring' 471 }, 472 { 473 id: 'bulk', 474 text: 'Bulk Lookups', 475 url: '/c/products/bulk?source=blackbar', 476 grep: '/c/products/bulk' 477 } 478 ], 479 'right': [ 480 { 481 id: 'allproducts', 482 text: 'All Products', 483 url: '/c/mxtoolboxproducts' 484 } 485 ] 486 }; 487 return secondary_menu_config; 488 } 489 490 function GetSecondaryMenu_Blog() { 491 let secondary_menu_config = { 492 'site': 'monitoring', 493 'left': [ 494 { 495 id: 'blog', 496 text: 'Blog', 497 url: 'https://blog.mxtoolbox.com?source=greynav' 498 } 499 ] 500 }; 501 return secondary_menu_config; 502 } 503 504 function GetSecondaryMenu_Support() { 505 let secondary_menu_config = { 506 'site': 'monitoring', 507 'left': [ 508 { 509 id: 'knowledgebase', 510 text: 'Knowledgebase', 511 url: '/DMARC/knowledgebase', 512 grep: ['/knowledgebase'] 513 }, 514 { 515 id: 'status', 516 text: 'Status', 517 url: 'https://mxtoolbox.statuspage.io/' 518 } 519 ], 520 'right': [ 521 { 522 id: 'request', 523 text: 'Support Request', 524 url: '/support' 525 } 526 ] 527 }; 528 return secondary_menu_config; 529 } 530 531 function fn_set_active(path) { 532 // Returns a FUNCTION that sets the active property of an item based on the path 533 return function(item) { 534 // See if there is a .grep or .url to match against 535 let find = []; 536 537 // If there is a .grep property, add it to the find array 538 if (typeof item.grep != 'undefined') { 539 // If the grep property is an array, add each item to the find array 540 if (typeof item.grep == 'object') { 541 find = find.concat(item.grep); 542 } else { 543 find.push(item.grep); 544 } 545 } 546 547 // Add the url to the find array 548 find.push(item.url); 549 550 // If any of the find items are in the path, set the active property to true 551 item.active = find.some(function (x) { 552 if (x === '') { 553 return path === '/' || path === ''; 554 } else { 555 return path.indexOf(x.toLowerCase()) > -1; 556 } 557 }); 558 } 559 } 560})();; 561(function () {
562 // Do not copy this this script is intended to be unique 563 // and shared among all the master pages 564 // all the functions here must be declared here and only here 565 566 window.MXT = window.MXT || {}; 567 var _postRun = { 568 functions: [], 569 hasRun: false 570 }; 571 let _adminConsoleMsgs = []; 572 // =============== Temp Shared Code 573 // This may be replaced with MXT.WriteLog 574 // In the meantime we need it for AB variation calls 575 window.MxErrorHandler = function (input, details, e) { 576 /* Remove this restriction for now - we can put it back in the future 577 if (input.indexOf("VWO") === -1 && details.indexOf("VWO") === -1) { 578 // Only engage for Errors that contain "VWO" 579 return false; 580 } 581 */ 582 583 if (typeof e === "undefined") { 584 e = "undefined"; 585 } 586 587 var userAgent = ""; 588 589 if (navigator) { 590 if (navigator.userAgent) { 591 userAgent = navigator.userAgent; 592 } 593 } 594 595 var params = { 596 "input": input, 597 "details": details, 598 "stackTrace": e, 599 "userAgent": userAgent 600 }; 601 $.ajax({ 602 type: "POST", 603 url: GetMxWebsite() + "Public/Lookup.aspx/ReportException3", 604 data: JSON.stringify(params), 605 contentType: "application/json; charset=utf-8", 606 dataType: "json", 607 success: function (msg) { 608 //console.log(msg); 609 }, 610 error: function (err) { 611 //console.log(err); 612 } 613 }); 614 615 }; 616 617 // =============== MXT Interface 618 MXT.GetMxWebsite = GetMxWebsite; 619 MXT.GetMxDeliveryWebsite = GetMxDeliveryWebsite; 620 MXT.API = MakeApiCall; 621 MXT.getCookie = MXT.GetCookie = GetCookie; 622 MXT.setCookie = MXT.SetCookie = SetCookie; 623 MXT.deleteCookie = DeleteCookie; 624 MXT.AddPostRun = AddPostRun; 625 MXT.ListPostRun = ListPostRun; 626 MXT.RunPosts = RunPosts; 627 MXT.MxUrl = MxUrl; 628 MXT.EscapeRegex = EscapeRegex; 629 MXT.StringFormat = StringFormat; 630 MXT.Deprecated = Deprecated; 631 MXT.AdminConsole = AdminConsole; 632 MXT.Watcher = Watcher; 633 MXT.OnPasswordChange = OnPasswordChange; 634 MXT.HTMLEncode = HTMLEncode; 635 MXT.HTMLDecode = HTMLDecode; 636 MXT.GetDeliveryTemplateUrl = GetDeliveryTemplateUrl; 637 638 // =============== Placeholders 639 // In case the application does not load the other resource 640 MXT.AddABTestTrigger = MXT.AddABTestTrigger || function () { }; 641 642 window.init = AB265init; 643 644 // =============== Initialize 645 InitMxToolboxHostDomain(); 646 function InitMxToolboxHostDomain() { 647 let hostName = window.location.hostname.toLowerCase(); 648 let hostSplit = hostName.split("."); 649 650 MXT.HOST_NAME = hostName; 651 MXT.ISLOCAL = hostName.includes('local'); 652 MXT.DOMAIN_NAME = hostSplit.slice(-2).join('.'); 653 MXT.DOMAIN_ENDING = hostSplit.slice(-1)[0]; 654 655 if (MXT.ISLOCAL) { 656 MXT.DELIVERY_HOST = `${MXT.HOST_NAME}/site`; 657 } else { 658 MXT.DELIVERY_HOST = `delivery.${MXT.HOST_NAME}`; 659 } 660 } 661 662 // =============== Events 663 // To let early callers know that the mx shared library is ready 664 document.dispatchEvent(new CustomEvent('mx-shared-ready')); 665 666 // =============== MXT Methods 667 function GetMxWebsite() { 668 return `https://${MXT.HOST_NAME}/`; 669 } 670 671 function GetMxDeliveryWebsite() { 672 return `https://${MXT.DELIVERY_HOST}${(MXT.DELIVERY_HOST[MXT.DELIVERY_HOST.length - 1] == "/" ? "" : "/") }`; 673 } 674 675 /** 676 * Used to indicate that an old function should not be used anymore 677 * NOTE 678 * The reason for this is urge the moving of global functions into the MXT namespace. 679 * When a file is converted to use the MXT namespace, this utility will help 680 * identify when we are still calling the old global. 681 * 682 * @param {*} newFunctionName This string will have an 'eval' applied. Make sure it's correct. 683 * @param {*} oldFunctionName Optional - The old function name 684 * @param {*} oldFnOverride Optional 685 * @returns {fn} conversion function 686 */ 687 function Deprecated(newFunctionName, oldFunctionName, oldFnOverride) { 688 return function () { 689 let msg = ''; 690 let fn; 691 692 if (oldFunctionName) { 693 msg += '"' + oldFunctionName + '" is deprecated. '; 694 } 695 msg += 'Use "' + newFunctionName + '" instead. '; 696 697 if (oldFnOverride) { 698 fn = oldFnOverride; 699 msg += 'NOTE: The new function uses a different signature. '; 700 } else { 701 fn = eval(newFunctionName); 702 } 703 704 MXT.AdminConsole(msg); 705 // 'arguments' is a hidden property available to all functions. This returned function is what the 706 // CALLEE is calling, so it has the params. 707 // Using apply to pass through all old 'arguments' into new function. 708 // Using Array.prototype.slice.call to turn arguments into real array 709 fn.apply(this, Array.prototype.slice.call(arguments)); 710 }; 711 } 712 713 714 /** 715 * This is for AngularJs templates that need to live in classic and DC2 716 */ 717 function GetDeliveryTemplateUrl(templateUrl) { 718 let url = templateUrl; 719 720 // Only Classic MXT will have MXT.currentLocation 721 if (MXT.currentLocation) { 722 url = `https://${MXT.DELIVERY_HOST}/${templateUrl}`; 723 } 724 725 return url; 726 } 727 728 /** 729 * Helper to angular $watch and $on that reports when watches are fired. 730 * 731 * @param {*} fn 732 * @returns 733 */ 734 function Watcher(fn) { 735 let controllerName = ''; 736 try { 737 controllerName = arguments.callee.caller.name; 738 } catch (error) { 739 // This is most likely because the caller of this Watcher function has 740 // 'use strict'; at the top of the file.. therefore it will block our attempts 741 // to look behind the scenes. 742 } 743 return function WatchInterceptor() { 744 AdminConsole('WATCH "' + fn.name + '" fired in ' + controllerName); 745 fn.apply(this, Array.prototype.slice.call(arguments)); 746 }; 747 } 748 749 /** 750 * Admin Console outputs a console.log(), but only if the USER is considered an ADMIN. 751 * 752 * @param {*} [arguments] native arguments 753 */ 754 function AdminConsole() { 755 // 'arguments' is a hidden property available to all functions. 756 let fnArguments = Array.prototype.slice.call(arguments); 757 758 if (MXT.isUserLoaded) { 759 // The MXT.User is not properly loaded by this time, but the mxUser.js has set the is_admin bit in the window variable. 760 // I'm pretty sure this is why the deprecated message that calls this exists 761 // This is a shim until we figure out a better way. 762 763 if (window._is_admin && window._admin_console) { // TODO - Add condition of inpers
763onated 764 // 'arguments' is a hidden property available to all functions. 765 // Using apply to pass through all 'arguments' to console. 766 // Using Array.prototype.slice.call to turn arguments into real array 767 // Colorizing tag with '%c' which console.log in chrome, safari and edge support 768 console.log.apply(this, ['%cMXT ADMIN LOG:', 'color:#FF9900;'].concat(fnArguments)); 769 } 770 } else { 771 _adminConsoleMsgs.push(fnArguments); 772 MXT.OnUserLoaded(function (event, user) { 773 let i, len; 774 for (i = 0, len = _adminConsoleMsgs.length; i < len; i++) { 775 AdminConsole.apply(this, _adminConsoleMsgs[i]); 776 } 777 _adminConsoleMsgs.length = 0; 778 }); 779 } 780 } 781 782 function OnPasswordChange(element) { 783 var decode = decodeURIComponent($(element).val()); 784 $(element).val(encodeURIComponent(decode)); 785 } 786 787 function MakeApiCall(type, href, success, params) { 788 var apiUrl = MXT.GetMxWebsite() + "api/"; 789 790 params = params || {}; 791 params = $.extend({ 792 type: { 793 GET: 'GET', 794 POST: 'POST', 795 DELETE: 'DELETE', 796 PUT: 'PUT' 797 }[type.toUpperCase()] || 'GET', 798 async: undefined, 799 contentType: 'application/json; charset=utf-8', 800 dataType: 'json', 801 data: null 802 }, params); 803 804 // v1 or v2 805 apiUrl += 'v' + (params.isV2 ? '2' : '1'); 806 807 //Are we passing in a path or a full href? 808 href = href[0] === '/' ? apiUrl + href : href; 809 810 $.ajax({ 811 type: params.type, 812 async: params.async, 813 data: params.data, 814 url: href, 815 contentType: params.contentType, 816 dataType: params.dataType, 817 success: function (data) { 818 success(data); 819 }, 820 error: function (xhr, status, error) { 821 console.log(xhr, status, error); 822 } 823 }); 824 } 825 826 function SetCookie(name, value, days, domain) { 827 let expires = ''; 828 let date = new Date(); 829 830 if (days) { 831 date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); 832 expires = "; expires=" + date.toGMTString(); 833 } 834 835 // Anything over 4k can not be stored in a cookie. We are going to store it in local storage instead 836 if (value.length > 3900) { 837 localStorage.setItem(name, value); 838 } else { 839 document.cookie = name + "=" + encodeURIComponent(value) + expires + "; " + (domain ? "domain=" + domain : "") + "; path=/"; 840 } 841 842 return document.cookie; 843 } 844 845 function DeleteCookie(name) { 846 let isCookie = !!FindCookie(name); 847 if (isCookie) { 848 MXT.setCookie(name, '', -1); 849 } else { 850 localStorage.removeItem(name); 851 } 852 } 853 854 function GetCookie(name) { 855 let cookieValue = FindCookie(name); 856 let localStorageItem; 857 858 if (cookieValue) { 859 return decodeURIComponent(cookieValue); 860 } else { 861 // We did not find it in cookies.. try localStorage 862 863 localStorageItem = localStorage.getItem(name); 864 if (localStorageItem) { 865 return localStorageItem; 866 } 867 } 868 869 return null; 870 } 871 872 function FindCookie(name) { 873 let nameEQ = name + "="; 874 let ca = document.cookie.split(';'); 875 876 for (var i = 0, len = ca.length; i < len; i++) { 877 var c = ca[i]; 878 while (c.charAt(0) === ' ') c = c.substring(1, c.length); 879 if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length); 880 } 881 882 return null; 883 } 884 885 /** 886 * These are tools for VWO and Optimize to trigger code execution 887 * AFTER VWO and Optimize have run it's test code on the page. 888 * To add a function to run after VWO/Optimize, use either: 889 * window.MXT.AddPostRun(fn) 890 * if there are no parameters that need to be passed into the executed code
891 * window.MXT.AddPostRun(function(){ your code that uses parameters outside this }) 892 * if you have parameters. Look at ShowSwitchResponsiveLink in this file 893 * for an example. 894 * 895 * VWO/Optimize then can call window.MXT.RunPosts() when it's done. 896 * 897 * In VWO, go into any of the javascript edit (Control or Variation), 898 * Click on the Edit Campaign Code item. This is where we can set code that 899 * runs after the variation code runs. All variations use the same campaign code. 900 * This is what you put in there: 901 * (function(){ 902 var MXT = window.MXT || { 903 RunPosts: function(){ 904 console.log('VWO On Done error. No MXT found'); 905 } 906 }; 907 if (MXT.RunPosts){ 908 MXT.RunPosts(); 909 } 910 })(); 911 * 912 * In Optimize: I could not figure out a solution to this one.. No optimize post execution 913 * until we figure this out. 914 */ 915 916 function AddPostRun(fn) { 917 _postRun.functions.push(fn); 918 } 919 920 function ListPostRun() { 921 console.log(_postRun); 922 } 923 924 function RunPosts() { 925 if (!_postRun.hasRun) { 926 let i, len; 927 for (i = 0, len = _postRun.functions.length; i < len; i++) { 928 _postRun.functions[i](); 929 } 930 // reset 931 _postRun.functions = []; 932 } 933 } 934 935 function AB265init(i) { //Put in place to fix a timing issue with AB-265 without restarting the test. 936 i = i || 0; 937 if (i < 10) { 938 i++; 939 setTimeout(function () { 940 window.init(i); 941 }, 300); 942 } 943 } 944 945 // MxUrl accepts a param that can be: 946 // [object] 'document.location' 947 // [string] a css selector 948 // [string] url (distinguished by '/' characters present) 949 function MxUrl(obj) { 950 let _$elem; 951 let _href; 952 let _location; 953 let _params; 954 let _this; 955 956 // if string is passed in, it's either a class name or url string 957 if (typeof obj === 'string') { 958 if (obj.indexOf('/') > -1) { 959 // The obj contains a '/' so it is most likely a url string 960 _href = obj; 961 _location = ParseURL(_href); 962 } else { 963 // The obj does not contain a '/' so it is most likely a css selector 964 _$elem = $(obj); 965 _href = _$elem.attr('href'); 966 _location = ParseURL(_href); 967 } 968 } else { 969 // The obj is an object, so it's expected that it is a 'document.location' 970 _href = obj.href; 971 _location = obj; 972 } 973 _params = ParseParams(_location.search); 974 975 _this = { 976 $elem: _$elem, 977 href: _href, 978 location: _location, 979 params: _params, 980 UpdateQS: UpdateQS, 981 IsPage: IsPage, 982 HasParam: HasParam, 983 GetUrl: GetUrl, 984 MergeParams: MergeParams 985 }; 986 return _this; 987 988 function ParseURL(url) { 989 let parser = document.createElement('a'); 990 // Let the browser do the work 991 parser.href = url; 992 993 // IE11 compatibiliy.. silly, I know. https://gist.github.com/koron/c66b67c6bcdd286f6209 994 parser.href = parser.href; 995 996 return { 997 protocol: parser.protocol, 998 host: parser.host, 999 hostname: parser.hostname, 1000 port: parser.port, 1001 pathname: parser.pathname.indexOf('/') !== 0 ? '/' + parser.pathname : parser.pathname, 1002 search: parser.search, 1003 hash: parser.hash 1004 }; 1005 } 1006 1007 function ParseParams(search) { 1008 let searchObject = {}, 1009 queries, split, i; 1010 if (search === "") { 1011 return {}; 1012 } 1013 queries = search.replace(/^\?/, '').split('&'); 1014 for (i = 0; i < queries.length; i++) { 1015 split = queries[i].split('='); 1016 searchObject[split[0]] = split[1]; 1017 } 1018 return searchObject; 1019 } 1020 1021 function IsPage(page) { 1022 return _location.pathname.toLowerCase().indexOf(page) > -1; 1023 } 1024 1025 function HasParam(key, value) { 1026 for (let param in _params) { 1027 if (_params.hasOwnProperty(param)) { 1028 if (param.toLowerCase() === key.toLowerCase()) { 1029 return value ? _params[param] === value : true; 1030 } 1031 } 1032 } 1033 return false;
1034 } 1035 1036 function UpdateQS(paramToChange, value, deleteUndefined) { 1037 let foundParam = false; 1038 for (let param in _params) { 1039 if (_params.hasOwnProperty(param)) { 1040 if (param.toLowerCase() === paramToChange.toLowerCase()) { 1041 _params[param] = value; 1042 foundParam = true; 1043 break; 1044 } 1045 } 1046 } 1047 if (!foundParam) { 1048 _params[paramToChange] = value; 1049 } 1050 1051 // If nessassary, delete the param if it has no value 1052 if (deleteUndefined && !value) { 1053 delete _params[paramToChange]; 1054 } 1055 1056 if (_$elem) { 1057 _$elem.attr('href', GetUrl()); 1058 } 1059 1060 return _this; 1061 } 1062 1063 function GetUrl() { 1064 let returnUrl; 1065 returnUrl = _location.protocol + '//' + _location.host + _location.pathname; 1066 if (_params) { 1067 returnUrl += '?' + ParamsToQs(); 1068 } 1069 return returnUrl; 1070 } 1071 1072 function ParamsToQs() { 1073 let returnQs = []; 1074 for (let param in _params) { 1075 if (_params.hasOwnProperty(param)) { 1076 returnQs.push(param + '=' + _params[param]); 1077 } 1078 } 1079 return returnQs.join('&'); 1080 } 1081 1082 function MergeParams(params) { 1083 for (param in params) { 1084 if (params.hasOwnProperty(param)) { 1085 UpdateQS(param, params[param]); 1086 } 1087 } 1088 } 1089 } 1090 1091 function StringFormat(str) { 1092 let argCount = arguments.length; 1093 let i, len, replace, re; 1094 1095 for (i = 1, len = argCount; i < len; i++) { 1096 replace = EscapeRegex('{' + (i - 1) + '}'); 1097 re = new RegExp(replace, "g"); 1098 str = str.replace(re, arguments[i]); 1099 1100 } 1101 1102 return str; 1103 } 1104 1105 function EscapeRegex(value) { 1106 return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&"); 1107 } 1108 1109 /** 1110 * Converts a string to html characters 1111 * @param {string} str String with unescaped HTML characters 1112 */ 1113 function HTMLEncode(str) { 1114 var buf = []; 1115 1116 for (var i = str.length - 1; i >= 0; i--) { 1117 buf.unshift(['&#', str[i].charCodeAt(), ';'].join('')); 1118 } 1119 1120 return buf.join(''); 1121 } 1122 /** 1123 * Converts HTML Entities to regular characters 1124 * @param {any} str String of HTML Entities 1125 */ 1126 function HTMLDecode(str) { 1127 return str.replace(/&#(\d+);/g, function (match, dec) { 1128 return String.fromCharCode(dec); 1129 }); 1130 } 1131})();; 1132(function () { 1133 // 1134 // !! THIS FILE REQUIRES MXSHARED !! 1135 // 1136 // Do not copy this this script is intended to be unique 1137 // and shared among all the master pages 1138 // all the functions here must be declared here and only here 1139 1140 window.MXT = window.MXT || {}; 1141 1142 let MXT = window.MXT; 1143 1144 MXT.AppLog = {}; 1145 MXT.AppLog.WriteTypedEntry = WriteTypedEntry; 1146 MXT.AppLog.TraceEventType = { 1147 Critical: 1, 1148 Error: 2, 1149 Warning: 4, 1150 Information: 8, 1151 Verbose: 16 1152 }; 1153 1154 function WriteTypedEntry(type, errMsg, traceEventType, detail) { 1155 if (detail) { 1156 if (typeof detail == 'object') { 1157 detail = JSON.stringify(detail); 1158 } 1159 } 1160 1161 var clientMessage = { 1162 Location: type, 1163 Message: errMsg, 1164 TraceEventType: traceEventType, 1165 Detail: detail 1166 }; 1167 1168 $.post('/api/v1/Loggly', clientMessage, WriteTypedEntrySuccess, WriteTypedEntryFail); 1169 1170 function WriteTypedEntrySuccess(data) { 1171 console.log('data', data); 1172 } 1173 1174 function WriteTypedEntryFail(response) { 1175 console.log('response', response); 1176 } 1177 } 1178 1179})();; 1180(function () { 1181 // 1182 // !! THIS FILE REQUIRES MXSHARED !! 1183 // 1184 // Do not copy this this script is intended to be unique 1185 // and shared among all the master pages 1186 // all the functions here must be declared here and only here 1187 1188 window.MXT = window.MXT || {}; 1189 window.MXT.OnUserLoaded = onUserLoaded; 1190 window.MXT.RegistrationValidation = registrationValidation; 1191 window.MXT.GetPasswordMinMax = getPasswordMinMax; 1192 1193 // Global variables 1194 window._is_paid = 0; 1195 window._is_logged_in = 0; 1196 window._is_admin = 0; 1197 window._numDomainHealthMonitors = 0; 1198 window._domainName = window.location.host.split('.').slice(-2).join('.'); 1199 1200 // Private variables 1201 _passwordMinLength = 8; 1202 _passwordMaxLength = 128; 1203 1204 (function GetUser() { 1205 $.getJSON('/api/v1/user', getUserSuccess) 1206 .fail(getUserFail); 1207 1208 })(); 1209 1210 function getUserSuccess(user) { 1211 // Store the cookies 1212 /* 1213 * Features: 1214 * If the cookie is present the backend won't return it, so just read from the cookie for now 1215 * We need to stop storing Features with the user, once that is done clean here 1216 */ 1217 if (user.Features && Object.getOwnPropertyNames(user.Features).length > 0) { 1218 localStorage.setItem('_mxt_f', JSON.stringify(user.Features)); 1219 } else { 1220 user.Features = JSON.parse(localStorage.getItem('_mxt_f')) || {}; 1221 } 1222 // Store user without the Features 1223 var _mxt_u = Object.assign({}, user); // Copy so it does not kill the prop Features on the real object 1224 delete _mxt_u.Features; 1225 MXT.setCookie('_mxt_u', JSON.stringify(_mxt_u), null, '.' + window._domainName); 1226 // Cookie Segment for ABtesting 1227 var _mxt_segment = "anon"; // for cookie _mxt_s 1228 // Set global variables 1229 if (user.IsLoggedIn) { 1230 window._is_logged_in = 1; 1231 _mxt_segment = "free"; 1232 if (user.IsPaidUser) { 1233 window._is_paid = 1; 1234 _mxt_segment = "paid"; 1235 } 1236 if (user.IsAdmin) { 1237 window._is_admin = 1; 1238 } 1239 // SetCustomerIo 1240 var _cio = window._cio || []; 1241 if (_cio['identify']) { 1242 _cio.identify({ 1243 id: user.MxVisitorUid, 1244 email: user.UserName 1245 }); 1246 } 1247 window._numDomainHealthMonitors = user.NumDomainHealthMonitors; 1248 } else { 1249 // Set the Temp Auth Key if is not logged in 1250 if (user.TempAuthKey) { 1251 window.TempAuthKey = user.TempAuthKey; 1252 } 1253 } 1254 1255 // Set Segment Cookie 1256 MXT.setCookie('_mxt_s', _mxt_segment, null, '.' + window._domainName); 1257 MXT.isUserLoaded = true; 1258
1259 // Raised each time User object comes back (for external integrations) 1260 $(document).trigger('userLoaded', user); 1261 document.dispatchEvent(new CustomEvent('mx-user-loaded', { 'user': user })); // For those who don't use jQuery 1262 } 1263 1264 function getUserFail() { 1265 // Delete the cookie/storage 1266 MXT.deleteCookie('_mxt_u'); 1267 MXT.deleteCookie('_mxt_f'); // If you see this line after 9/25/2021, please remove 1268 localStorage.removeItem('_mxt_f'); 1269 1270 MXT.isUserLoaded = true; 1271 1272 // Raised each time User object comes back (for external integrations) 1273 $(document).trigger('userLoaded', null); 1274 document.dispatchEvent(new CustomEvent('mx-user-loaded', { 'user': null })); // For those who don't use jQuery 1275 } 1276 1277 // Wrap up function to execute something as soon as the User is loaded 1278 // Async user integrations: Used by Ab Test and by MxMaster (to load the header) 1279 function onUserLoaded(callback) { 1280 if (MXT.isUserLoaded) { 1281 // Already loaded, send from cookie 1282 // We are mocking the trigger and the first argument must be the event (null to let the called know that it was immediate) 1283 var event = null; 1284 var user = JSON.parse(MXT.getCookie('_mxt_u')); 1285 user.Features = JSON.parse(localStorage.getItem('_mxt_f')) || {}; 1286 callback(event, user); 1287 } else { 1288 // The trigger will handle this 1289 $(document).on('userLoaded', callback); 1290 } 1291 } 1292 1293 // This is a form validator for our Registration forms 1294 // Starting with password validation, it can be extended to any and everything. 1295 function registrationValidation(registration) { 1296 let returnMessage = ''; 1297 if (typeof registration.name !== 'undefined' && (registration.name === '' || registration.name.length === 0)) { 1298 returnMessage += 'Name can not be empty.<br />'; 1299 } 1300 if (typeof registration.email !== 'undefined' && (registration.email === '' || registration.email.length === 0)) { 1301 returnMessage += 'Email address can not be empty.<br />'; 1302 } 1303 if (typeof registration.password !== 'undefined' && registration.password.length < 8) { 1304 returnMessage += 'Password too short. Password must be at least 8 characters.<br />'; 1305 } 1306 if (typeof registration.password !== 'undefined' && registration.password.length > 128) { 1307 returnMessage += 'Password too long. Password can not exceed 128 characters. <br />'; 1308 } 1309 if (typeof registration.name !== 'undefined' && registration.company.length > 50) { 1310 returnMessage += 'Company Name too long. Company Name can not exceed 50 characters. <br />'; 1311 } 1312 if (typeof registration.domain !== 'undefined' && (registration.domain === '' || registration.domain.length === 0)) { 1313 returnMessage += 'Domain can not be empty.<br />'; 1314 } 1315 return returnMessage; 1316 } 1317 1318 function getPasswordMinMax() { 1319 let passwordLengths = { 1320 min: _passwordMinLength, 1321 max: _passwordMaxLength 1322 }; 1323 return passwordLengths; 1324 } 1325 1326})();; 1327/* 1328 * JavaScript MD5 1329 * https://github.com/blueimp/JavaScript-MD5 1330 * 1331 * Copyright 2011, Sebastian Tschan 1332 * https://blueimp.net 1333 * 1334 * Licensed under the MIT license: 1335 * https://opensource.org/licenses/MIT 1336 * 1337 * Based on 1338 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message 1339 * Digest Algorithm, as defined in RFC 1321. 1340 * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 1341 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet 1342 * Distributed under the BSD License 1343 * See http://pajhome.org.uk/crypt/md5 for more info. 1344 */ 1345 1346/* global define */ 1347 1348;(function ($) { 1349 'use strict' 1350 1351 /* 1352 * Add integers, wrapping at 2^32. This uses 16-bit operations internally 1353 * to work around bugs in some JS interpreters. 1354 */ 1355 function safeAdd (x, y) { 1356 var lsw = (x & 0xffff) + (y & 0xffff) 1357 var msw = (x >> 16) + (y >> 16) + (lsw >> 16) 1358 return (msw << 16) | (lsw & 0xffff) 1359 } 1360 1361 /* 1362 * Bitwise rotate a 32-bit number to the left. 1363 */ 1364 function bitRotateLeft (num, cnt) { 1365 return (num << cnt) | (num >>> (32 - cnt)) 1366 } 1367 1368 /* 1369 * These functions implement the four basic operations the algorithm uses. 1370 */ 1371 function md5cmn (q, a, b, x, s, t) { 1372 return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b) 1373 } 1374 function md5ff (a, b, c, d, x, s, t) { 1375 return md5cmn((b & c) | (~b & d), a, b, x, s, t) 1376 } 1377 function md5gg (a, b, c, d, x, s, t) { 1378 return md5cmn((b & d) | (c & ~d), a, b, x, s, t) 1379 } 1380 function md5hh (a, b, c, d, x, s, t) { 1381 return md5cmn(b ^ c ^ d, a, b, x, s, t) 1382 } 1383 function md5ii (a, b, c, d, x, s, t) { 1384 return md5cmn(c ^ (b | ~d), a, b, x, s, t) 1385 } 1386 1387 /* 1388 * Calculate the MD5 of an array of little-endian words, and a bit length. 1389 */ 1390 function binlMD5 (x, len) { 1391 /* append padding */ 1392 x[len >> 5] |= 0x80 << (len % 32) 1393 x[((len + 64) >>> 9 << 4) + 14] = len 1394 1395 var i 1396 var olda 1397 var oldb 1398 var oldc 1399 var oldd 1400 var a = 1732584193 1401 var b = -271733879 1402 var c = -1732584194 1403 var d = 271733878 1404 1405 for (i = 0; i < x.length; i += 16) { 1406 olda = a 1407 oldb = b 1408 oldc = c 1409 oldd = d 1410 1411 a = md5ff(a, b, c, d, x[i], 7, -680876936) 1412 d = md5ff(d, a, b, c, x[i + 1], 12, -389564586) 1413 c = md5ff(c, d, a, b, x[i + 2], 17, 606105819) 1414 b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330) 1415 a = md5ff(a, b, c, d, x[i + 4], 7, -176418897) 1416 d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426) 1417 c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341) 1418 b = md5ff(b, c, d, a, x[i + 7], 22, -45705983) 1419 a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416) 1420 d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417) 1421 c = md5ff(c, d, a, b, x[i + 10], 17, -42063) 1422 b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162) 1423 a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682) 1424 d = md5ff(d, a, b, c, x[i + 13], 12, -40341101) 1425 c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290)
1426 b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329) 1427 1428 a = md5gg(a, b, c, d, x[i + 1], 5, -165796510) 1429 d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632) 1430 c = md5gg(c, d, a, b, x[i + 11], 14, 643717713) 1431 b = md5gg(b, c, d, a, x[i], 20, -373897302) 1432 a = md5gg(a, b, c, d, x[i + 5], 5, -701558691) 1433 d = md5gg(d, a, b, c, x[i + 10], 9, 38016083) 1434 c = md5gg(c, d, a, b, x[i + 15], 14, -660478335) 1435 b = md5gg(b, c, d, a, x[i + 4], 20, -405537848) 1436 a = md5gg(a, b, c, d, x[i + 9], 5, 568446438) 1437 d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690) 1438 c = md5gg(c, d, a, b, x[i + 3], 14, -187363961) 1439 b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501) 1440 a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467) 1441 d = md5gg(d, a, b, c, x[i + 2], 9, -51403784) 1442 c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473) 1443 b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734) 1444 1445 a = md5hh(a, b, c, d, x[i + 5], 4, -378558) 1446 d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463) 1447 c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562) 1448 b = md5hh(b, c, d, a, x[i + 14], 23, -35309556) 1449 a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060) 1450 d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353) 1451 c = md5hh(c, d, a, b, x[i + 7], 16, -155497632) 1452 b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640) 1453 a = md5hh(a, b, c, d, x[i + 13], 4, 681279174) 1454 d = md5hh(d, a, b, c, x[i], 11, -358537222) 1455 c = md5hh(c, d, a, b, x[i + 3], 16, -722521979) 1456 b = md5hh(b, c, d, a, x[i + 6], 23, 76029189) 1457 a = md5hh(a, b, c, d, x[i + 9], 4, -640364487) 1458 d = md5hh(d, a, b, c, x[i + 12], 11, -421815835) 1459 c = md5hh(c, d, a, b, x[i + 15], 16, 530742520) 1460 b = md5hh(b, c, d, a, x[i + 2], 23, -995338651) 1461 1462 a = md5ii(a, b, c, d, x[i], 6, -198630844) 1463 d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415) 1464 c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905) 1465 b = md5ii(b, c, d, a, x[i + 5], 21, -57434055) 1466 a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571) 1467 d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606) 1468 c = md5ii(c, d, a, b, x[i + 10], 15, -1051523) 1469 b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799) 1470 a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359) 1471 d = md5ii(d, a, b, c, x[i + 15], 10, -30611744) 1472 c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380) 1473 b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649) 1474 a = md5ii(a, b, c, d, x[i + 4], 6, -145523070) 1475 d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379) 1476 c = md5ii(c, d, a, b, x[i + 2], 15, 718787259) 1477 b = md5ii(b, c, d, a, x[i + 9], 21, -343485551) 1478 1479 a = safeAdd(a, olda) 1480 b = safeAdd(b, oldb) 1481 c = safeAdd(c, oldc) 1482 d = safeAdd(d, oldd) 1483 } 1484 return [a, b, c, d] 1485 } 1486 1487 /* 1488 * Convert an array of little-endian words to a string 1489 */ 1490 function binl2rstr (input) { 1491 var i 1492 var output = '' 1493 var length32 = input.length * 32 1494 for (i = 0; i < length32; i += 8) { 1495 output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xff) 1496 } 1497 return output 1498 } 1499 1500 /* 1501 * Convert a raw string to an array of little-endian words 1502 * Characters >255 have their high-byte silently ignored. 1503 */ 1504 function rstr2binl (input) { 1505 var i 1506 var output = [] 1507 output[(input.length >> 2) - 1] = undefined 1508 for (i = 0; i < output.length; i += 1) { 1509 output[i] = 0 1510 } 1511 var length8 = input.length * 8 1512 for (i = 0; i < length8; i += 8) { 1513 output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << (i % 32) 1514 } 1515 return output 1516 } 1517 1518 /* 1519 * Calculate the MD5 of a raw string 1520 */ 1521 function rstrMD5 (s) { 1522 return binl2rstr(binlMD5(rstr2binl(s), s.length * 8)) 1523 } 1524 1525 /* 1526 * Calculate the HMAC-MD5, of a key and some data (raw strings) 1527 */ 1528 function rstrHMACMD5 (key, data) { 1529 var i 1530 var bkey = rstr2binl(key) 1531 var ipad = [] 1532 var opad = [] 1533 var hash 1534 ipad[15] = opad[15] = undefined 1535 if (bkey.length > 16) { 1536 bkey = binlMD5(bkey, key.length * 8) 1537 } 1538 for (i = 0; i < 16; i += 1) { 1539 ipad[i] = bkey[i] ^ 0x36363636 1540 opad[i] = bkey[i] ^ 0x5c5c5c5c 1541 } 1542 hash = binlMD5(ipad.concat(rstr2binl(data)), 512 + data.length * 8) 1543 return binl2rstr(binlMD5(opad.concat(hash), 512 + 128)) 1544 } 1545 1546 /* 1547 * Convert a raw string to a hex string 1548 */ 1549 function rstr2hex (input) { 1550 var hexTab = '0123456789abcdef' 1551 var output = '' 1552 var x 1553 var i 1554 for (i = 0; i < input.length; i += 1) { 1555 x = input.charCodeAt(i) 1556 output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f) 1557 } 1558 return output 1559 } 1560 1561 /* 1562 * Encode a string as utf-8 1563 */ 1564 function str2rstrUTF8 (input) { 1565 return unescape(encodeURIComponent(input)) 1566 } 1567 1568 /* 1569 * Take string arguments and return either raw or hex encoded strings 1570 */ 1571 function rawMD5 (s) { 1572 return rstrMD5(str2rstrUTF8(s)) 1573 } 1574 function hexMD5 (s) { 1575 return rstr2hex(rawMD5(s)) 1576 }
1577 function rawHMACMD5 (k, d) { 1578 return rstrHMACMD5(str2rstrUTF8(k), str2rstrUTF8(d)) 1579 } 1580 function hexHMACMD5 (k, d) { 1581 return rstr2hex(rawHMACMD5(k, d)) 1582 } 1583 1584 function md5 (string, key, raw) { 1585 if (!key) { 1586 if (!raw) { 1587 return hexMD5(string) 1588 } 1589 return rawMD5(string) 1590 } 1591 if (!raw) { 1592 return hexHMACMD5(key, string) 1593 } 1594 return rawHMACMD5(key, string) 1595 } 1596 1597 if (typeof define === 'function' && define.amd) { 1598 define(function () { 1599 return md5 1600 }) 1601 } else if (typeof module === 'object' && module.exports) { 1602 module.exports = md5 1603 } else { 1604 $.md5 = md5 1605 } 1606})(this) 1607; 1608(function () { 1609 // Do not copy this this script is intended to be unique 1610 // and shared among all the master pages. 1611 // all the functions here must be declared here and only here 1612 1613 window.MXT = window.MXT || {}; 1614 1615 // =============== MXT Interface 1616 MXT.LazyLoadQualaroo = LazyLoadQualaroo; 1617 MXT.SetQualarooPopupState = SetQualarooPopupState; 1618 1619 // =============== MXT Methods 1620 function LazyLoadQualaroo() { 1621 window._kiq = window._kiq || []; 1622 setTimeout(amazonaws, 1); 1623 1624 function amazonaws() { 1625 var d = document, 1626 f = d.getElementsByTagName('script')[0], 1627 s = d.createElement('script'); 1628 1629 s.type = 'text/javascript'; 1630 s.async = true; 1631 s.src = '//d3vk40ihlliju7.cloudfront.net/61819/e3J.js'; 1632 f.parentNode.insertBefore(s, f); 1633 AddQualarooStatsScript(); 1634 } 1635 } 1636 1637 // Pass in multiple strings as param array. 1638 function SetQualarooPopupState() { 1639 var i, len; 1640 if (window._kiq) { 1641 window.onpopstate = function () { 1642 for (i = 0, len = arguments.length; i < len; i++) { 1643 window._kiq.push([arguments[i]]); 1644 } 1645 }; 1646 } 1647 } 1648 1649 function AddQualarooStatsScript() { 1650 MXT.OnUserLoaded(function (event, user) { 1651 if (user) { 1652 if (user.UserName) { 1653 window._kiq.push(['identify', user.UserName]); 1654 } 1655 window._kiq.push(['set', user.Membership]); 1656 } 1657 }); 1658 } 1659})();; 1660/////////////////////////////////// 1661// MX Internal AB Testing 1662/////////////////////////////////// 1663/* 1664 If you are using a different IDE than VS, then make sure to include new files into the project 1665*/ 1666(function () { 1667 window.MXT = window.MXT || {}; 1668 MXT.ABTest = MXT.ABTest || {}; 1669 1670 MXT.ABTestUtil = { 1671 GetHtmlContent: GetHtmlContent, 1672 EventSubmitOnEnter: EventSubmitOnEnter 1673 }; 1674 1675 function GetHtmlContent(name, callback) { 1676 return function () { 1677 let notFound = '404 - Page not found'; 1678 let fileName = 'Test_' + name + '.html'; 1679 let localFilePath = '/public/scripts/shared/mxabtest/' + name + '/' + fileName; 1680 1681 MXT.HTMLTemplates = MXT.HTMLTemplates || {}; 1682 1683 if (MXT.HTMLTemplates[fileName]) { 1684 callback(MXT.HTMLTemplates[fileName]); 1685 } else { 1686 // We are not using Bundled html. Get it manually. 1687 console.log('Getting file: ', localFilePath); 1688 $.get(localFilePath, function (html) { 1689 MXT.HTMLTemplates[fileName] = notFound; 1690 if (html.indexOf(notFound) === -1) { 1691 MXT.HTMLTemplates[fileName] = html; 1692 } 1693 callback(MXT.HTMLTemplates[fileName]); 1694 }); 1695 } 1696 }; 1697 } 1698 1699 function EventSubmitOnEnter(inputElem, callback) { 1700 let elem = $(inputElem); 1701 elem.keydown(function (event) { 1702 // Return key 1703 if (event.which === 13) { 1704 event.preventDefault(); 1705 callback(event); 1706 } else { 1707 return true; 1708 } 1709 }); 1710 } 1711})(); 1712/* suspending internal testing for now. commenting out to revisit at a later date. 1713 TODO: when we bring this back to life, make sure the .json files are being cached (they are not at the time of this comment) 1714 2018-01-03 Paul 1715 1716 1717(function () { 1718 1719 // =============== JSON Data Files AB Testing 1720 var abtests = ""; 1721 $.ajax({ 1722 global: false, 1723 url: "https://mxtoolbox.com/Public/json/ABTests.json", 1724 dataType: "json", 1725 success: function (data) { 1726 abtests = data; 1727 }, 1728 error: function (xhr, status, err) { 1729 console.log("xhr: " + xhr + "\nstatus: " + status + "\nerror: " + err); 1730 } 1731 }) 1732 var slots = ''; 1733 $.ajax({ 1734 global: false, 1735 url: "https://mxtoolbox.com/Public/json/ABSlots.json", 1736 dataType: "json", 1737 success: function (data) { 1738 slots = data; 1739 }, 1740 error: function (xhr, status, err) { 1741 console.log("xhr: " + xhr + "\nstatus: " + status + "\nerror: " + err); 1742 } 1743 }); 1744 1745 function coinFlip(seed, testId) { 1746 var hash = md5(seed.toString() + testId.toString()); 1747 var lastChar = hash.slice(-1); 1748 var lastCharInDec = parseInt(lastChar, 16) 1749 return lastCharInDec % 2; 1750 } 1751 1752 function getABTestVariation() { 1753 //TODO Error handling 1754 var landingURL = window.location.href; 1755 var visitorUID = MXT.getCookie('MxVisitorUID'); 1756 var variationsRun = []; 1757 for (var i = 0; i < abtests.length; i++) { 1758 // check for match (contains) 1759 if (landingURL.toLowerCase().indexOf(abtests[i].url.toLowerCase()) > -1) { 1760 // set coinflip 1761 var testid = abtests[i].name 1762 var coinSide = coinFlip(visitorUID, testid); 1763 var variant = (coinSide) ? abtests[i].control : abtests[i].variation; 1764 // Loop through the slots 1765 for (var j = 0; j < variant.slots.length; j++) { 1766 var htmlContent = slots[variant.slots[j].contentkey]; 1767 $('[id*="' + variant.slots[j].id + '_divContent"]').html(htmlContent); 1768 } 1769 variationsRun.push({ testid: testid, variant: variant.name }) 1770 } 1771 } 1772 for (var i = 0; i < variationsRun.length; i++) { 1773 MXT.SetVariationCookie(variationsRun[i].testid, variationsRun[i].variant); 1774 } 1775 } 1776 1777 // ================ Document Ready 1778 $(document).ready(function () { 1779 getABTestVariation(); 1780 }); 1781})(); 1782*/;
1783// Record a click against the ad UNIT, then navigate if the caller owns navigation. 1784// Attribution is per unit, not per link: every link inside an HTML ad reports the same 1785// pair, because sale attribution only asks whether the unit was clicked (PROD-6503). 1786// Image ads pass navigateUrl - their wrapper link has no href of its own. HTML ads omit 1787// it and let the admin's own href navigate. 1788function ClickAd(adGroupId, adUnitId, navigateUrl) { 1789 var url = GetMxWebsite() + "api/v2/ad/click-v2"; 1790 var body = JSON.stringify({ "adGroupId": adGroupId, "adUnitId": adUnitId }); 1791 1792 // Navigation cancels an in-flight ajax POST; sendBeacon is the only send that 1793 // survives it. The Blob carries the content type - a bare string posts as text/plain 1794 // and the endpoint will not bind the model. A beacon cannot run a CORS preflight and 1795 // application/json is not safelisted, so restrict it to a same-origin endpoint. 1796 if (navigator.sendBeacon && url.indexOf(window.location.origin + "/") === 0) { 1797 navigator.sendBeacon(url, new Blob([body], { type: "application/json" })); 1798 if (navigateUrl) { 1799 window.location.href = navigateUrl; 1800 } 1801 return; 1802 } 1803 1804 $.ajax({ 1805 type: "POST", 1806 url: url, 1807 data: body, 1808 contentType: "application/json; charset=utf-8", 1809 dataType: "json", 1810 error: function () { 1811 MxErrorHandler("", "ClickAd: "); 1812 }, 1813 // complete, not success: a failed click POST must still take the user to the ad. 1814 complete: function () { 1815 if (navigateUrl) { 1816 window.location.href = navigateUrl; 1817 } 1818 } 1819 }); 1820} 1821 1822// Build the InlineAd using AdUnitSimple from next-v2 1823function BuildAd(adPerc, divAd) { 1824 var hasInnerHtml = adPerc.InnerHtml != null && adPerc.InnerHtml !== ''; 1825 var $content; 1826 1827 // A divAd is rebuilt whenever a new ad is served, so drop the previous build's 1828 // handler before binding - otherwise the unit it closed over keeps reporting too. 1829 divAd.off('click.mxInlineAd'); 1830 1831 if (hasInnerHtml) { 1832 // HTML content ads (Admin > Ad Unit Manager): render the admin's markup as-is - 1833 // nothing wrapped, unwrapped or rewritten. The HTML decides what (if anything) is 1834 // clickable (PROD-6446). A delegated handler catches any link inside it so the unit 1835 // still records a click, which is what sale attribution keys off (PROD-6503). 1836 $content = adPerc.InnerHtml; 1837 divAd.on('click.mxInlineAd', 'a', function () { 1838 // No navigateUrl - the admin's own href navigates, so do not preventDefault. 1839 ClickAd(adPerc.AdGroupId, adPerc.AdUnitId); 1840 }); 1841 } else { 1842 // Image ads have no link of their own, so wrap in a tracked outer link. Bind a 1843 // real click handler instead of building an onclick="..." attribute string - 1844 // navigateUrl can come from admin-typed ad content and must not be interpolated 1845 // into an HTML attribute unescaped (a stray quote would break the tag). 1846 $content = $("<a class='mx-inline-ad-link' href='#'></a>").on('click', function (e) { 1847 e.preventDefault(); 1848 ClickAd(adPerc.AdGroupId, adPerc.AdUnitId, adPerc.NavigateUrl); 1849 }).append($("<img class='mx-inline-ad-img' />").attr({ 1850 src: adPerc.ImageUrl, 1851 alt: adPerc.AdGroupName + " advertisement image" 1852 })); 1853 } 1854 1855 if (adPerc.DelayMs !== 0) { 1856 divAd.empty().hide(); 1857 divAd.append($content); 1858 var str = "<script>setTimeout(function(){ $('#" + divAd.attr('id') + "').slideToggle('slow');}, " + adPerc.DelayMs + ");"; 1859 str += "<"; 1860 str += "/script>"; 1861 divAd.append(str); 1862 } else { 1863 divAd.empty().append($content); 1864 } 1865} 1866 1867// Call next-v2 to get the best ad for the given command, then build it 1868function ShowAd(divAd, adId, command) { 1869 // AB-569 - Turn off Inline Ads 1870 if (MXT.DisableInlineAds) { 1871 return false; 1872 } 1873 1874 if (arguments.length > 3) { 1875 return false; 1876 } 1877 1878 var params = { 1879 "command": command 1880 }; 1881 $.ajax({ 1882 type: "POST", 1883 url: GetMxWebsite() + "api/v2/ad/next-v2", 1884 data: JSON.stringify(params), 1885 contentType: "application/json; charset=utf-8", 1886 dataType: "json", 1887 success: function (adPerc) { 1888 if (adPerc.d !== null) { 1889 BuildAd(adPerc.d, divAd); 1890 } 1891 }, 1892 error: function () { 1893 MxErrorHandler("Show next ad", "ShowAd: "); 1894 console.log(arguments); 1895 } 1896 }); 1897} 1898; 1899(function () {
1900 // Do not copy this this script is intended to be unique 1901 // and shared among all the master pages 1902 // all the functions here must be declared here and only here 1903 1904 // =============== Windows Interface 1905 // These are here for backwards compatibility. We should remove calls to these 1906 // and replace them with MXT calls. 1907 // Be aware that any functionality that needs to work on any browers that 1908 // do not support a root object called 'window' will fail. Some stateless 1909 // browsers are like this 1910 window.setIpAddress = SetIpAddress; 1911 1912 window.MXT = window.MXT || {}; 1913 1914 // =============== MXT Interface 1915 MXT.SetIpAddress = SetIpAddress; 1916 1917 // =============== MXT Methods 1918 //Called on Init from MxMaster 1919 function SetIpAddress() { 1920 // Derive the API host from the current site's domain so the DEVS site (mxtoolbox.net) 1921 // calls the DEVS API and PROD (mxtoolbox.com) calls the PROD API. This was previously 1922 // hardcoded to api.mxtoolbox.com, so the DEVS site always hit the PROD API. (PROD-6449) 1923 var host = (window.location.hostname || '').toLowerCase(); 1924 var match = host.match(/mxtoolbox\.(com|net)$/); 1925 var baseDomain = match ? 'mxtoolbox.' + match[1] : 'mxtoolbox.com'; 1926 var whatsmyipUrl = 'https://api.' + baseDomain + '/api/v1/utils/whatsmyip'; 1927 1928 $.get(whatsmyipUrl, function (data) { 1929 // ******* TODO ******** Move this out of here. This should only return data 1930 $('#lnkIp').html(data); 1931 }); 1932 } 1933})();; 1934(function () { 1935 // 1936 // !! THIS FILE REQUIRES MXSHARED !! 1937 // 1938 // Do not copy this this script is intended to be unique 1939 // and shared among all the master pages 1940 // all the functions here must be declared here and only here 1941 1942 window.MXT = window.MXT || {}; 1943 1944 MXT.IsMobile = IsMobile; 1945 MXT.IsTablet = IsTablet; 1946 1947 //Mobile handler 1948 function IsMobile(ignoreCookie) { 1949 if (!MXT.getCookie('ismobile') || ignoreCookie) { 1950 if (!MXT.IsTablet() && (MXT.CheckUserAgent(['ipod', 'iphone', 'android', 'blackberry', 'palm', 'smartphone', 'windows ce']))) { 1951 if (ignoreCookie == false) { 1952 MXT.setCookie('ismobile', true); 1953 } 1954 return true; 1955 } 1956 else { 1957 if (ignoreCookie == false) { 1958 MXT.setCookie('ismobile', false); 1959 } 1960 return false; 1961 } 1962 } 1963 else { 1964 if (MXT.getCookie('ismobile')) { 1965 return MXT.getCookie('ismobile').toLowerCase() === 'true' ? true : false; 1966 } 1967 return false; 1968 } 1969 } 1970 1971 function IsTablet() { 1972 if (MXT.CheckUserAgent(["ipad", "android!mobile", "tablet", "kindle", "playbook", "xoom", "sch-i800"])) { 1973 return true; 1974 } 1975 return false; 1976 } 1977})();; 1978(function () { 1979 window.MXT = window.MXT || {}; 1980 1981 // =============== MXT Interface 1982 MXT.ProductUrlCarry = ProductUrlCarry; 1983 1984 // =============== MXT Methods 1985 1986 /** 1987 * Finds Sign Up or Upgrade buttons and attaches any page url querystrings to it's href 1988 * Button querystrings have a higher priority than the page querystrings. So if both have 1989 * the same parameter, the button rules. 1990 * */ 1991 function ProductUrlCarry() { 1992 let pagequerystring, productBuyButtons, buttonUrl, $button, url; 1993 1994 // Get pages url 1995 pagequerystring = MXT.MxUrl(document.location.href); 1996 1997 // Grab all upgrade buttons on page 1998 productBuyButtons = $('.--upgrade'); 1999 2000 productBuyButtons.each(function (index, button) { 2001 // Get button iteration back into jquery 2002 $button = $(button); 2003 // Get this button's url 2004 buttonUrl = MXT.MxUrl($button.attr('href')); 2005 2006 // Copy the all of the button params into the page url 2007 // This sets the priority of buttons over page 2008 pagequerystring.MergeParams(buttonUrl.params); 2009 2010 // Copy the params back into the button
2011 buttonUrl.MergeParams(pagequerystring.params); 2012 2013 // Assign the full url to the button 2014 url = buttonUrl.GetUrl(); 2015 $button.attr('href', url); 2016 }); 2017 } 2018})(); 2019/** 2020 * Object.assign() 2021 2022 * The Object.assign() method is used to copy the values of all enumerable own properties 2023 * from one or more source objects to a target object. It will return the target object. 2024 */ 2025if (typeof Object.assign !== 'function') { 2026 // Must be writable: true, enumerable: false, configurable: true 2027 Object.defineProperty(Object, "assign", { 2028 value: function assign(target, varArgs) { // .length of function is 2 2029 'use strict'; 2030 if (target === null || target === undefined) { 2031 throw new TypeError('Cannot convert undefined or null to object'); 2032 } 2033 2034 var to = Object(target); 2035 2036 for (var index = 1; index < arguments.length; index++) { 2037 var nextSource = arguments[index]; 2038 2039 if (nextSource !== null && nextSource !== undefined) { 2040 for (var nextKey in nextSource) { 2041 // Avoid bugs when hasOwnProperty is shadowed 2042 if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { 2043 to[nextKey] = nextSource[nextKey]; 2044 } 2045 } 2046 } 2047 } 2048 return to; 2049 }, 2050 writable: true, 2051 configurable: true 2052 }); 2053} 2054 2055 2056 2057/** 2058 * window.Promise Polyfill 2059 2060 * Lightweight ES6 Promise polyfill for the browser and node. Adheres closely to the spec. 2061 * It is a perfect polyfill IE or any other browser that does not support native promises. 2062 */ 2063(function (global, factory) { 2064 typeof exports === 'object' && typeof module !== 'undefined' ? factory() : 2065 typeof define === 'function' && define.amd ? define(factory) : 2066 (factory()); 2067}(this, (function () { 2068 'use strict'; 2069 2070 /** 2071 * @this {Promise} 2072 */ 2073 function finallyConstructor(callback) { 2074 var constructor = this.constructor; 2075 return this.then( 2076 function (value) { 2077 // @ts-ignore 2078 return constructor.resolve(callback()).then(function () { 2079 return value; 2080 }); 2081 }, 2082 function (reason) { 2083 // @ts-ignore 2084 return constructor.resolve(callback()).then(function () { 2085 // @ts-ignore 2086 return constructor.reject(reason); 2087 }); 2088 } 2089 ); 2090 } 2091 2092 // Store setTimeout reference so promise-polyfill will be unaffected by 2093 // other code modifying setTimeout (like sinon.useFakeTimers()) 2094 var setTimeoutFunc = setTimeout; 2095 2096 function isArray(x) { 2097 return Boolean(x && typeof x.length !== 'undefined'); 2098 } 2099 2100 function noop() { } 2101 2102 // Polyfill for Function.prototype.bind 2103 function bind(fn, thisArg) { 2104 return function () { 2105 fn.apply(thisArg, arguments); 2106 }; 2107 } 2108 2109 /** 2110 * @constructor 2111 * @param {Function} fn 2112 */ 2113 function Promise(fn) { 2114 if (!(this instanceof Promise)) 2115 throw new TypeError('Promises must be constructed via new'); 2116 if (typeof fn !== 'function') throw new TypeError('not a function'); 2117 /** @type {!number} */ 2118 this._state = 0; 2119 /** @type {!boolean} */ 2120 this._handled = false; 2121 /** @type {Promise|undefined} */ 2122 this._value = undefined; 2123 /** @type {!Array<!Function>} */ 2124 this._deferreds = []; 2125 2126 doResolve(fn, this); 2127 } 2128 2129 function handle(self, deferred) { 2130 while (self._state === 3) { 2131 self = self._value; 2132 } 2133 if (self._state === 0) { 2134 self._deferreds.push(deferred); 2135 return; 2136 } 2137 self._handled = true; 2138 Promise._immediateFn(function () { 2139 var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; 2140 if (cb === null) { 2141 (self._state === 1 ? resolve : reject)(deferred.promise, self._value); 2142 return; 2143 } 2144 var ret; 2145 try { 2146 ret = cb(self._value); 2147 } catch (e) { 2148 reject(deferred.promise, e); 2149 return; 2150 } 2151 resolve(deferred.promise, ret); 2152 }); 2153 } 2154 2155 function resolve(self, newValue) { 2156 try { 2157 // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure 2158 if (newValue === self) 2159 throw new TypeError('A promise cannot be resolved with itself.'); 2160 if ( 2161 newValue && 2162 (typeof newValue === 'object' || typeof newValue === 'function') 2163 ) { 2164 var then = newValue.then; 2165 if (newValue instanceof Promise) { 2166 self._state = 3; 2167 self._value = newValue; 2168 finale(self); 2169 return; 2170 } else if (typeof then === 'function') { 2171 doResolve(bind(then, newValue), self); 2172 return; 2173 } 2174 } 2175 self._state = 1; 2176 self._value = newValue; 2177 finale(self); 2178 } catch (e) { 2179 reject(self, e); 2180 } 2181 } 2182 2183 function reject(self, newValue) { 2184 self._state = 2; 2185 self._value = newValue; 2186 finale(self); 2187 } 2188 2189 function finale(self) { 2190 if (self._state === 2 && self._deferreds.length === 0) { 2191 Promise._immediateFn(function () { 2192 if (!self._handled) { 2193 Promise._unhandledRejectionFn(self._value); 2194 } 2195 }); 2196 } 2197 2198 for (var i = 0, len = self._deferreds.length; i < len; i++) { 2199 handle(self, self._deferreds[i]); 2200 } 2201 self._deferreds = null; 2202 } 2203 2204 /** 2205 * @constructor 2206 */ 2207 function Handler(onFulfilled, onRejected, promise) { 2208 this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; 2209 this.onRejected = typeof onRejected === 'function' ? onRejected : null; 2210 this.promise = promise; 2211 } 2212 2213 /** 2214 * Take a potentially misbehaving resolver function and make sure 2215 * onFulfilled and onRejected are only called once. 2216 * 2217 * Makes no guarantees about asynchrony. 2218 */ 2219 function doResolve(fn, self) { 2220 var done = false;
vendor: 4,440 bytes, lines 2221-2368
2221 try { 2222 fn( 2223 function (value) { 2224 if (done) return; 2225 done = true; 2226 resolve(self, value); 2227 }, 2228 function (reason) { 2229 if (done) return; 2230 done = true; 2231 reject(self, reason); 2232 } 2233 ); 2234 } catch (ex) { 2235 if (done) return; 2236 done = true; 2237 reject(self, ex); 2238 } 2239 } 2240 2241 Promise.prototype['catch'] = function (onRejected) { 2242 return this.then(null, onRejected); 2243 }; 2244 2245 Promise.prototype.then = function (onFulfilled, onRejected) { 2246 // @ts-ignore 2247 var prom = new this.constructor(noop); 2248 2249 handle(this, new Handler(onFulfilled, onRejected, prom)); 2250 return prom; 2251 }; 2252 2253 Promise.prototype['finally'] = finallyConstructor; 2254 2255 Promise.all = function (arr) { 2256 return new Promise(function (resolve, reject) { 2257 if (!isArray(arr)) { 2258 return reject(new TypeError('Promise.all accepts an array')); 2259 } 2260 2261 var args = Array.prototype.slice.call(arr); 2262 if (args.length === 0) return resolve([]); 2263 var remaining = args.length; 2264 2265 function res(i, val) { 2266 try { 2267 if (val && (typeof val === 'object' || typeof val === 'function')) { 2268 var then = val.then; 2269 if (typeof then === 'function') { 2270 then.call( 2271 val, 2272 function (val) { 2273 res(i, val); 2274 }, 2275 reject 2276 ); 2277 return; 2278 } 2279 } 2280 args[i] = val; 2281 if (--remaining === 0) { 2282 resolve(args); 2283 } 2284 } catch (ex) { 2285 reject(ex); 2286 } 2287 } 2288 2289 for (var i = 0; i < args.length; i++) { 2290 res(i, args[i]); 2291 } 2292 }); 2293 }; 2294 2295 Promise.resolve = function (value) { 2296 if (value && typeof value === 'object' && value.constructor === Promise) { 2297 return value; 2298 } 2299 2300 return new Promise(function (resolve) { 2301 resolve(value); 2302 }); 2303 }; 2304 2305 Promise.reject = function (value) { 2306 return new Promise(function (resolve, reject) { 2307 reject(value); 2308 }); 2309 }; 2310 2311 Promise.race = function (arr) { 2312 return new Promise(function (resolve, reject) { 2313 if (!isArray(arr)) { 2314 return reject(new TypeError('Promise.race accepts an array')); 2315 } 2316 2317 for (var i = 0, len = arr.length; i < len; i++) { 2318 Promise.resolve(arr[i]).then(resolve, reject); 2319 } 2320 }); 2321 }; 2322 2323 // Use polyfill for setImmediate for performance gains 2324 Promise._immediateFn = 2325 // @ts-ignore 2326 (typeof setImmediate === 'function' && 2327 function (fn) { 2328 // @ts-ignore 2329 setImmediate(fn); 2330 }) || 2331 function (fn) { 2332 setTimeoutFunc(fn, 0); 2333 }; 2334 2335 Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) { 2336 if (typeof console !== 'undefined' && console) { 2337 console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console 2338 } 2339 }; 2340 2341 /** @suppress {undefinedVars} */ 2342 var globalNS = (function () { 2343 // the only reliable means to get the global object is 2344 // `Function('return this')()` 2345 // However, this causes CSP violations in Chrome apps. 2346 if (typeof self !== 'undefined') { 2347 return self; 2348 } 2349 if (typeof window !== 'undefined') { 2350 return window; 2351 } 2352 if (typeof global !== 'undefined') { 2353 return global; 2354 } 2355 throw new Error('unable to locate global object'); 2356 })(); 2357 2358 if (!('Promise' in globalNS)) { 2359 globalNS['Promise'] = Promise; 2360 } else if (!globalNS.Promise.prototype['finally']) { 2361 globalNS.Promise.prototype['finally'] = finallyConstructor; 2362 } 2363 2364})));; 2365(function () { 2366 // 2367 // !! THIS FILE REQUIRES MXSHARED !! 2368 //
2369 // Do not copy this this script is intended to be unique 2370 // and shared among all the master pages 2371 // all the functions here must be declared here and only here 2372 let _proABTests = {}; 2373 let _abTestTriggers = {}; 2374 2375 window.mxtv = []; 2376 2377 window.MXT = window.MXT || {}; 2378 2379 // =============== MXT Interface 2380 MXT.setVariationCookie = SetVariationCookie; 2381 MXT.SetVariationCookie = SetVariationCookie; // MXT.SetVariationCookie is the preferred usage 2382 2383 // =============== MXT Methods 2384 function SetVariationCookie(testid, variation) { 2385 try { 2386 if (testid && variation) { 2387 var setVariationApi = function () { 2388 MXT.API('PUT', '/utils/setvariation', function (data) { window.mxtv.push(data); }, { data: JSON.stringify({ Name: testid, Value: variation }), async: false }); 2389 // Send to GA 2390 if (testid != 'GTM-Blocked') { 2391 WaitAndSendAB(0, testid, variation); 2392 } 2393 } 2394 if (MXT.API) { 2395 setVariationApi(); 2396 } else { 2397 // API not available yet, wait for it 2398 document.addEventListener('mx-shared-ready', setVariationApi); 2399 } 2400 } 2401 } catch (e) { 2402 var errHandler = function () { window.MxErrorHandler('TestId:' + testid + ',Variation:' + variation, 'Error from MXT.SetVariationCookieGA4', e); } 2403 if (window.MxErrorHandler) { errHandler(); } 2404 else { document.addEventListener('mx-shared-ready', errHandler); } 2405 } 2406 } 2407 2408 // =============== MXT INTERNAL AB Testing System 2409 (function CheckInternalABTests() { 2410 var getVariationApi = function () { 2411 MXT.API('GET', '/variation/check?system=0&path=' + encodeURIComponent(window.location.pathname + window.location.search), function (data) { 2412 if (data) { 2413 for (var i = 0; i < data.length; i++) { 2414 let abTest = data[i]; 2415 // We only care about the result if is a variation 2416 if (abTest.Result == 'Variation') { 2417 // Split or Ab Test? 2418 if (abTest.TestType.toLowerCase() === 'split_url') { 2419 // Split Test 2420 // Keep the querystring 2421 var redirectUrl = MXT.GetMxWebsite() + abTest.VariationArgument + window.location.search; 2422 window.location.replace(redirectUrl); 2423 } else if (abTest.TestType.toLowerCase() === 'ab_test') { 2424 // AB Test 2425 // Get the variation script and execute it 2426 $.ajaxSetup({ cache: true }); // Prevents the script from adding a cache buster 2427 $.getScript("/api/v2/variation/script?name=" + abTest.AbTestName); 2428 // We could return the script within the VariationArgument and execute it here 2429 // But we'd need to execute it after the page is loaded via "eval" 2430 } 2431 } 2432 } 2433 } 2434 }, { async: true, isV2: true }); 2435 } 2436 if (MXT.API) { 2437 getVariationApi(); 2438 } else { 2439 // API not available yet, wait for it 2440 document.addEventListener('mx-shared-ready', getVariationApi); 2441 } 2442 })(); 2443 2444 /** 2445 * ABTest system overview: 2446 * Optimize, VWO or other ABTest systems will need to run custom functions within our code. These functions need access to 2447 * data in order to modify it or for decision making. When the page loads, Optimize and VWO's functions run immediately, 2448 * way before any data is available to them, so we need to store these functions into a queue that can run later once 2449 * there is data. 2450 * 2451 * There are 2 areas of concern in order to implement. 2452 * 2453 * Triggers 2454 * Devs need to set up trigger types and determine where they live. Triggers are what pass data to the ABTests so they have to be 2455 * placed accordingly. You create a trigger in the code by calling the 'AddABTestTrigger' method. The first parameter is the 2456 * 'type'. When naming the type, come up with something that describes the kind of parameters you'll be passing in. For 2457 * instance, if we wanted to call AddABTestTrigger that allows AB Tests to modify the main menu array, we could use 2458 * MXT.AddABTestTrigger('MainMenuArray', myMenuArray), then in the Optimize/VWO, we would put 2459 * MXT.AddABTest('MainMenuArray',function(myPassedInMenuArray){ ... code that modifies the array ... }) 2460 * We can put AddABTestTrigger anywhere, like the application's run cycle, a specific controller or in a specific event. 2461 * 2462 * AB Tests 2463 * Second is the method 'AddABTest'. In Optimize, VWO or other ABTest system, we use the AddABTest method to add functions to 2464 * a queue. We give the method an existing 'type' (defined with the placement of the AddABTestTrigger method) 2465 * so that the AddABTest passed in function will know what parameters to expect. 2466 * 2467 * Basically, when the code detects the existence of a Trigger and a Test of the same 'type', it will execute the Test code 2468 * by passing in the Trigger data. Then the trigger is deleted from memory. 2469 * 2470 * The dev team will be responsible for determining where the AddABTestTrigger methods are called and defined, and the ABTest devs will be responsible for 2471 * using what we've defined within the ab test suites, so we have to make sure that with each type defined, the ABTest devs are 2472 * aware that they can use it. 2473 * */ 2474 2475 /* 2476 * ***** Triggers defined: (Please add to this list when creating a new trigger) ***** 2477 * 'API_UserLoaded' 2478 * Location: /scripts/shared/mxUser.js in getUserSuccess() 2479 * Params: function(user){} 2480 * 2481 * 'OnStateChange' 2482 * Location: /src/code/general/ctlr.global.js in StateChangeSuccess(). 2483 * Params: function(toState,toParams,user){} 2484 * 2485 * 'ServiceProviderMenu'
2486 * Location: /src/code/general/ctlr.global.js in init() 2487 * Params: function(serviceProviderMenu){} 2488 * 2489 * 'ClassicLookup' 2490 * Location: /Public/scripts/MxAngular/supertool.js 2491 * Params: data? 2492 * 2493 * 'Supertool_Ads_Loaded' 2494 * Location: /Public/scripts/mxmaster.js 2495 * Params: {}? 2496 * 2497 * 'SuperToolLookup' 2498 * Location: /Public/scripts/SuperTool.2.js 2499 * Params: function(lookupResult){} 2500 * 2501 * 'ProLookup' 2502 * Location: /src/code/lookup/controllers/ctlr.lookup.results.js 2503 * Params: data? 2504 */ 2505 2506 MXT.AddABTest = AddABTest; 2507 MXT.AddABTestTrigger = AddABTestTrigger; 2508 MXT.GetABTests = GetABTests; 2509 MXT.GetABTestTriggers = GetABTestTriggers; 2510 2511 /** 2512 * Adds an AB Test that will get run if there are any triggers with the same type 2513 * @param type 2514 * @param fn 2515 * @constructor 2516 */ 2517 function AddABTest(type, fn) { 2518 let currentABTypeList = _proABTests[type]; 2519 2520 if (!currentABTypeList) { 2521 currentABTypeList = { 2522 type: type, 2523 tests: [] 2524 }; 2525 _proABTests[type] = currentABTypeList; 2526 } 2527 2528 currentABTypeList.tests.push({ 2529 func: fn, 2530 runCount: 0 2531 }); 2532 2533 RunInternalABTests(type); 2534 } 2535 2536 /** 2537 * This adds a trigger to the trigger queue.. as soon as a test is available, the test will run. 2538 * Triggers will delete themselves from the queue once complete. 2539 * @param param1 2540 * @param param2 2541 * @param param3 2542 * @param param4 2543 * @param param5 2544 * @constructor 2545 */ 2546 function AddABTestTrigger(type, param1, param2, param3, param4, param5) { 2547 let trigger = { 2548 type: type, 2549 param1: param1, 2550 param2: param2, 2551 param3: param3, 2552 param4: param4, 2553 param5: param5 2554 }; 2555 2556 // We only save the latest trigger per type 2557 _abTestTriggers[type] = trigger; 2558 2559 RunInternalABTests(type); 2560 } 2561 2562 /** 2563 * Returns the current list of AB Tests that have been loaded 2564 * @param {any} type 2565 */ 2566 function GetABTests(type) { 2567 return type ? _proABTests[type] : _proABTests; 2568 } 2569 2570 2571 /** 2572 * Returns the current queue of AB Test triggers 2573 * @param {any} type 2574 */ 2575 function GetABTestTriggers(type) { 2576 return type ? _abTestTriggers[type] : _abTestTriggers; 2577 } 2578 2579 /** 2580 * This matches up triggers and AB tests. 2581 * @param {any} type 2582 */ 2583 function RunInternalABTests(type) { 2584 let i, len; 2585 2586 let currentTests = _proABTests[type]; 2587 let trigger = _abTestTriggers[type]; 2588 2589 if (currentTests && trigger) { 2590 for (i = 0, len = currentTests.tests.length; i < len; i++) { 2591 currentTests.tests[i].runCount += 1; 2592 currentTests.tests[i].func(trigger.param1, trigger.param2, trigger.param3, trigger.param4, trigger.param5); 2593 } 2594 // Done with trigger. Delete it. 2595 _abTestTriggers[type] = null; 2596 } 2597 } 2598 2599 // =============== Self Calling Functions 2600 MXT.CheckBlockedAB_VWO = CheckBlockedAB_VWO; 2601 MXT.CheckBlockedAB_GA = CheckBlockedAB_GA; 2602 MXT.CheckExcludedAB_VWO = CheckExcludedAB_VWO; 2603 MXT.CheckCookieVariationQuerystring = CheckCookieVariationQuerystring; 2604 2605 function CheckBlockedAB_VWO(retries) { 2606 MXT.WaitForVWO(function () { 2607 if (MXT.VWO_Blocked) { 2608 MXT.SetVariationCookie('VWO-Blocked', 'true'); 2609 } else { 2610 // VVO is not blocked check for segment exclusion 2611 CheckExcludedAB_VWO(); 2612 } 2613 }); 2614 } 2615 2616 function CheckExcludedAB_VWO(retries) { 2617 retries = retries || 0; 2618 2619 // Check for vwo experiment ids 2620 if (window._vwo_exp_ids) { 2621 for (var i = 0; i < _vwo_exp_ids.length; i++) { 2622 // Get the vwo experiment Id 2623 var id = _vwo_exp_ids[i]; 2624 var exp = _vwo_exp[id]; 2625 // This is our internal AB Test Id 2626 var testId = exp.name.split(' ')[0]; 2627 // If the _exclude cookie is present set it, mark it as "excluded" 2628 if (MXT.getCookie('_vis_opt_exp_' + id + '_exclude')) { 2629 MXT.SetVariationCookie(testId, 'excluded'); 2630 } 2631 } 2632 } else if (retries < 10) { 2633 // If is not ready yet, retry for 2 sec 2634 setTimeout(function () { CheckExcludedAB_VWO(retries + 1); }, 200); 2635 } 2636 } 2637 2638 function WaitAndSendAB(retries, testid, variation) { 2639 // Initialize 2640 testid = testid || null; 2641 variation = variation || null; 2642 2643 // Check if GTM has Loaded 2644 MXT.WaitForGTM(function () { 2645 if (MXT.GTM_Blocked) { 2646 MXT.SetVariationCookie('GTM-Blocked', 'true'); 2647 } else if (testid !== null) { 2648 //GTAG is not blocked execute GTAG Event 2649 window.dataLayer = window.dataLayer || []; 2650 2651 window.dataLayer.push({ 2652 'ab_test_id': testid, 2653 'ab_test_val': variation 2654 }); 2655 2656 //Pushing the event will also push the variables defined in the dataLayer 2657 //This event is configured in GTM to trigger the GA4 event when the dataLayer is updated 2658 window.dataLayer.push({ 'event': 'abtest_javascript_trigger' }); 2659 } 2660 }); 2661 } 2662 2663 function CheckBlockedAB_GA() { 2664 WaitAndSendAB(); 2665 } 2666 2667 /** 2668 * CheckCookieVariationQuerystring looks at a page's querystring to determine if 2669 * a cookie should be written for an ABTests. This includes validating the information 2670 * before a cookie is written so bad data is not introduced to our systems. */ 2671 function CheckCookieVariationQuerystring() { 2672 let qs, testid, variation, i, param, len; 2673 2674 // Starts with AB- or CTA-, etc. and ends with 1 to 5 digits with an optional single letter a-f '[STEM]-00000C' 2675 // ex. AB-5345, CTA-1, DATA-300, DATA-300b, etc. 2676 let testIdValidator = /^(AB|CTA|DOM|DNS|WD|DATA|VA|UP)\-\d{1,5}[a-fA-F]{0,1}$/i; 2677 let search = window.location.search[0] === '?' ? window.location.search.substring(1) : window.location.search; 2678 2679 qs = search.split('&'); 2680 2681 for (i = 0, len = qs.length; i < len; i++) { 2682 param = qs[i].split('='); 2683 if (param[0] === 'abt_id') { 2684 // Validation of AB Test Id and make sure there are only 2 values 2685 if (param.length === 2 && testIdValidator.test(param[1])) { 2686 testid = param[1]; 2687 } else { 2688 if (MXT.User && MXT.User.IsAdmin) { 2689 // Temporary for admins 2690 console.log('ABTesting.js - Could not parse abt_id from querystring', window.location); 2691 } 2692 MXT.AppLog.WriteTypedEntry('ABTesting.js', 'Could not parse abt_id from querystring', MXT.AppLog.TraceEventType.Error, window.location.href); 2693 } 2694 } 2695 2696 // Get variation variable.. Should be 'Control' or 'Variation', but I've seen a bunch of others 2697 if (param.length === 2 && param[0] === 'abt_var') {
2698 // Not validating this as I'm not sure of all the values. Could be this: 2699 /* if(param[1].toLowerCase === 'variation') { 2700 * variation = 'Variation'; 2701 * } elseif (param[1].toLowerCase === 'control') { 2702 * variation = 'Control'; 2703 * } else { 2704 * // Write applog and/or set default to 'Unknown' or something 2705 * } 2706 * 2707 **/ 2708 variation = param[1]; 2709 } 2710 } 2711 2712 // Must have a test id and variation type to be considered an AB Test 2713 if (testid && variation) { 2714 MXT.SetVariationCookie(testid, variation); 2715 } 2716 } 2717 2718 /* 2719 * Temp Patch (Master Min does not need nor have this method) 2720 */ 2721 if (MXT.OnUserLoaded) { 2722 MXT.OnUserLoaded(function (event, user) { 2723 var auth = {}; 2724 2725 if (user) { 2726 auth = { 2727 isLoggedIn: user.IsLoggedIn, 2728 isPaid: user.IsPaidUser, 2729 isAdmin: user.IsAdmin 2730 }; 2731 } 2732 2733 MXT.AddABTestTrigger('API_UserLoaded', auth); 2734 }); 2735 } 2736})(); 2737 2738MXT.CheckBlockedAB_VWO(); 2739MXT.CheckBlockedAB_GA(); 2740MXT.CheckCookieVariationQuerystring(); 2741; 2742/* 2743 Test: AB488 2744 Created: 09-09-2019 2745 2746 NOTES: 2747 * Include this comment block in all AB Test JS files 2748 * To create AB Test: 2749 * In MxABTest, create directory named 'AB[000]' 2750 * Create / Copy 'Test_AB[000].js' in directory 'AB[000]', then copy this file and wipe ABTestFunction clean 2751 * If you need HTML, create 'Test_AB[000].html' in directory 'AB[000]' and utilize MXT.ABTestUtil.GetHtmlContent 2752 * If you are using a different IDE than VS, then make sure to include new files into the project 2753*/ 2754(function () { 2755 const testName = 'AB488'; 2756 2757 window.MXT = window.MXT || {}; 2758 MXT.ABTest = MXT.ABTest || {}; 2759 2760 // If HTML is needed 2761 MXT.ABTest[testName] = ABTestFunction; 2762 2763 // If HTML is NOT needed 2764 // MXT.ABTest[testName] = ABTestFunction; 2765 2766 function ABTestFunction() { 2767 // Get all risk free taglines 2768 let elems = $(".--risk-free-tagline"); 2769 elems.html("Try it risk-free with our 14-day satisfaction guarantee!"); 2770 } 2771})();; 2772/* 2773 Test: AB511 2774 Created: 01-28-2020 2775 2776 NOTES: 2777 * Include this comment block in all AB Test JS files 2778 * To create AB Test: 2779 * In MxABTest, create directory named 'AB[000]' 2780 * Create / Copy 'Test_AB[000].js' in directory 'AB[000]', then copy this file and wipe ABTestFunction clean 2781 * If you need HTML, create 'Test_AB[000].html' in directory 'AB[000]' and utilize MXT.ABTestUtil.GetHtmlContent 2782 * If you are using a different IDE than VS, then make sure to include new files into the project 2783*/ 2784(function () { 2785 const testName = 'AB493'; 2786 2787 window.MXT = window.MXT || {}; 2788 MXT.ABTest = MXT.ABTest || {}; 2789 2790 // If HTML is needed 2791 // MXT.ABTest[testName] = MXT.ABTestUtil.GetHtmlContent(testName, ABTestFunction); 2792 2793 // If HTML is NOT needed 2794 MXT.ABTest[testName] = ABTestFunction; 2795 2796 function ABTestFunction(a, b, c) { 2797 // Even though this test has a VWO segment, we must use a timeout to wait for cookies to 'catch up' 2798 setTimeout(function () { 2799 var status = MXT.GetCookie('_mxt_s'); 2800 if (status === 'anon') { 2801 ShowSignIn('Register to save your Favorite Tools', 'Register', 'network_tools', null, null, 'PB_FREE'); 2802 } 2803 }, 1000); 2804 } 2805})();; 2806/* 2807 Test: AB494 2808 Created: 07-25-2019 2809 2810 NOTES: 2811 * Include this comment block in all AB Test JS files 2812 * To create AB Test: 2813 * In MxABTest, create directory named 'AB[000]' 2814 * Create/Copy 'Test_AB[000].js' in directory 'AB[000]', then copy this file and wipe ABTestFunction clean 2815 * If you need HTML, create 'Test_AB[000].html' in directory 'AB[000]' and utilize MXT.ABTestUtil.GetHtmlContent 2816 * If you are using a different IDE than VS, then make sure to include new files into the project 2817*/ 2818(function () { 2819 const testName = 'AB494'; 2820 2821 window.MXT = window.MXT || {}; 2822 MXT.ABTest = MXT.ABTest || {}; 2823 2824 // If HTML is needed 2825 MXT.ABTest[testName] = MXT.ABTestUtil.GetHtmlContent(testName, ABTestFunction); 2826 2827 // If HTML is NOT needed 2828 // MXT.ABTest[testName] = ABTestFunction; 2829 2830 function ABTestFunction(htmlContent) { 2831 let tilesElem = $('div.tiles'); 2832 let adHtml = $('<div style="text-align: center;">' + htmlContent + '</div>'); 2833 let inputElem = adHtml.find('.callout-banner-input'); 2834 let linkElem = adHtml.find('.callout-banner-button'); 2835 2836 MXT.ABTestUtil.EventSubmitOnEnter(inputElem, EventClick); 2837 2838 linkElem.click(EventClick); 2839 2840 tilesElem.before(adHtml); 2841 2842 function EventClick(evt) { 2843 evt.preventDefault(); 2844 window.location.href = 'http://delivery.mxtoolbox.com' 2845 } 2846 } 2847})();; 2848/* 2849 Test: AB498 2850 Created: 08-26-2019 2851 2852 NOTES: 2853 * Include this comment block in all AB Test JS files 2854 * To create AB Test: 2855 * In MxABTest, create directory named 'AB[000]' 2856 * Create / Copy 'Test_AB[000].js' in directory 'AB[000]', then copy this file and wipe ABTestFunction clean 2857 * If you need HTML, create 'Test_AB[000].html' in directory 'AB[000]' and utilize MXT.ABTestUtil.GetHtmlContent 2858 * If you are using a different IDE than VS, then make sure to include new files into the project 2859*/ 2860(function () { 2861 const testName = 'AB498'; 2862 2863 window.MXT = window.MXT || {}; 2864 MXT.ABTest = MXT.ABTest || {}; 2865 2866 // If HTML is needed 2867 MXT.ABTest[testName] = MXT.ABTestUtil.GetHtmlContent(testName, ABTestFunction); 2868 2869 // If HTML is NOT needed 2870 // MXT.ABTest[testName] = ABTestFunction; 2871 2872 function ABTestFunction(htmlContent) { 2873 // Add fixedDiv to Foooter for scrolling 2874 let daFooter = $("footer.container-fluid"); 2875 daFooter.prepend(htmlContent); 2876 } 2877})();; 2878/* 2879 Test: AB499 2880 Created: 08-26-2019 2881 2882 NOTES: 2883 * Include this comment block in all AB Test JS files 2884 * To create AB Test: 2885 * In MxABTest, create directory named 'AB[000]' 2886 * Create / Copy 'Test_AB[000].js' in directory 'AB[000]', then copy this file and wipe ABTestFunction clean 2887 * If you need HTML, create 'Test_AB[000].html' in directory 'AB[000]' and utilize MXT.ABTestUtil.GetHtmlContent 2888 * If you are using a different IDE than VS, then make sure to include new files into the project 2889*/ 2890(function () { 2891 const testName = 'AB499'; 2892 2893 window.MXT = window.MXT || {}; 2894 MXT.ABTest = MXT.ABTest || {}; 2895 2896 // If HTML is needed 2897 //MXT.ABTest[testName] = MXT.ABTestUtil.GetHtmlContent(testName, ABTestFunction); 2898 2899 // If HTML is NOT needed 2900 MXT.ABTest[testName] = ABTestFunction; 2901 2902 function ABTestFunction(htmlContent) {
2903 // Change some colors and some text 2904 $('[id$="hlLeadTag"]').text("Schedule a Walkthrough").removeClass("btn-warning").addClass("btn-success"); 2905 $('#BH-MGDC-Buy, #MGDC-PROCESS-BUY, #DCM-1-Buy').text("Sign Up"); 2906 } 2907})();; 2908/* 2909 Test: AB500 2910 Created: 09-05-2019 2911 2912 NOTES: 2913 * Include this comment block in all AB Test JS files 2914 * To create AB Test: 2915 * In MxABTest, create directory named 'AB[000]' 2916 * Create / Copy 'Test_AB[000].js' in directory 'AB[000]', then copy this file and wipe ABTestFunction clean 2917 * If you need HTML, create 'Test_AB[000].html' in directory 'AB[000]' and utilize MXT.ABTestUtil.GetHtmlContent 2918 * If you are using a different IDE than VS, then make sure to include new files into the project 2919*/ 2920(function () { 2921 const testName = 'AB500'; 2922 2923 window.MXT = window.MXT || {}; 2924 MXT.ABTest = MXT.ABTest || {}; 2925 2926 // If HTML is needed 2927 MXT.ABTest[testName] = MXT.ABTestUtil.GetHtmlContent(testName, ABTestFunction); 2928 2929 // If HTML is NOT needed 2930 // MXT.ABTest[testName] = ABTestFunction; 2931 2932 function ABTestFunction(htmlContent) { 2933 // Add a Managed Services 1280 card 2934 $(".template_ad_static").append(htmlContent); 2935 } 2936})();; 2937/* 2938 Test: AB511 2939 Created: 01-28-2020 2940 2941 NOTES: 2942 * Include this comment block in all AB Test JS files 2943 * To create AB Test: 2944 * In MxABTest, create directory named 'AB[000]' 2945 * Create / Copy 'Test_AB[000].js' in directory 'AB[000]', then copy this file and wipe ABTestFunction clean 2946 * If you need HTML, create 'Test_AB[000].html' in directory 'AB[000]' and utilize MXT.ABTestUtil.GetHtmlContent 2947 * If you are using a different IDE than VS, then make sure to include new files into the project 2948*/ 2949(function () { 2950 const testName = 'AB511'; 2951 2952 window.MXT = window.MXT || {}; 2953 MXT.ABTest = MXT.ABTest || {}; 2954 2955 // If HTML is needed 2956 // MXT.ABTest[testName] = MXT.ABTestUtil.GetHtmlContent(testName, ABTestFunction); 2957 2958 // If HTML is NOT needed 2959 MXT.ABTest[testName] = ABTestFunction; 2960 2961 function ABTestFunction(htmlContent) { 2962 // Add htmlContent to Slot1 2963 $(".link_have-more-than-255").addClass('text-md').text('Emails bouncing? MxToolbox has your email delivery solution.'); 2964 } 2965})();; 2966/* 2967 Test: AB516 2968 Created: 03-02-2020 2969 2970 NOTES: 2971 * Include this comment block in all AB Test JS files 2972 * To create AB Test: 2973 * In MxABTest, create directory named 'AB[000]' 2974 * Create / Copy 'Test_AB[000].js' in directory 'AB[000]', then copy this file and wipe ABTestFunction clean 2975 * If you need HTML, create 'Test_AB[000].html' in directory 'AB[000]' and utilize MXT.ABTestUtil.GetHtmlContent 2976 * If you are using a different IDE than VS, then make sure to include new files into the project 2977*/ 2978(function () { 2979 const testName = 'AB516'; 2980 window.MXT = window.MXT || {}; 2981 MXT.ABTest = MXT.ABTest || {}; 2982 2983 // If HTML is needed 2984 // MXT.ABTest[testName] = MXT.ABTestUtil.GetHtmlContent(testName, ABTestFunction); 2985 2986 // If HTML is NOT needed 2987 MXT.ABTest[testName] = ABTestFunction; 2988 2989 function ABTestFunction(lookupResult, htmlContent) { 2990 if (lookupResult) { 2991 if (lookupResult.Command === 'spf') { 2992 try { 2993 RunDMARCLookup(lookupResult.UID, lookupResult.CommandArgument); 2994 } catch (e) { 2995 MxErrorHandler("ABTesting", testName + ": " + e.message, e.stack); 2996 } 2997 } 2998 } 2999 } 3000 3001 function RunDMARCLookup(lookupResultUID, commandArg) { 3002 //Run an additional DMARC Lookup for SPF on SuperTool 3003 let params = { 3004 "command": 'dmarc', 3005 "argument": commandArg, 3006 "format": 0 //Results 3007 }; 3008 $.ajax({ 3009 type: "GET", 3010 url: "api/v1/Lookup", 3011 data: params, 3012 async: false, 3013 contentType: "application/json; charset=utf-8", 3014 dataType: "json", 3015 success: function (res) { ProcessResults(res, lookupResultUID); }, 3016 error: function (xhr, status, error) { 3017 console.log("lookup dns failure"); 3018 }, 3019 beforeSend: function (xhr) { 3020 // Include the anon lookup key if exists 3021 if (window.TempAuthKey) { 3022 xhr.setRequestHeader('TempAuthorization', window.TempAuthKey); 3023 } 3024 } 3025 }); 3026 } 3027 3028 function ProcessResults(response, spfDivResId) { 3029 // We only care about failed subactions and specifically these 2 Dmarc Subactions 3030 let dnsRecordPublished = 506; //506 DNS Record Published 3031 let dmarcPublished = 441; //441 -- DMARC Record Published 3032 let dmarcPolEnabled = 197; //197 -- DMARC Policy Not Enabled 3033 let tableSubactions = $('#' + spfDivResId + '_resultDiv .tool-result-table:last tbody'); 3034 3035 if (tableSubactions.length > 0) { 3036 let htmlElements = []; 3037 3038 //get Columns from SPf results 3039 let dmarcResCols = 3; //All Good 3040 let spfResCols = tableSubactions[0].rows[0].cells.length; //From Spf Result 3041 let addEmptyCol = false;
3042 3043 //check For Failed Subactions 3044 for (let idxF = 0; idxF < response.Failed.length; idxF++) { 3045 //If we have a DNS Record Published Failure, this subaction will already be included on the SPF lookup, dont include it 3046 if (response.Failed[idxF].ID !== dnsRecordPublished) { 3047 dmarcResCols = 4; 3048 addEmptyCol = true; 3049 htmlElements.push(GetSubActionElement(response.Failed[idxF], 'problem')); 3050 } 3051 } 3052 3053 //Check for Warnings Subactions 3054 for (let idxW = 0; idxW < response.Warnings.length; idxW++) { 3055 if (response.Warnings[idxW].ID === dmarcPublished || response.Warnings[idxW].ID === dmarcPolEnabled) { 3056 dmarcResCols = 4; 3057 addEmptyCol = true; 3058 htmlElements.push(GetSubActionElement(response.Warnings[idxW], 'warning')); 3059 } 3060 } 3061 3062 //Check for Passed Subactions 3063 for (let idxP = 0; idxP < response.Passed.length; idxP++) { 3064 if (response.Passed[idxP].ID === dmarcPublished || response.Passed[idxP].ID === dmarcPolEnabled) { 3065 htmlElements.push(GetSubActionElement(response.Passed[idxP], 'ok', addEmptyCol)); 3066 } 3067 } 3068 3069 3070 if (dmarcResCols > spfResCols) { 3071 //Add a an empty header column to the Subactions table header 3072 $('#' + spfDivResId + '_resultDiv .tool-result-table:last thead tr').append('<th></th>'); 3073 //We need Add empty cols for the whole table 3074 tableSubactions.find('tr').append('<td></td>'); 3075 } 3076 3077 for (let i = 0; i < htmlElements.length; i++) { 3078 if (addEmptyCol && i < response.Failed.length + response.Warnings.length) { 3079 //Place Failed/Warnings subactions on Top 3080 tableSubactions.prepend(htmlElements[i]); 3081 } 3082 else { 3083 tableSubactions.append(htmlElements[i]); 3084 } 3085 } 3086 } 3087 } 3088 3089 function GetSubActionElement(subaction, statusIcon, addEmpty) { 3090 let infoCol = ''; 3091 if (statusIcon !== 'ok') { 3092 //the MoreInfo Icon is only placed on Warnings and Fails 3093 infoCol = '<td class="force-width" width="100px"> <a api-data="' + subaction.Name.replace(' ', '-') + '" href="' + subaction.Url + '&showlogin=1&hidepitch=0&hidetoc=1" class="more-info-link ' + subaction.Name.toLowerCase().replace(' ', '-') + '" target="_blank" onclick="return true;"><img src="/public/images/StatusIcons/info.png" width="17"> More Info </a></td>'; 3094 } 3095 else if (addEmpty) { 3096 //Add Empty Column 3097 infoCol = '<td></td>'; 3098 } 3099 return '<tr><td class="table-column-Status"><a href="' + subaction.Url + '&showlogin=1&hidepitch=0&hidetoc=1" target="_blank"><img src="https://mxtoolbox.com/public/images/statusicons/' + statusIcon + '.png" width="17"></a></td><td class="table-column-Name">' + subaction.Name + '</td> <td class="table-column-Response">' + subaction.Info + '</td> ' + infoCol + '</tr>'; 3100 } 3101 3102})();; 3103/* 3104 Test: AB517 3105 Created: 05-05-2020 3106 3107 NOTES: 3108 * Include this comment block in all AB Test JS files 3109 * To create AB Test: 3110 * In MxABTest, create directory named 'AB[000]' 3111 * Create / Copy 'Test_AB[000].js' in directory 'AB[000]', then copy this file and wipe ABTestFunction clean 3112 * If you need HTML, create 'Test_AB[000].html' in directory 'AB[000]' and utilize MXT.ABTestUtil.GetHtmlContent 3113 * If you are using a different IDE than VS, then make sure to include new files into the project 3114*/ 3115(function () { 3116 const testName = 'AB517'; 3117 3118 window.MXT = window.MXT || {}; 3119 MXT.ABTest = MXT.ABTest || {}; 3120 3121 // If HTML is needed 3122 //MXT.ABTest[testName] = MXT.ABTestUtil.GetHtmlContent(testName, ABTestFunction); 3123 3124 // If HTML is NOT needed 3125 MXT.ABTest[testName] = ABTestFunction; 3126 3127 function ABTestFunction(htmlContent) { 3128 OrderTools(); 3129 CreateMostPopularTab(); 3130 MarkMostPopular(); 3131 } 3132 3133 /** 3134 * If implementing this, just do a orderby in the code behind.*/ 3135 function OrderTools() { 3136 let tileContainer = $('div.tiles ul'); 3137 3138 tileContainer.children('li').sort(function (a, b) { 3139 let aTitle = $(a).find('._title').text(); 3140 let bTitle = $(b).find('._title').text(); 3141 return (aTitle < bTitle) ? -1 : (bTitle > bTitle) ? 1 : 0; 3142 }).appendTo(tileContainer); 3143 } 3144 3145 function CreateMostPopularTab() { 3146 let navbar = $('<li class="most-popular-tab">'); 3147 let link = $('<a href="#" data-toggle="tab" aria-expanded="false">Most Popular</a>'); 3148 link.click(function () { 3149 showTab('most-popular'); 3150 }); 3151 navbar.append(link); 3152 $('.email-tab').before(navbar); 3153 } 3154 3155 function MarkMostPopular() { 3156 let mostPopular = ['mx', 'blacklist', 'dmarc', 'smtp', 'spf', 'whois', 'dns', 'bimi', 'dkim', 'dmarc', 'header analyzer', 'ptr']; 3157 let tileContainer = $('div.tiles ul'); 3158 tileContainer.children('li').each(function (index, elem) { 3159 let $elem = $(elem); 3160 let title = $elem.find('._title').text(); 3161 if (mostPopular.indexOf(title) > -1) { 3162 $elem.addClass('most-popular'); 3163 } 3164 }) 3165 } 3166})();; 3167/* 3168 Test: AB520 3169 Created: 03-08-2020 3170 3171 NOTES: 3172 * Include this comment block in all AB Test JS files 3173 * To create AB Test: 3174 * In MxABTest, create directory named 'AB[000]' 3175 * Create / Copy 'Test_AB[000].js' in directory 'AB[000]', then copy this file and wipe ABTestFunction clean 3176 * If you need HTML, create 'Test_AB[000].html' in directory 'AB[000]' and utilize MXT.ABTestUtil.GetHtmlContent 3177 * If you are using a different IDE than VS, then make sure to include new files into the project 3178*/ 3179(function () { 3180 const testName = 'AB520'; 3181 3182 window.MXT = window.MXT || {}; 3183 MXT.ABTest = MXT.ABTest || {}; 3184 3185 // If HTML is needed 3186 MXT.ABTest[testName] = MXT.ABTestUtil.GetHtmlContent(testName, ABTestFunction); 3187 3188 // If HTML is NOT needed 3189 // MXT.ABTest[testName] = ABTestFunction; 3190 3191 function ABTestFunction(htmlContent) { 3192 // Using MXT Event Triggering to activate test. Trigger is in ctlr.monitors.js 3193 MXT.AddABTest('monitors-loaded', function () { 3194 $(".mx-mon-view-card").prepend(htmlContent); 3195 }) 3196 } 3197})();; 3198/* 3199 Test: AB524 3200 Created: 03-08-2020 3201 3202 NOTES: 3203 * Include this comment block in all AB Test JS files 3204 * To create AB Test: 3205 * In MxABTest, create directory named 'AB[000]' 3206 * Create / Copy 'Test_AB[000].js' in directory 'AB[000]', then copy this file and wipe ABTestFunction clean 3207 * If you need HTML, create 'Test_AB[000].html' in directory 'AB[000]' and utilize MXT.ABTestUtil.GetHtmlContent 3208 * If you are using a different IDE than VS, then make sure to include new files into the project 3209*/ 3210(function () { 3211 const testName = 'AB524'; 3212 3213 window.MXT = window.MXT || {}; 3214 MXT.ABTest = MXT.ABTest || {}; 3215 3216 // If HTML is needed. Make SURE to put an ID on your containing div inside the HTML 3217 MXT.ABTest[testName] = MXT.ABTestUtil.GetHtmlContent(testName, ABTestFunction); 3218 3219 // If HTML is NOT needed 3220 // MXT.ABTest[testName] = ABTestFunction; 3221 3222 function ABTestFunction(htmlContent) { 3223 // Using MXT Event Triggering to activate test. Trigger is in ctlr.monitors.js 3224 MXT.AddABTest('monitors-loaded', function () { 3225 // Make sure html does not already exist. 3226 let existingTest = $('#abtest_' + testName); 3227 3228 if (existingTest.length === 0) { 3229 $(".mx-mon-view-card").prepend(htmlContent); 3230 } 3231 }); 3232 } 3233})();; 3234/* 3235 Test: AB528 3236 Created: 10-09-2020 3237 3238 NOTES: 3239 * Include this comment block in all AB Test JS files 3240 * To create AB Test: 3241 * In MxABTest, create directory named 'AB[000]' 3242 * Create / Copy 'Test_AB[000].js' in directory 'AB[000]', then copy this file and wipe ABTestFunction clean 3243 * If you need HTML, create 'Test_AB[000].html' in directory 'AB[000]' and utilize MXT.ABTestUtil.GetHtmlContent 3244 * If you are using a different IDE than VS, then make sure to include new files into the project 3245*/ 3246(function () { 3247 const testName = 'AB528'; 3248 3249 window.MXT = window.MXT || {}; 3250 MXT.ABTest = MXT.ABTest || {}; 3251 3252 // If HTML is needed 3253 MXT.ABTest[testName] = ABTestFunction; 3254 3255 // If HTML is NOT needed 3256 // MXT.ABTest[testName] = ABTestFunction; 3257 3258 function ABTestFunction() { 3259 // Get all risk free taglines 3260 let elems = $("#supertool_ad_variation"); 3261 let wind = $(window); 3262 3263 // To INSTALL this, change class 'alert_panel' to the 3264 // properies below. Add scroll function to the file 3265 // src/code/monitors/monitors.ctlr.js 3266 elems.removeClass("alert_panel"); 3267 3268 elems.css({ 3269 position: 'fixed', 3270 right: '0', 3271 top: '113px' 3272 }); 3273 3274 wind.scroll(function () { 3275 let top = 113 - wind.scrollTop(); 3276 top = top < 0 ? 0 : top; 3277 elems.css('top', top + 'px'); 3278 }); 3279 } 3280})();; 3281/* 3282 Test: AB529 3283 Created: 07-13-2020 3284 3285 NOTES: 3286 * Include this comment block in all AB Test JS files 3287 * To create AB Test: 3288 * In MxABTest, create directory named 'AB[000]' 3289 * Create / Copy 'Test_AB[000].js' in directory 'AB[000]', then copy this file and wipe ABTestFunction clean 3290 * If you need HTML, create 'Test_AB[000].html' in directory 'AB[000]' and utilize MXT.ABTestUtil.GetHtmlContent 3291 * If you are using a different IDE than VS, then make sure to include new files into the project 3292*/ 3293(function () { 3294 const testName = 'AB529'; 3295 3296 window.MXT = window.MXT || {}; 3297 MXT.ABTest = MXT.ABTest || {}; 3298 3299 MXT.ABTest[testName] = ABTestFunction; 3300 3301 function ABTestFunction(htmlContent) { 3302 3303 //Changes after cookie has been set 3304 let _CookieVarVal = MXT.GetCookie('_mx_vtc'); 3305 if (!!_CookieVarVal && _CookieVarVal.indexOf('AB-529=Variation') !== -1) { 3306 MXT.AddABTest('domain-tab', function () { 3307 //Change Domain Health to Email Health 3308 $('#spanReportTitle').text('Email Health Report'); 3309 $('#btnLookup').text('Check Email Health'); 3310 $('#AB_529-H2').text('ABOUT EMAIL HEALTH'); 3311 $('#AB_529-P3').text('The Email Health Check will execute hun
3311dreds of domain/email/network performance tests to make sure all of your systems are online and performing optimally. The report will then return results for your domain and highlight critical problem areas for your domain that need to be resolved.'); 3312 $('#AB_529-B1').text('Email Health Monitoring Features'); 3313 }); 3314 3315 // Trigger to change Menu 3316 MXT.AddABTest('domain-menu', function (toolsMenu) { 3317 //Find Domain Health and Transform to Email Health 3318 toolsMenu[0].sub.left.forEach(function(menuleft) { 3319 if (menuleft.text === 'Domain Health') { 3320 menuleft.text = 'Email Health'; 3321 } 3322 }); 3323 }); 3324 3325 //Trigger Change After check click 3326 MXT.AddABTest('domain-click', function () { 3327 //Replace Domain HEalth After click 3328 let _replaceText = $('#spanReportTitle').text(); 3329 $('#spanReportTitle').text(_replaceText.replace('Domain Health', 'Email Health')); 3330 }); 3331 3332 //Trigger Change On NetWorktools 3333 MXT.AddABTest('domain-networks', function () { 3334 //Grab all Controller Labels and look for Domain to REplace 3335 $.each($("[id$='lblSubTitle']"), function (index, val) { 3336 if (val.innerText === 'Domain Health Report') { 3337 let _replaceHTML = val.innerHTML; 3338 val.innerHTML = _replaceHTML.replace('Domain Health', 'Email Health'); 3339 } 3340 }); 3341 }); 3342 } else { 3343 //TriggerForSwitchinMenuTheFirstTimeCookie is Setup, Trigger Only called by the AB test when cookie is set 3344 MXT.AddABTest('domain-first', function () { 3345 //Grab all Controller Labels and look for Domain to REplace 3346 $.each($(".subnav-item-domain"), function (index, val) { 3347 if (val.innerText === 'Domain Health') { 3348 let _replaceHTML = val.innerHTML; 3349 val.innerHTML = _replaceHTML.replace('Domain Health', 'Email Health'); 3350 } 3351 }); 3352 }); 3353 } 3354 3355 } 3356})();; 3357/* 3358 Test: AB537 3359 Created: 9/15/2020 3360 3361 NOTES: 3362 * Include this comment block in all AB Test JS files 3363 * To create AB Test: 3364 * In MxABTest, create directory named 'AB[000]' 3365 * Create / Copy 'Test_AB[000].js' in directory 'AB[000]', then copy this file and wipe ABTestFunction clean 3366 * If you need HTML, create 'Test_AB[000].html' in directory 'AB[000]' and utilize MXT.ABTestUtil.GetHtmlContent 3367 * If you are using a different IDE than VS, then make sure to include new files into the project 3368*/ 3369(function () { 3370 const testName = 'AB537'; 3371 3372 window.MXT = window.MXT || {}; 3373 MXT.ABTest = MXT.ABTest || {}; 3374 3375 // If HTML is needed 3376 MXT.ABTest[testName] = MXT.ABTestUtil.GetHtmlContent(testName, ABTestFunction); 3377 3378 // If HTML is NOT needed 3379 //MXT.ABTest[testName] = ABTestFunction; 3380 3381 function ABTestFunction(htmlContent) { 3382 // Add htmlContent after div Cancel Summary 3383 $("[id$='cancelServicesSummary']").after(htmlContent); 3384 3385 //Hide Original Buttons 3386 $("[id$='cancelButton']").hide(); 3387 $("[id$='spanRequestWalkthrough']").hide(); 3388 3389 } 3390})();; 3391/* 3392 Test: AB569 3393 Created: 01-12-2022 3394 3395 NOTES: 3396 * Include this comment block in all AB Test JS files 3397 * To create AB Test: 3398 * In MxABTest, create directory named 'AB[000]' 3399 * Create / Copy 'Test_AB[000].js' in directory 'AB[000]', then copy this file and wipe ABTestFunction clean 3400 * If you need HTML, create 'Test_AB[000].html' in directory 'AB[000]' and utilize MXT.ABTestUtil.GetHtmlContent 3401 * If you are using a different IDE than VS, then make sure to include new files into the project 3402*/ 3403(function () { 3404 const testName = 'AB569'; 3405 3406 window.MXT = window.MXT || {}; 3407 MXT.ABTest = MXT.ABTest || {}; 3408 3409 // If HTML is needed 3410 // MXT.ABTest[testName] = MXT.ABTestUtil.GetHtmlContent(testName, ABTestFunction); 3411 3412 // If HTML is NOT needed 3413 MXT.ABTest[testName] = ABTestFunction; 3414 3415 // AB-569 - Turn off Inline Ads 3416 // If installing this, then: 3417 // * go to public/scripts/inlineAds.js and remove the "AB-569" section in the function ShowAd() (arou
3417nd lines 61 to 66) 3418 // This gets rid of the mechanism that stops the ad 3419 // * go to LookupResultHtmlView.vb and remove the "inline ad" section in the function CreateBody() (around lines 178 to 183) 3420 // This gets rid of the call to get inline ads 3421 // * Determine what inline ad related code will be orphaned and delete it 3422 function ABTestFunction(lookupResult, htmlContent) { 3423 MXT.DisableInlineAds = true; 3424 } 3425})();; 3426/* 3427 Test: AB608 3428 Created: 12/14/2022 3429 3430 NOTES: 3431 * Include this comment block in all AB Test JS files 3432 * To create AB Test: 3433 * In MxABTest, create directory named 'AB[000]' 3434 * Create / Copy 'Test_AB[000].js' in directory 'AB[000]', then copy this file and wipe ABTestFunction clean 3435 * If you need HTML, create 'Test_AB[000].html' in directory 'AB[000]' and utilize MXT.ABTestUtil.GetHtmlContent 3436 * If you are using a different IDE than VS, then make sure to include new files into the project 3437*/ 3438(function () { 3439 const testName = 'AB608'; 3440 3441 window.MXT = window.MXT || {}; 3442 MXT.ABTest = MXT.ABTest || {}; 3443 3444 // If HTML is needed 3445 MXT.ABTest[testName] = MXT.ABTestUtil.GetHtmlContent(testName, ABTestFunction); 3446 3447 // If HTML is NOT needed 3448 //MXT.ABTest[testName] = ABTestFunction; 3449 3450 function ABTestFunction(htmlContent) { 3451 // Add htmlContent after div Cancel Summary 3452 $('#divExtraFields').append(htmlContent); 3453 if (_validationInputs) { 3454 _validationInputs.domain = '#txtDomain'; 3455 } 3456 } 3457})();; 3458/* 3459 Test: AB447 3460 Created: 07-25-2019 3461 3462 NOTES: 3463 * Include this comment block in all AB Test JS files 3464 * To create AB Test: 3465 * In MxABTest, create directory named 'AB[000]' 3466 * Create / Copy 'Test_AB[000].js' in directory 'AB[000]', then copy this file and wipe ABTestFunction clean 3467 * If you need HTML, create 'Test_AB[000].html' in directory 'AB[000]' and utilize MXT.ABTestUtil.GetHtmlContent 3468 * If you are using a different IDE than VS, then make sure to include new files into the project 3469*/ 3470(function () { 3471 const testName = 'ABTEST'; 3472 3473 window.MXT = window.MXT || {}; 3474 MXT.ABTest = MXT.ABTest || {}; 3475 3476 // If HTML is needed. Make SURE to put an ID on your containing div inside the HTML 3477 MXT.ABTest[testName] = MXT.ABTestUtil.GetHtmlContent(testName, ABTestFunction); 3478 3479 // If HTML is NOT needed 3480 // MXT.ABTest[testName] = ABTestFunction; 3481 3482 function ABTestFunction(htmlContent) { 3483 // Add htmlContent to Slot1 3484 MXT.AddABTest('ClassicLookup', function (lookup) { 3485 console.log('THIS IS AN AB TEST', lookup); 3486 3487 let existingTest = $('#abtest_' + testName); 3488 3489 if (existingTest.length === 0) { 3490 let slot1 = $('.' + lookup._ui_id + ' .tool-result-header').last(); 3491 slot1.after(htmlContent); 3492 } 3493 }); 3494 3495 } 3496})();;
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.