PageSourceSearch

https://mxtoolbox.com/bundles/mxShared.js?v=62C7mZRH6NY4waNFh7r1DWyOgg9ZNUxx89I_6DwZavk1

js mxtoolbox.com collected 2026-10-01 06:49:25 UTC 123,356 bytes, 3,496 lines download raw bytes

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 + '&amp;showlogin=1&amp;hidepitch=0&amp;hidetoc=1" class="more-info-link ' + subaction.Name.toLowerCase().replace(' ', '-') + '" target="_blank" onclick="return true;"><img src="/public/images/StatusIcons/info.png" width="17">&nbsp;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 + '&amp;showlogin=1&amp;hidepitch=0&amp;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.