PageSourceSearch

https://waylandunion.org/js/core/common.js?v=7.265.42.1

js waylandunion.org collected 2026-09-24 10:16:30 UTC 361,848 bytes, 9,729 lines download raw bytes

1"use strict";
2
3function FxBSizeWizard()
4{
5    return undefined;
6}
7
8(function ($)
9{
10    FxBSizeWizard._MobileThreshold = 768;
11    FxBSizeWizard._MobileClass = "fxb_mobile";
12    FxBSizeWizard._DesktopClass = "fxb_desktop";
13    FxBSizeWizard._CurrentClass = null;
14
15    FxBSizeWizard._GetCurrentSizeClass = function () 
16    {
17        if (window.innerWidth < FxBSizeWizard._MobileThreshold)
18        {
19            return FxBSizeWizard._MobileClass;
20        }
21        return FxBSizeWizard._DesktopClass;
22    };
23
24    FxBSizeWizard._UpdateSizeClassification = function () 
25    {
26        var lNewClass = FxBSizeWizard._GetCurrentSizeClass();
27        if (lNewClass !== FxBSizeWizard._CurrentClass)
28        {
29            if (FxBSizeWizard._CurrentClass !== null)
30            {
31                $('body').removeClass(FxBSizeWizard._CurrentClass);
32            }
33            FxBSizeWizard._CurrentClass = lNewClass;
34            $('body').addClass(FxBSizeWizard._CurrentClass);
35
36            // run callbacks here?
37        }
38    };
39    
40    FxBSizeWizard._Init = function ()
41    {
42        var lLocalMobileThreshold = $("body").css("--FxB-MobileThreshold");
43        if (!FBUtilities.IsParameterUndefinedOrEmpty(lLocalMobileThreshold))
44        {
45            FxBSizeWizard._MobileThreshold = Number(lLocalMobileThreshold);
46        }
47        FxBSizeWizard._UpdateSizeClassification();
48    };
49
50    // considering DOMContentLoaded for this, but would need jQuery free code.
51    $(window).on('load', FxBSizeWizard._Init);
52    $(window).resize(FxBSizeWizard._UpdateSizeClassification);
53}(jQuery));
54
55
56// -----------------------------------------------------------------------------------------------------
57//  Foxbright CMS commonly-used functions v1.0
58//  Created: 2009-09-10 (AW)
59// -----------------------------------------------------------------------------------------------------
60
61function FBUtilities()
62{
63    return undefined;
64}
65
66function FBQuickAjax()
67{
68}
69
70function FBMenuManager()
71{
72    return undefined;
73}
74
75function ThumbnailUtilities()
76{
77    return undefined;
78}
79
80function FoxAjaxContent()
81{
82    return undefined;
83}
84
85function FoxEncoder()
86{
87    return undefined;
88}
89
90function VideoUtilities()
91{
92    return undefined;
93}
94
95function FBLightbox()
96{
97    return undefined;
98}
99
100var fb_js_debug = false;
101
102///////////////////////////////////////////////////////////////////////////////
103// FBCommon functions
104///////////////////////////////////////////////////////////////////////////////
105
106var FBCommon_1_2 =
107{
108    dumpCollection: function (name, collection)
109    {
110        var key;
111        window.console.log(name);
112        if (collection)
113        {
114            for (key in collection)
115            {
116                if (collection.hasOwnProperty(key))
117                {
118                    window.console.log("%s: %s", key, collection[key]);
119                }
120            }
121        }
122    },
123    // Try use replaceChars() instead going forward (AW: 2010-10-01)
124    // Params:
125    //   Required: words (string)
126    //   Required: character (string to remove)
127    // Return vals: words (string with character removed)
128    // Notes: Remove all instances of character from words
129    removeCharacters: function (parameters)
130    {
131        var words, character, chars, x;
132
133        words = parameters.words;
134        character = (parameters.character === undefined ? ' ' : parameters.character);
135        chars = words.length;
136
137        for (x = 0; x < chars; x += 1)
138        {
139            words = words.replace(character, "");
140        }
141        return words;
142    },
143
144    // Return true if we are in an editor page.
145    isInEditor: function ()
146    {
147        if (jQuery('body.fbcms_editmode').length === 1)
148        {
149            return true;
150        }
151        return false;
152    },
153
154
155    /**
156     * Encodes html tag characters
157     * @param {string} text
158     */
159    htmlTagEncode: function (text)
160    {
161        let encoded;
162        if (text === undefined || text === null)
163        {
164            encoded = '';
165        }
166        else
167        {
168            encoded = text.replaceAll('<', '&lt;').replaceAll('>', '&gt;');
169        }
170        return encoded;
171    },
172
173    // --[ html Encode/Decode ]-------------------------------------------------------------------------
174    // Params:
175    //    text - the text to encode/decode.
176    // Returns:
177    //    the text, encoded or decoded.
178    htmlEncode: function (text)
179    {
180        return jQuery('<div/>').text(text).html();
181    },
182
183    htmlEncodeMinimal: function (str)
184    {
185        if (str === undefined || str === null || typeof (str) !== 'string' || str.length === 0)
186        {
187            return str;
188        }
189
190        let buffer = [];
191        for (let i = 0; i < str.length; i++)
192        {
193            let iC = str[i].charCodeAt();
194            if (iC === 38)
195            {
196                buffer.push('&amp;');
197            }
198            else if (iC === 60)
199            {
200                buffer.push('&lt;');
201            }
202            else if (iC === 62)
203            {
204                buffer.push('&gt;');
205            }
206            else if (iC === 34)
207            {
208                buffer.push('&quot;');
209            }
210            // NOPE, ignore these please!
211            /*else if (iC < 65
212                || iC > 127
213                || (iC > 90
214                    && iC < 97)) {
215                buffer.push('&#');
216                buffer.push(iC);
217                buffer.push(';');
218            }*/
219            else
220            {
221                buffer.push(str[i]);
222            }
223        }
224        return buffer.join('');
225    },
226
227    htmlEncodeKludgy: function(str)
228    {       
229        if (str === undefined || str === null || typeof (str) !== 'string' || str.length)
230        {
231            return str;
232        }
233        let aRet = []
234
235        for (let i = 0; i < str.lenght; i++)
236        {
237            let iC = str[i].charCodeAt();
238            if (iC < 65
239                || iC > 127
240                || (iC > 90
241                    && iC < 97))
242            {
243                aRet.push('&#');
244                aRet.push(iC);
245                aRet.push(';');
246            }
247            else
248            {
249                aRet.push(str[i]);
250            }
251        }//endwhile
252        return aRet.join('');
253    },
254
255    htmlDecode: function (text)
256    {
257        return jQuery('<div/>').html(text).text();
258    },
259
260    PairCountrySelectWithStateSelect: function (countryCodeId, stateCodeId) 
261    {
262        const countrySelector = `#${countryCodeId}`;
263        const stateSelector = `#${stateCodeId}`;
264        function updateStateCodeSelect()
265        {
266            const selectedCountryCode = $(countrySelector).val();
267
268            $(stateSelector).find('option').each(function ()
269            {
270                const country = $(this).data('country_code');
271                if (selectedCountryCode === '' || country === '' || country === selectedCountryCode)
272                {
273                    $(this).prop('hidden', false);
274                }
275                else
276                {
277                    $(this).prop('hidden', true);
278                    if ($(this).prop('selected'))
279                    {
280                        $(this).prop('selected', false);
281                    }
282                }
283            });
284        }
285
286        $(countrySelector).on('change', updateStateCodeSelect);
287        updateStateCodeSelect.call($(countrySelector).get(0));
288    },
289
290    // Example calls:
291    //    addSearchBoxTriggers({});
292    //    addSearchBoxTriggers({ fieldId : 'myField', defaultText : 'enter search criteria' });
293    // Params:
294    //    Optional: ID of search field
295    //    Optional: Default text
296    //    Optional: CSS class for empty field
297    // Return vals: None
298    // Notes: Clear search field on focus if empty / return to default state on blur if empty
299    addSearchBoxTriggers: function (parameters)
300    {
301        var defaultText, emptyClass, fieldId;
302
303        fieldId = (parameters.fieldId === undefined) ? 'search_string' : parameters.fieldId;
304        defaultText = (parameters.defaultText === undefined) ? '' : parameters.defaultText;
305        emptyClass = (parameters.emptyClass === undefined) ? 'empty' : parameters.emptyClass;
306
307        jQuery("#" + fieldId).focus(function ()
308        {
309            jQuery(this).removeClass(emptyClass);
310            if (jQuery(this).val() === defaultText)
311            {
312                jQuery(this).val('');
313            }
314        }).blur(function ()
315        {
316            if (FBCommon_1_2.removeCharacters({ words: jQuery(this).val() }) === '')
317            {
318                jQuery(this).addClass('empty');
319                jQuery(this).val(defaultText);
320            }
321        });
322
323        // Ensure empty class is removed if page is refreshed while search f
323ield contains text
324        if (jQuery("#" + fieldId).val() !== defaultText)
325        {
326            jQuery("#" + fieldId).removeClass(emptyClass);
327        }
328    },
329
330    // Example calls:
331    //    setQuickJumpMenu({});
332    //    setQuickJumpMenu({ quickJumpSpeed : 'slow', quickJumpDelay : 300 });
333    // Params:
334    //    Optional: quickJumpID: ID of the QuickJump menu.
335    //    Optional: quickJumpButtonClass: Class of quick jump button
336    //    Optional: quickJumpDropClass:   Class of quick jump drop menu
337    //    Optional: quickJumpDelay:  milliseconds before QJD hides
338    //    Optional: quickJumpSpeed:  milliseconds QJD hide/show transition takes
339    //    Optional: quickJumpOpenClass: class to put on the overall element when the menu is open
340    // Return vals: none
341    // Notes: Adds show/hide triggers to quick jump menu
342    // Requires: jQuery.timer, jQuery.clearTimer
343    setQuickJumpMenu: function (parameters)
344    {
345        var defaults, options, quickJumpTimer, quickJumpButton, quickJumpDrop;
346
347        defaults =
348        {
349            quickJumpID: 'myQuickJump'
350            , quickJumpButtonClass: 'quickJumpButton'
351            , quickJumpDropClass: 'quickJumpDrop'
352            , quickJumpDelay: 300
353            , quickJumpSpeed: 0
354            , quickJumpClickActive: true
355            , quickJumpHoverActive: false
356            , quickJumpOpenClass: 'quickJumpOpen'
357        };
358
359        options = jQuery.extend(
360            defaults,
361            parameters
362        );
363
364        quickJumpTimer = null;
365        quickJumpButton = jQuery("#" + options.quickJumpID + ' .' + options.quickJumpButtonClass);
366        quickJumpDrop = jQuery("#" + options.quickJumpID + ' .' + options.quickJumpDropClass);
367
368
369        function openQuickJumpMenu()
370        {
371            quickJumpDrop.slideDown(options.quickJumpSpeed);
372            if (!FBUtilities.IsParameterUndefinedOrEmpty(options.quickJumpOpenClass))
373            {
374                jQuery("#" + options.quickJumpID).addClass(options.quickJumpOpenClass);
375            }
376        }//end openQuickJumpMenu
377
378        function closeQuickJumpMenu()
379        {
380            quickJumpDrop.slideUp(options.quickJumpSpeed);
381            if (!FBUtilities.IsParameterUndefinedOrEmpty(options.quickJumpOpenClass))
382            {
383                jQuery("#" + options.quickJumpID).removeClass(options.quickJumpOpenClass);
384            }
385        }//end closeQuickJumpMenu
386
387        if (quickJumpButton.length === 0 || quickJumpDrop.length === 0)
388        {
389            return;
390        }
391
392        if (options.quickJumpClickActive === true)
393        {
394            quickJumpButton.click(
395                function ()
396                {
397                    if (quickJumpDrop.is(':hidden'))
398                    {
399                        openQuickJumpMenu();
400                    }
401                    else
402                    {
403                        closeQuickJumpMenu();
404                    }
405                }
406            );
407        }
408
409        quickJumpButton.hover(
410            function ()
411            {
412                if (options.quickJumpHoverActive === true)
413                {
414                    if (quickJumpDrop.is(':hidden'))
415                    {
416                        jQuery('.' + options.quickJumpDropClass).slideUp(options.quickJumpSpeed);
417                        openQuickJumpMenu();
418                    }
419                }
420
421                if (quickJumpTimer !== null)
422                {
423                    jQuery.clearTimer(quickJumpTimer);
424                }
425            },
426            function ()
427            {
428                quickJumpTimer = jQuery.timer(
429                    options.quickJumpDelay,
430                    function ()
431                    {
432                        closeQuickJumpMenu();
433                    }
434                );
435            }
436        );
437
438        quickJumpDrop.hover(
439            function ()
440            {
441                if (quickJumpTimer !== null)
442                {
443                    jQuery.clearTimer(quickJumpTimer);
444                }
445            },
446            function ()
447            {
448                quickJumpTimer = jQuery.timer(
449                    options.quickJumpDelay,
450                    function ()
451                    {
452                        closeQuickJumpMenu();
453                    }
454                );
455            }
456        );
457
458
459
460    }, //end setQuickJumpMenu
461
462    initCmsAlert: function (aParentId, aChildClass)
463    {
464        const $alert = $(`#${aParentId}`);
465        const noAlertToShow = $alert.find(`.${aChildClass}`).length === 0;
466        if (noAlertToShow) {
467            return;
468        }
469
470        function fnHideCmsAlert() {
471            $alert.attr('aria-hidden', "true").hide();
472
473            jQuery('#fbcms_ea_shadowBox').remove(); // close trigger
474            var jFirst = jQuery('.skip_link').first();
475            if (jFirst.length === 0) {
476                jFirst = jQuery(':tabbable').first();
477            }
478            jFirst.focus();
479            return false;
480        };
481
482        jQuery('body')
483            .prepend('<div id="fbcms_ea_shadowBox"></div>');
484
485        jQuery('#fbcms_ea_shadowBox')
486            .height(jQuery(document).height() + 'px')
487            .click(fnHideCmsAlert);
488
489        const labbelledById = `${aParentId}-header`;
490        $alert.find('.fbcms_contentheader .contentheader_item.fbcms_news_headlines_title').attr('id', labbelledById);
491
492        const describedById = `${aParentId}-content`;
493        $alert.find('.fbcms_headline_list_content ').attr('id', describedById);
494
495        $alert
496            .prepend(`<button type="button" title="Close alert" aria-labelledby="closeAlert-label" id="closeAlert"><i class="fas fa-times" aria-hidden="true"></i> <span id="closeAlert-label" class="visually-hidden">Close alert</span></button>`)
497            .attr('role', 'alertdialog')
498            .attr('aria-labelledby', labbelledById)
499            .attr('aria-describedby', describedById)
500            .attr('aria-hidden', "false")
501            .show();
502
503        $alert
504            .find('#closeAlert')
505            .on('click', fnHideCmsAlert)
506            .on('keypress', function (event) {
507                if (event.keyCode === 13 /*enter*/ || event.keyCode === 27 /*escape*/) {
508                    event.preventDefault();
509                    fnHideCmsAlert();
510                }
511            })
512            .focus();
513
514
515        let tababble = $alert.find(':tabbable');
516        tababble.first().on('keydown', function (event) {
517            if (event.keyCode === 9 && event.shiftKey === true) {
518                event.preventDefault();
519                $alert.find(':tabbable').last().focus();
520            }
521
522        });
523
524        tababble.last().on('keydown', function (event) {
525            if (event.keyCode === 9 && event.shiftKey === false) {
526                event.preventDefault();
527                $alert.find(':tabbable').first().focus();
528            }
529
530        });
531    },
532
533    /*
534        FBCommon_1_2.initTabs();
535    
536        Initializes tab events (and cleanup).
537        A Tabset is identified by .fbcms_tabset
538        Two containers must be found inside, for the titles (.tabset_titlecontainer) and content (.tabset_contentcontainer)
539        If only one title (.tabset_title) is found, nothing further is done.
540        Active titles and content are indicated by the active class (.active).  
541            This should be set in the markup on the desired set to be initially displayed.
542        Each title must have data-tabselector attribute set with a selector for the matching content element inside the content container.
543        Content and Tab elments must have id attributes set.
544        If two or more are titles are present:
545            Each title is checked if it is empty of Text (not markup).
546            If blank or otherwise empty, the title and matching content elements are removed - unless Active (see extra options).
547            If text is present, the title and content elements are labelled with ARIA attributes.
548        
549        Extra options:
550            Clear All Empty (default: false) This will cause a title/content element to be removed EVEN IF IT IS ACTIVE. 
551                This can be specified by a data attribute on the tabset container: data-tabset-clearallempty
552                Setting this to 'true' will activate this setting, all other values will result in the default behavior.
553        
554        Unprocessed, initial markup example:
555        
556        <div class="fbcms_tabset" data-tabset-clearallempty="false">
557            <ul class="tabset_titlecontainer" role="tablist">
558                <li id="tabtitle1" class="tabset_title active" data-tab-selector="#tab1" role="tab">
559                </li><!-- .tabset_title //-->
560                <li id="tabtitle2" class="tabset_title" data-tab-selector="#tab2" role="tab">
561                </li><!-- .tabset_title //-->
562            </ul><!-- .tabset_titlecontainer //-->
563            <div class="tabset_contentcontainer">
564                <div id="tab1" class="tabset_content active" role="tabpanel">
565                </div><!-- .tabset_content //-->
566                <div id="tab2" class="tabset_content" role="tabpanel">
567                </div><!-- .tabset_content //-->
568            </div><!-- .tabset_contentcontainer //-->
569        </div>
570    */
571    initTabs: function ()
572    {
573        function activateTabSetTitle(aTarget, aCleanAnchorHash) 
574        {
575            const jCurrentTab = $(aTarget).closest('.tabset_title');
576            if (jCurrentTab.hasClass('active'))
577            {
578                return;
579            }
580
581            const contentSelector = jCurrentTab.data('tab-selector');
582            if (contentSelector === undefined || contentSelector === null || contentSelector.length === 0)
583            {
584                return;
585            }
586            
587            const jTabSet = jCurrentTab.closest('.fbcms_tabset');
588            const jContentContainers = jTabSet.find('.tabset_contentcontainer');            
589            const jContent = jContentContainers.find(contentSelector);
590            if (jContent.length === 0)
591            {
592                return;
593            }
594
595            const jTitleContainers = jTabSet.find('.tabset_titlecontainer');
596
597            jTitleContainers.find('.tabset_title').removeClass('active').attr('aria-selected', 'false');
598            jContentContainers.find('.tabset_content').removeClass('active');
599            jCurrentTab.addClass('active').attr('aria-selected', 'true');
600            jContent.addClass('active');
601
602            if (!FBUtilities.IsParameterUndefinedOrEmpty(aCleanAnchorHash))
603            {
604                // was given an anchor, use it.
605                window.location.hash = `#${aCleanAnchorHash}`;
606            }
607            else if (!FBUtilities.IsParameterUndefinedOrEmpty(window.location.hash))
608            {   
609                // is the current hash inside this hashset? in active content? in t
609itles?
610                const inTabset = jTabSet.find(window.location.hash).length !== 0
611                const inActiveContent = jContent.find(window.location.hash).length !== 0;
612                const inTitles = jTitleContainers.find(window.location.hash).length !== 0;
613
614                if (inTabset && !inActiveContent && !inTitles)
615                {
616                    const cleanedUrl = window.location.href.split('#')[0];
617                    window.history.replaceState(null, null, cleanedUrl);
618                }
619            }
620
621            const cleanAnchorHash = !FBUtilities.IsParameterUndefinedOrEmpty(aCleanAnchorHash) ? aCleanAnchorHash : undefined;
622            setTimeout(function ()
623            {
624                if (cleanAnchorHash !== undefined)
625                {
626                    // was given an anchor, use it.
627                    const cleanedUrl = window.location.href.split('#')[0];
628                    window.history.replaceState(null, null, cleanedUrl);
629                    window.location.hash = `#${cleanAnchorHash}`;
630                }
631
632                const masons = jContent.find('.masonry');
633                if (masons.length !== 0 && masons.masonry !== undefined)
634                {
635                    masons.masonry("layout");
636                }
637            }, 1);
638        }
639
640        const tabSets = $('.fbcms_tabset');
641        if (tabSets.length === 0)
642        {
643            return;
644        }
645
646        const isEditMode = FBCommon_1_2.isInEditor();
647
648        tabSets.each(function ()
649        {
650            const $tabSet = $(this);
651            const $titleContainers = $tabSet.find('.tabset_titlecontainer');
652            const $contentContainers = $tabSet.find('.tabset_contentcontainer');
653
654            if ($titleContainers.length > 0 && $contentContainers.length > 0)
655            {
656                const clearAllEmpty = $tabSet.data('tabset-clearallempty');
657                const keepEmpty = $tabSet.data('tabset-keepempty');
658                const $titles = $titleContainers.find('.tabset_title');
659                if ($titles.length < 2)
660                {
661                    $titleContainers.on('click', '.tabset_title', function (aEvent) {
662                        aEvent.preventDefault();
663                        return false;
664                    });
665
666                    if ($tabSet.find('.tabset_title').length === 0) {
667                        $tabSet.remove();
668                    }
669                    return;
670                }
671
672                if (isEditMode === false)
673                {
674                    $.each($titles, function (aIndex, aValue)//ignore jslint
675                    {
676                        const $title = $(aValue);
677                        const isActive = $title.hasClass('active');
678                        const contentSelector = $title.data('tab-selector');
679
680                        if (FBUtilities.IsParameterUndefinedOrEmpty(contentSelector))
681                        {
682                            return;
683                        }
684
685                        const $content = $contentContainers.find(contentSelector);
686                        if ($content.length === 0)
687                        {
688                            return;
689                        }
690
691                        if (keepEmpty !== true
692                            && FBUtilities.IsStringEmptyOrWhitespace($title.text()) === true
693                            && (isActive === false || clearAllEmpty === true)
694                        )
695                        {
696                            $title.remove();
697                            $content.remove();
698                            if ($tabSet.find('.tabset_title').length === 0) {
699                                $tabSet.remove();
700                            }
701                            return;
702                        }
703
704                        const tabId = $title.attr('id');
705                        const contentId = $content.attr('id');
706                        if (FBUtilities.IsStringEmptyOrWhitespace(tabId) === true
707                            || FBUtilities.IsStringEmptyOrWhitespace(contentId) === true)
708                        {
709                            return;
710                        }
711                        $title.attr('aria-controls', contentId);
712                        $title.attr('aria-selected', isActive);
713                        $content.attr('aria-labelledby', tabId);
714                    });
715                }
716
717                $titleContainers.on('click', '.tabset_title', (event) => 
718                {
719                    event.preventDefault();
720                    const tabSet = event.target;
721                    activateTabSetTitle(tabSet);
722                    return false;
723                });
724            }
725
726            window.addEventListener("hashchange", (event) =>
727            {
728                selectHashRequestedTab($titleContainers);
729            });
730
731            selectHashRequestedTab($titleContainers);
732        });
733
734        function selectHashRequestedTab(aTabWrapper)
735        {
736            const rawAnchorHash = window.location.hash;
737            if (rawAnchorHash.lastIndexOf('#') === -1)
738            {
739                return;
740            }
741
742            const cleanAnchorHash = rawAnchorHash.substring(rawAnchorHash.lastIndexOf('#') + 1);
743            const jDirect = aTabWrapper.find(`.tabset_title[data-tab-selector="${rawAnchorHash}"]`);
744            if (jDirect.length !== 0)
745            {
746                if (jDirect.length === 1)
747                {
748                    activateTabSetTitle(jDirect, cleanAnchorHash);
749                }
750                return
751            }
752
753            const jTarget = $(`#${cleanAnchorHash}`);
754            if (jTarget.length !== 1)
755            {
756                return;
757            }
758
759            const jTabsetContent = jTarget.closest('.tabset_content');
760            if (jTabsetContent.length !== 1)
761            {
762                return;
763            }
764
765            const parentTabContentId = jTabsetContent.attr('id');
766            const jTitlesToActivate = aTabWrapper.find('.tabset_title[data-tab-selector="#' + parentTabContentId + '"]')
767            if (jTitlesToActivate.length !== 1)
768            {
769                return;
770            }
771
772            activateTabSetTitle(jTitlesToActivate, cleanAnchorHash);
773        }//end selectHashRequestedTab
774    },//end initTabs
775
776    getJQueryDialogButton: function (dialog_selector, button_name)
777    {
778        var buttons = $(dialog_selector + ' .ui-dialog-buttonpane button');
779        for (var i = 0; i < buttons.length; ++i)
780        {
781            var jButton = $(buttons[i]);
782            if (jButton.text() === button_name)
783            {
784                return jButton;
785            }
786        }
787
788        return null;
789    },
790
791    disableJQueryDialogButton: function (dialog_selector, button_name)
792    {
793        var button = FBCommon_1_2.getJQueryDialogButton(dialog_selector, button_name);
794        if (button !== null)
795        {
796            button.attr('disabled', 'disabled').addClass('ui-state-disabled');
797        }
798    },
799
800    enableJQueryDialogButton: function (dialog_selector, button_name)
801    {
802        var button = FBCommon_1_2.getJQueryDialogButton(dialog_selector, button_name);
803        if (button !== null)
804        {
805            button.removeAttr('disabled').removeClass('ui-state-disabled');
806        }
807    }
808};
809
810///////////////////////////////////////////////////////////////////////////////
811// FBCommon_1_2 Function Aliases
812///////////////////////////////////////////////////////////////////////////////
813
814var isInEditor = FBCommon_1_2.isInEditor;
815var PairCountrySelectWithStateSelect = FBCommon_1_2.PairCountrySelectWithStateSelect;
816var addSearchBoxTriggers = FBCommon_1_2.addSearchBoxTriggers; //clarenceville & vansingel(!)
817var setQuickJumpMenu = FBCommon_1_2.setQuickJumpMenu; //fruitport2015 & mioausable
818
819
820///////////////////////////////////////////////////////////////////////////////
821// jQuery Timer Plugin
822///////////////////////////////////////////////////////////////////////////////
823
824(function ()
825{
826    /*
827    * jQuery Timer Plugin
828    * http://www.evanbot.com/article/jquery-timer-plugin/23
829    *
830    * @version      1.0
831    * @copyright    2009 Evan Byrne (http://www.evanbot.com)
832    */
833
834    jQuery.timer = function (time, func, callback)
835    {
836        var a = {
837            timer: setTimeout(func, time),
838            callback: null
839        };
840
841        if (typeof callback === 'function')
842        {
843            a.callback = callback;
844        }
845        return a;
846    };
847
848    jQuery.clearTimer = function (a)
849    {
850        if (a === null)
851        {
852            return this;
853        }
854        clearTimeout(a.timer);
855        if (typeof (a.callback) === 'function')
856        {
857            a.callback();
858        }
859        return this;
860    };
861}(jQuery));
862
863///////////////////////////////////////////////////////////////////////////////
864// A real string replaceAll function
865///////////////////////////////////////////////////////////////////////////////
866
867//(function ()
868//{
869//    // http://www.codedigest.com/CodeDigest/75-String-ReplaceAll-function-in-JavaScript.aspx
870//    String.prototype.replaceAll = function (stringToFind, stringToReplace)
871//    {
872//        var temp, index;
873
874//        temp = this.toString();
875//        index = temp.indexOf(stringToFind);
876
877//        while (index !== -1)
878//        {
879//            temp = temp.replace(stringToFind, stringToReplace);
880//            index = temp.indexOf(stringToFind);
881//        }
882//        return temp;
883//    };
884//}(jQuery));
885
886///////////////////////////////////////////////////////////////////////////////
887// FoxEncoder
888///////////////////////////////////////////////////////////////////////////////
889
890(function ()
891{
892    //----------------------------------------------------------------------------
893    //  FoxEncoder Javascript functions.
894    //  http://ntt.cc/2008/01/19/base64-encoder-decoder-with-javascript.html
895    //----------------------------------------------------------------------------
896
897    FoxEncoder.keyStr64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
898    FoxEncoder.base64RegexTest = /[^A-Za-z0-9\+\/\=]/g;
899
900    FoxEncoder.encodeUTF8andBase64 = function (input)
901    {
902        return FoxEncoder.encodeUTF8andBase64worker(input, FoxEncoder.keyStr64);
903    };
904
905    FoxEncoder.encodeUTF8andBase64worker = function (input, keyStr64)
906    {
907        var output = "",
908            chr1,
909            chr2,
910            chr3 = "",
911            enc1,
912            enc2,
913            enc3,
914            enc4 = "",
915            i = 0;
916
917        if (FBUtilities.IsParameterUndefinedOrEmpty(input))
918        {
919            return output;
920        }
921
922        input = FoxEncoder.encodeUTF8(input);
923
924        do
925        {
926            chr1 = input.charCodeAt(i);
927            i += 1;
928            chr2 = input.charCodeAt(i);
929            i += 1;
930            chr3 = input.charCodeAt(i);
931            i += 1;
932
933            enc1 = chr1 >> 2; //ignore jslint
934            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); //ignore jslint
935            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); //ignore jslint
936            enc4 = chr3 & 63; //ignore jslint
937
938            if (isNaN(chr2))
939            {
940                enc3 = enc4 = 64;
941            }
942            else if (isNaN(chr3))
943            {
944                enc4 = 64;
945            }
946
947            output = output +
948                keyStr64.charAt(enc1) +
949                keyStr64.charAt(enc2) +
950                keyStr64.charAt(enc3) +
951                keyStr64.charAt(enc4);
952            chr1 = chr2 = chr3 = "";
953            enc1 = enc2 = enc3 = enc4 = "";
954        }
955        while (i < input.length);
956
957        return output;
958    }; //end FoxEncoder.encodeUTF8andBase64worker
959
960    FoxEncoder.decodeBase64andUTF8 = function (input)
961    {
962        return FoxEncoder.decodeBase64andUTF8worker(input, FoxEncoder.keyStr64, FoxEncoder.base64RegexTest);
963    };
964
965    FoxEncoder.decodeBase64andUTF8worker = function (input, keyStr64, base64RegexTest)
966    {
967        var output = "",
968            chr1, chr2, chr3 = "",
969            enc1, enc2, enc3, enc4 = "",
970            i = 0;
971
972        // remove all characters that are not in the key string.
973        if (base64RegexTest.exec(input))
974        {
975            return null;
976        }
977        input = input.replace(base64RegexTest, "");
978
979        do
980        {
981            enc1 = keyStr64.indexOf(input.charAt(i));
982            i += 1;
983            enc2 = keyStr64.indexOf(input.charAt(i));
984            i += 1;
985            enc3 = keyStr64.indexOf(input.charAt(i));
986            i += 1;
987            enc4 = keyStr64.indexOf(input.charAt(i));
988            i += 1;
989
990            chr1 = (enc1 << 2) | (enc2 >> 4); //ignore jslint
991            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); //ignore jslint
992            chr3 = ((enc3 & 3) << 6) | enc4; //ignore jslint
993
994            output = output + String.fromCharCode(chr1);
995
996            if (enc3 !== 64)
997            {
998                output = output + String.fromCharCode(chr2);
999            }
1000            if (enc4 !== 64)
1001            {
1002                output = output + String.fromCharCode(chr3);
1003            }
1004
1005            chr1 = chr2 = chr3 = "";
1006            enc1 = enc2 = enc3 = enc4 = "";
1007        }
1008        while (i < input.length);
1009
1010        output = FoxEncoder.decodeUTF8(output);
1011        return output;
1012    }; //end FoxEncoder.decodeBase64andUTF8
1013
1014    // private method for UTF-8 encoding
1015    FoxEncoder.encodeUTF8 = function (string) 
1016    {
1017        var utftext = "",
1018            n,
1019            c;
1020
1021        string = string.replace(/\r\n/g, "\n");
1022
1023
1024        for (n = 0; n < string.length; n += 1)
1025        {
1026
1027            c = string.charCodeAt(n);
1028
1029            if (c < 128)
1030            {
1031                utftext += String.fromCharCode(c);
1032            }
1033            else if ((c > 127) && (c < 2048))
1034            {
1035                utftext += String.fromCharCode((c >> 6) | 192); //ignore jslint
1036                utftext += String.fromCharCode((c & 63) | 128);  //ignore jslint
1037            }
1038            else
1039            {
1040                utftext += String.fromCharCode((c >> 12) | 224); //ignore jslint
1041                utftext += String.fromCharCode(((c >> 6) & 63) | 128);  //ignore jslint
1042                utftext += String.fromCharCode((c & 63) | 128);  //ignore jslint
1043            }
1044
1045        }
1046
1047        return utftext;
1048    };
1049
1050    // private method for UTF-8 decoding
1051    FoxEncoder.decodeUTF8 = function (utftext)
1052    {
1053        var string = "",
1054            i = 0,
1055            c = 0,
1056            c2 = 0,
1057            c3 = 0;
1058
1059        while (i < utftext.length)
1060        {
1061
1062            c = utftext.charCodeAt(i);
1063
1064            if (c < 128)
1065            {
1066                string += String.fromCharCode(c);
1067                i += 1;
1068            }
1069            else if ((c > 191) && (c < 224))
1070            {
1071                c2 = utftext.charCodeAt(i + 1);
1072                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); //ignore jslint
1073                i += 2;
1074            }
1075            else
1076            {
1077                c2 = utftext.charCodeAt(i + 1);
1078                c3 = utftext.charCodeAt(i + 2);
1079                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); //ignore jslint
1080                i += 3;
1081            }
1082
1083        }
1084
1085        return string;
1086    };
1087}(jQuery));
1088
1089///////////////////////////////////////////////////////////////////////////////
1090// FoxAjaxContent
1091///////////////////////////////////////////////////////////////////////////////
1092
1093(function ()
1094{
1095    // "Static" Variables
1096    //////////////////////////////////////////////////////////////////////////////////////////////////
1097    //   Variables defined here are used by all fox ajax content instances on the page.
1098
1099    // class of the div wrapper
1100    FoxAjaxContent.loading_class = "fb_ajax_loading";
1101    FoxAjaxContent.content_class = "fb_ajax_content";
1102
1103    FoxAjaxContent.support_animate = true;
1104
1105    //////////////////////////////////////////////////////////////////////////////////////////////////
1106
1107
1108    // "Static" Methods
1109    //////////////////////////////////////////////////////////////////////////////////////////////////
1110    //   Functions defined here are used by all fox ajax content instances on the page.
1111
1112    FoxAjaxContent.createFoxAjaxContent = function (guid, pagecontent_id, fullpath, oncompletecallback)
1113    {
1114        if (guid === undefined)
1115        {
1116            FBUtilities.LogMessage("FoxAjaxContent: creation error - guid undefined");
1117            return null;
1118        }
1119
1120        if (pagecontent_id === undefined)
1121        {
1122            FBUtilities.LogMessage("FoxAjaxContent: creation error - pagecontent_id undefined");
1123            return null;
1124        }
1125
1126        if (fullpath === undefined)
1127        {
1128            FBUtilities.LogMessage("FoxAjaxContent: creation error - fullpath undefined");
1129            return null;
1130        }
1131
1132        if ('v' == '\v' /*jQuery.browser.msie*/) //ignore jslint
1133        {
1134            FoxAjaxContent.support_animate = false;
1135        }
1136
1137        var my_content = new FoxAjaxContent();
1138        my_content.guid = guid;
1139        my_content.pagecontent_id = pagecontent_id;
1140        my_content.fullpath = fullpath;
1141        if (oncompletecallback !== undefined)
1142        {
1143            if (oncompletecallback !== undefined && typeof oncompletecallback === "function")
1144            {
1145                my_content.oncompletecallback = oncompletecallback;
1146            }
1147        }
1148        return my_content;
1149    };
1150
1151
1152    // "Object" Variables
1153    //////////////////////////////////////////////////////////////////////////////////////////////////
1154    //    Variables defined here are used as unique copies in each instance of fox ajax content.
1155    //    they are defined with these defaults, and may be modified after being instanciated
1156    //    without effecting others.
1157
1158
1159    // id of the div where content is to be placed.
1160    FoxAjaxContent.prototype.guid = null;
1161    // name of the section which the contains the content.
1162    FoxAjaxContent.prototype.pagecontent_id = null;
1163    // url where the section is found (usually the current url)
1164    FoxAjaxContent.prototype.fullpath = null;
1165
1166
1167    //////////////////////////////////////////////////////////////////////////////////////////////////
1168
1169    // Loads the requested content into the area, sending along requested parameters, if any.
1170    FoxAjaxContent.prototype.loadRequestedContent = function (params) 
1171    {
1172        var lContentLoader, content_selector, loading_selector, fullpath, data, prop;
1173
1174        lContentLoader = this;
1175        content_selector = '#' + lContentLoader.guid + ' .' + FoxAjaxContent.content_class;
1176        loading_selector = '#' + lContentLoader.guid + ' .' + FoxAjaxContent.loading_class;
1177
1178        // animate fade out.
1179        if (FoxAjaxContent.support_animate)
1180        {
1181            jQuery(content_selector).animate(
1182                { opacity: 0.4 },
1183                { duration: 250 });
1184        }
1185        jQuery(loading_selector).show(250);
1186
1187        fullpath = lContentLoader.fullpath;
1188        data = {
1189            fco: '1',
1190            pcid: lContentLoader.pagecontent_id
1191        };
1192
1193        if (params !== undefined)
1194        {
1195            if (typeof params === "object")
1196            {
1197                for (prop in params)
1198                {
1199                    if (params.hasOwnProperty(prop))
1200                    {
1201                        data[prop] = params[prop];
1202                    }
1203                } //endfor
1204            }
1205            //else if ( typeof ( params ) === "function" )
1206            //{
1207            //    // function
1208            //}
1209            //else
1210            //{
1211            //    // number, string, or boolean
1212            //}
1213        }
1214        jQuery(content_selector).load(fullpath, data, function ()
1215        {
1216            FBUtilities.initializeLoadables($(content_selector).get(0));
1217            if (lContentLoader.oncompletecallback !== undefined)
1218            {
1219                if (typeof (lContentLoader.oncompletecallback) === "function")
1220                {
1221                    lContentLoader.oncompletecallback(content_selector);
1222                }
1223            }
1224            // animate fade back
1225            if (FoxAjaxContent.support_animate)
1226            {
1227                jQuery(content_selector).animate(
1228                    { opacity: 1.0 },
1229                    { duration: 250 });
1230            }
1231            jQuery(loading_selector).hide(250);
1232        });
1233    };
1234}(jQuery));
1235
1236///////////////////////////////////////////////////////////////////////////////
1237// ThumbnailUtilities
1238///////////////////////////////////////////////////////////////////////////////
1239
1240(function ($)
1241{
1242    ThumbnailUtilities.RandomParamName = 'r';
1243    ThumbnailUtilities.MaxWidthParamName = 'w';
1244    ThumbnailUtilities.MaxHeightParamName = 'h';
1245    ThumbnailUtilities.CropperOptionParamName = 'c';
1246    ThumbnailUtilities.FullCropperOptionValue = 'f';
1247    ThumbnailUtilities.ThumbnailCropperOptionValue = 't';
1248
1249    ThumbnailUtilities.GetThumbnailUrl = function (aParameters)
1250    {
1251
1252        if (FBUtilities.IsParameterUndefinedOrEmpty(aParameters)) 
1253        {
1254            FBUtilities.LogMessage('ThumbnailUtilities: Null or empty parameters sent.');
1255            return;
1256        }
1257
1258        var lDefaults, lParameters, lUrl, lThumbnailParams;
1259
1260        lDefaults = {
1261            Url: null
1262            , MaxWidth: null
1263            , MaxHeight: null
1264            , AddRandomParam: false
1265        };
1266
1267        lParameters = $.extend(lDefaults, aParameters);
1268
1269        if (FBUtilities.IsParameterUndefinedOrEmpty(lParameters.Url)) 
1270        {
1271            FBUtilities.LogMessage('ThumbnailUtilities: Url not set.');
1272            return;
1273        }
1274
1275        lUrl = lParameters.Url;
1276        lThumbnailParams = {};
1277        lThumbnailParams[ThumbnailUtilities.CropperOptionParamName] = ThumbnailUtilities.ThumbnailCropperOptionValue;
1278
1279        if (!FBUtilities.IsParameterUndefinedOrEmpty(lParameters.MaxWidth)) 
1280        {
1281            lThumbnailParams[ThumbnailUtilities.MaxWidthParamName] = lParameters.MaxWidth;
1282        }
1283
1284        if (!FBUtilities.IsParameterUndefinedOrEmpty(lParameters.MaxHeight)) 
1285        {
1286            lThumbnailParams[ThumbnailUtilities.MaxHeightParamName] = lParameters.MaxHeight;
1287        }
1288
1289        if (lParameters.AddRandomParam === true)
1290        {
1291            lThumbnailParams[ThumbnailUtilities.RandomParamName] = Math.random();
1292        }
1293
1294        return FBUtilities.BuildGetUrl(lUrl, lThumbnailParams);
1295    }; //end GetThumbnailUrl
1296
1297}(jQuery));
1298
1299///////////////////////////////////////////////////////////////////////////////
1300// FBUtilities
1301///////////////////////////////////////////////////////////////////////////////
1302
1303(function ($)
1304{
1305    FBUtilities.DebugMode = false;
1306
1307    let allowedTldArray = null;
1308    const defaultAllowedTlds = 'com,org,net,edu,gov,mil,us,academy,education,school';
1309
1310    FBUtilities.GetAllowedTLDs = function ()
1311    {        
1312        if (allowedTldArray === null)
1313        {
1314            const allowed = FBUtilities.IsStringEmptyOrWhitespace(cmsAllowedEmailTlds) ? defaultAllowedTlds : cmsAllowedEmailTlds;
1315            let items = allowed.split(/[\s,;]+/)
1316            let update = [];
1317            for (let i = 0; i < items.length; i++)
1318            {
1319                let value = items[i];
1320                if (value !== undefined && value !== null && (typeof value === 'string') && value.length !== 0)
1321                {
1322                    update.push(value);
1323                }
1324            }
1325
1326            allowedTldArray = update;
1327        }
1328
1329        return allowedTldArray;
1330    };
1331
1332    FBUtilities.IsParameterUndefined = function (aParameter)
1333    {
1334        if (aParameter === undefined)
1335        {
1336            return true;
1337        }
1338        return false;
1339    };//end IsParameterUndefined
1340
1341    FBUtilities.IsParameterUndefinedOrNull = function (aParameter)
1342    {
1343        return (aParameter === undefined || aParameter === null);
1344    };//end IsParameterUndefinedOrNull
1345
1346    FBUtilities.IsParameterUndefinedOrEmpty = function (aParameter)
1347    {
1348        return (aParameter === undefined || aParameter === null || aParameter.length === 0);
1349    };//end IsParameterUndefinedOrEmpty
1350
1351    FBUtilities.IsStringEmptyOrWhitespace = function (aParameter)
1352    {
1353        return (aParameter === undefined || aParameter === null || aParameter.length === 0 || $.trim(aParameter) === '');
1354    };//end IsStringEmptyOrWhitespace
1355
1356    FBUtilities.AreStringsEqual = function (aFirst, aSecond, aCaseInsensitive)
1357    {
1358        if (typeof aFirst !== "string")
1359        {
1360            return false;
1361        }
1362
1363        if (typeof aSecond !== "string")
1364        {
1365            return false;
1366        }
1367
1368        let first = (aCaseInsensitive === true) ? aFirst.toLowerCase() : aFirst;
1369        let second = (aSecond === true) ? aFirst.toLowerCase() : aSecond;
1370
1371        return first === second;
1372    };
1373
1374
1375    FBUtilities.GetIntOrNull = function (aParameter)
1376    {
1377        if (FBUtilities.IsParameterUndefinedOrEmpty(aParameter))
1378        {
1379            return null;
1380        }
1381
1382        var lInt = parseInt(aParameter, 10);
1383        if (isNaN(lInt))
1384        {
1385            return null;
1386        }
1387
1388        return lInt;
1389    };//end GetIntOrNull
1390
1391    FBUtilities.GetBoolOrNull = function (aParameter)
1392    {
1393        if (FBUtilities.IsParameterUndefinedOrEmpty(aParameter))
1394        {
1395            return null;
1396        }
1397
1398        var lInput = aParameter;
1399        if (typeof lInput === 'string')
1400        {
1401            lInput = lInput.toLowerCase();
1402        }
1403        switch (lInput)
1404        {
1405            case true:
1406            case "true":
1407            case 1:
1408            case "1":
1409            case "on":
1410            case "yes":
1411                return true;
1412            case false:
1413            case "false":
1414            case 0:
1415            case "0":
1416            case "off":
1417            case "no":
1418                return false;
1419            default:
1420                return null;
1421        }//endswitch
1422    }; //end GetBoolOrNull
1423
1424    FBUtilities.GetCallbackOrNull = function (aCallbacks, aName)
1425    {
1426        if (FBUtilities.IsParameterUndefinedOrEmpty(aCallbacks))
1427        {
1428            return null;
1429        }
1430
1431        if (FBUtilities.IsParameterUndefinedOrNull(aCallbacks[aName]))
1432        {
1433            return null;
1434        }
1435        return aCallbacks[aName];
1436    };//end GetCallbackOrNull
1437
1438    FBUtilities.HasCallback = function (aCallbacks, aName)
1439    {
1440        var lCallback = FBUtilities.GetCallbackOrNull(aCallbacks, aName);
1441        if (lCallback === null)
1442        {
1443            return false;
1444        }
1445        return true;
1446    };//end HasCallback
1447
1448    FBUtilities.RunCallback = function (aCallbacks, aName)
1449    {
1450        var lIndex, lCallback, lPassOnArgs;
1451
1452        FBUtilities.LogMessage('RunCallback - "' + aName + '"');
1453
1454        lCallback = FBUtilities.GetCallbackOrNull(aCallbacks, aName);
1455        if (lCallback === null)
1456        {
1457            FBUtilities.LogMessage('RunCallback - "' + aName + '" - Not Found');
1458            return;
1459        }
1460
1461        if (arguments.length > 2)
1462        {
1463            lPassOnArgs = [];
1464            for (lIndex = 2; (lIndex < arguments.length); lIndex += 1)
1465            {
1466                lPassOnArgs.push(arguments[lIndex]);
1467            }
1468            lCallback.apply(this, lPassOnArgs);
1469        }
1470        else
1471        {
1472            lCallback();
1473        }
1474    };//end RunCallback
1475
1476    // DEPRECATED -- TODO REEVALUATE KEEPING THIS. //
1477    FBUtilities.YesNoValue = function (aElement) 
1478    {
1479        if (FBUtilities.IsParameterUndefinedOrNull(aElement))
1480        {
1481            return;
1482        }
1483
1484        var lId, lIsRadio;
1485
1486        lId = aElement.attr('id');
1487        lIsRadio = aElement.hasClass('yesnoradio');
1488
1489        if (FBUtilities.IsParameterUndefinedOrEmpty(lId))
1490        {
1491            return;
1492        }
1493
1494        if (lIsRadio)
1495        {
1496            if (aElement.find('#' + lId + '_yes:checked').length === 1)
1497            {
1498                return true;
1499            }
1500            if (aElement.find('#' + lId + '_no:checked').length === 1)
1501            {
1502                return false;
1503            }
1504        }
1505
1506        return null;
1507    };//end YesNoValue
1508
1509    // Replaces an item in an array with a replacement item.
1510    //  Assumes uniqueness, i.e. only the first item will 
1511    //    be replaced.
1512    //  Array: Array to act on, changes are made in place.
1513    //           If undefined or null, returns.
1514    //  Parameters: (If undefined or null, returns.)
1515    //    Item - Item in the array to replace, if not defined 
1516    //          (or not found), the Replacement will be put 
1517    //          at the end of the array.
1518    //    Replacement - Replacement item for the array. If 
1519    //          undefined, the Item will simply be deleted.
1520    FBUtilities.ReplaceArrayItem = function (aArray, aParameters)
1521    {
1522        if (FBUtilities.IsParameterUndefinedOrNull(aArray))
1523        {
1524            return;
1525        }
1526
1527        if (FBUtilities.IsParameterUndefinedOrNull(aParameters))
1528        {
1529            return;
1530        }
1531
1532        var lDeleteCount, lStart, lIndex;
1533
1534        lDeleteCount = 0;
1535        lStart = aArray.length;
1536
1537        if (!FBUtilities.IsParameterUndefined(aParameters.Item))
1538        {
1539            lDeleteCount = 1;
1540        }
1541
1542        if (!FBUtilities.IsParameterUndefined(aParameters.Replacement))
1543        {
1544            for (lIndex = 0; lIndex < aArray.length; lIndex += 1)
1545            {
1546                if (aArray[lIndex] === aParameters.Replacement)
1547                {
1548                    lStart = lIndex;
1549                    break;
1550                }
1551            }//endfor
1552
1553            aArray.splice(lStart, lDeleteCount, aParameters.Replacement);
1554        }
1555        else
1556        {
1557            aArray.splice(lStart, lDeleteCount);
1558        }
1559    }; //end ReplaceArrayItem
1560
1561    // Converts a hash array into a string using the separators given.
1562    //  Parameters:
1563    //    aHashArray - (If undefined, null or empty, blank is returned.)
1564    //    aEntrySeparator - Separator to appear between each entry. 
1565    //      (If undefined, null, or blank, ';' is used.)
1566    //    aPairSeparator - Separator to appear between the key and value in each pair. 
1567    //      (If undefined, null, or blank, '=' is used.)
1568    //
1569    // Example:
1570    //  "key1=value1;key2=value2"
1571    FBUtilities.JoinHashArray = function (aHashArray, aEntrySeparator, aPairSeparator) 
1572    {
1573        var lResult = '',
1574            lEntrySeparator = ';',
1575            lPairSeparator = '=',
1576            lKey;
1577
1578        if (FBUtilities.IsParameterUndefinedOrEmpty(aHashArray))
1579        {
1580            return lResult;
1581        }
1582
1583        if (!FBUtilities.IsParameterUndefinedOrEmpty(aEntrySeparator))
1584        {
1585            lEntrySeparator = aEntrySeparator;
1586        }
1587
1588        if (!FBUtilities.IsParameterUndefinedOrEmpty(aPairSeparator))
1589        {
1590            lPairSeparator = aPairSeparator;
1591        }
1592
1593        for (lKey in aHashArray)
1594        {
1595            if (aHashArray.hasOwnProperty(lKey))
1596            {
1597                lResult += lKey;
1598                lResult += lPairSeparator;
1599                lResult += aHashArray[lKey];
1600                lResult += lEntrySeparator;
1601            }
1602        }//endfor
1603
1604        return lResult;
1605    };//end JoinHashArray
1606
1607    // Converts a hash array into a GET url string.
1608    //  Parameters:
1609    //    aUrl - The url the parameters are to be appended to.
1610    //      (If undefined or null it is returned. If empty or 
1611    //      full, parameters [if any] are appended)
1612    //    aParameters - GET params to be appended to the url.
1613    FBUtilities.BuildGetUrl = function (aUrl, aParameters)
1614    {
1615        var lResult, lParamString;
1616
1617        lResult = aUrl;
1618        if (FBUtilities.IsParameterUndefinedOrNull(aUrl))
1619        {
1620            FBUtilities.LogMessage('BuildGetUrl: Null or undefined url.');
1621            return lResult;
1622        }
1623
1624        if (FBUtilities.IsParameterUndefinedOrNull(aParameters))
1625        {
1626            FBUtilities.LogMessage('BuildGetUrl: Null or undefined parameters.');
1627            return lResult;
1628        }
1629
1630        lParamString = FBUtilities.JoinHashArray(aParameters, '&');
1631        if (lParamString.length > 0)
1632        {
1633            lParamString = '?' + lParamString;
1634        }
1635
1636        return lResult + lParamString;
1637    }; //end BuildGetUrl
1638
1639    FBUtilities.CombinePath = function (...segments)
1640    {
1641        let combined = '';
1642        for (const segment of segments)
1643        {
1644            if (!FBUtilities.IsStringEmptyOrWhitespace(segment))
1645            {
1646                combined = combined.replace(/\/+$/, '') + '/' + segment.replace(/^\/+/, '');
1647            }
1648        }
1649        return combined;
1650    };
1651    FBUtilities.LogMessage = function (aMessage)
1652    {
1653        if (FBUtilities.DebugMode === false) 
1654        {
1655            return;
1656        }
1657
1658        if (window.console
1659            && window.console.log)
1660        {
1661            window.console.log(aMessage);
1662        }
1663    }; //end LogMessage
1664
1665
1666    FBUtilities.GetPageSize = function ()
1667    {
1668        var lXScroll,
1669            lYScroll,
1670            lWindowWidth,
1671            lWindowHeight,
1672            lPageWidth,
1673            lPageHeight,
1674            self = window,
1675            lArrayPageSize;
1676
1677        if (window.innerHeight
1678            && window.scrollMaxY) 
1679        {
1680            lXScroll = document.body.scrollWidth;
1681            lYScroll = window.innerHeight + window.scrollMaxY;
1682        }
1683        else if (document.body.scrollHeight > document.body.offsetHeight)
1684        {
1685            // all but Explorer Mac
1686            lXScroll = document.body.scrollWidth;
1687            lYScroll = document.body.scrollHeight;
1688        }
1689        else 
1690        {
1691            // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
1692            lXScroll = document.body.offsetWidth;
1693            lYScroll = document.body.offsetHeight;
1694        }
1695
1696
1697        if (self.innerHeight) 
1698        {
1699            // all except Explorer
1700            lWindowWidth = self.innerWidth;
1701            lWindowHeight = self.innerHeight;
1702        }
1703        else if (document.documentElement
1704            && document.documentElement.clientHeight) 
1705        {
1706            // Explorer 6 Strict Mode
1707            lWindowWidth = document.documentElement.clientWidth;
1708            lWindowHeight = document.documentElement.clientHeight;
1709        }
1710        else if (document.body) 
1711        {
1712            // other Explorers
1713            lWindowWidth = document.body.clientWidth;
1714            lWindowHeight = document.body.clientHeight;
1715        }
1716
1717        // for small pages with total height less then height of the viewport
1718        if (lYScroll < lWindowHeight)
1719        {
1720            lPageHeight = lWindowHeight;
1721        }
1722        else 
1723        {
1724            lPageHeight = lYScroll;
1725        }
1726
1727        // for small pages with total width less then width of the viewport
1728        if (lXScroll < lWindowWidth)
1729        {
1730            lPageWidth = lWindowWidth;
1731        }
1732        else 
1733        {
1734            lPageWidth = lXScroll;
1735        }
1736
1737        lArrayPageSize = [lPageWidth, lPageHeight, lWindowWidth, lWindowHeight];
1738        return lArrayPageSize;
1739    };//end GetPageSize
1740
1741    FBUtilities.GetPageScroll = function ()
1742    {
1743        var lYScroll,
1744            self = window,
1745            lArrayPageScroll;
1746
1747        if (self.pageYOffset) 
1748        {
1749            lYScroll = self.pageYOffset;
1750        }
1751        else if (document.documentElement
1752            && document.documentElement.scrollTop)
1753        {
1754            // Explorer 6 Strict
1755            lYScroll = document.documentElement.scrollTop;
1756        }
1757        else if (document.body) 
1758        {
1759            // all other Explorers
1760            lYScroll = document.body.scrollTop;
1761        }
1762
1763        lArrayPageScroll = ['', lYScroll];
1764        return lArrayPageScroll;
1765    };//end GetPageScroll
1766
1767    FBUtilities.SetupStickyNav = function (aHeaderSelector, aStickyNav
1767Selector)
1768    {
1769        var lHeader = $(aHeaderSelector);
1770        if (lHeader.length === 0)
1771        {
1772            return;
1773        }
1774
1775        var lStickyNav = $(aStickyNavSelector);
1776        if (lStickyNav.length === 0)
1777        {
1778            return;
1779        }
1780
1781        var lHeaderHeight = lHeader.outerHeight();
1782        var lStickyNavHeight = lStickyNav.outerHeight();
1783
1784        if (lHeaderHeight === 0
1785            || lStickyNavHeight === 0
1786            || lHeaderHeight < lStickyNavHeight)
1787        {
1788            return;
1789        }
1790
1791        var lAboveHeight = lHeaderHeight;
1792
1793
1794        var lUpdateStickyNav = function ()
1795        {
1796            //if the page size is too small, remove modifiers
1797            if (window.innerWidth < 768)
1798            {
1799                lStickyNav.removeClass('fixed_menu');
1800            }
1801            else
1802            {
1803                lHeaderHeight = lHeader.outerHeight();
1804                lAboveHeight = lHeaderHeight;
1805            }
1806        };
1807
1808        $(window).scroll(
1809            function ()
1810            {
1811                //if (Modernizr.mq('(max-width: 767px)')) {
1812                if ($(window).width() < 768)
1813                {
1814                    return;
1815                }
1816
1817                //if scrolled down more than the header’s height
1818                if ($(window).scrollTop() > lAboveHeight)
1819                {
1820                    // if yes, add sticky class to the sticky nav
1821                    lStickyNav.addClass('fixed_menu');
1822                    $('.main_menu  .top_level_item > .drop_down').css('max-height', 'calc(100vh - ' + lStickyNavHeight + 'px');
1823                }
1824                else
1825                {
1826                    // when scroll up or less than lAboveHeight, remove the sticky class
1827                    lStickyNav.removeClass('fixed_menu');
1828                }
1829            }
1830        );
1831
1832        $(window).resize(lUpdateStickyNav);
1833        lUpdateStickyNav();
1834    };//end SetupStickyNav
1835
1836    FBUtilities.SetupStickyHeader = function (aHeaderSelector, aPaddingSelector)
1837    {
1838        var lHeader = $(aHeaderSelector);
1839        if (lHeader.length === 0)
1840        {
1841            return;
1842        }
1843
1844        var lPaddedElement = $(aPaddingSelector);
1845        if (lPaddedElement.length === 0)
1846        {
1847            return;
1848        }
1849
1850        var lHeaderHeight = lHeader.outerHeight();
1851
1852        if (lHeaderHeight === 0)
1853        {
1854            return;
1855        }
1856
1857        var lMainNavClass = 'nav-main';
1858        if ($('.' + lMainNavClass).length === 0)
1859        {
1860            lMainNavClass = 'main-nav';
1861        }
1862        if ($('.' + lMainNavClass).length === 0)
1863        {
1864            lMainNavClass = null;
1865        }
1866
1867        var lUpdateStickyHeader = function ()
1868        {
1869            //if the page size is too small, remove modifiers
1870            if (window.innerWidth < 768)
1871            {
1872                lHeader.removeClass('fixed_header');
1873                $('.fixed_header_padded').css('padding-top', '');
1874                if (lMainNavClass !== null)
1875                {
1876                    $('.' + lMainNavClass + ' .top_level_item > .drop').css('max-height', 'none');
1877                }
1878                lPaddedElement.removeClass('fixed_header_padded');
1879                //if there might be some other dynamic styling, set the styled bits to "".
1880            }
1881            else
1882            {
1883                // if yes, modify the stuff
1884                lHeader.addClass('fixed_header');
1885                lPaddedElement.addClass('fixed_header_padded');
1886
1887                // remove shrink class before measuring header height
1888                lHeader.removeClass('shrink');
1889                lHeaderHeight = lHeader.outerHeight();
1890                $("body").css("--StickyHeader-Height", "" + lHeaderHeight + "px");
1891
1892                if ($(document).scrollTop() > 50)
1893                {
1894                    lHeader.addClass("shrink");
1895                }
1896
1897                $('.fixed_header_padded').css('padding-top', lHeaderHeight + 'px');
1898                if (lMainNavClass !== null)
1899                {
1900                    //$('.' + lMainNavClass + ' .top_level_item > .drop').attr("tabindex", "1");// Not sure why this was here. Removed for #16028
1901                    $('.' + lMainNavClass + ' .top_level_item > .drop').css('max-height', 'calc(100vh - ' + lHeaderHeight + 'px');
1902                }
1903            }
1904        };
1905
1906        // https://codepen.io/tomdurkin/pen/nvAjd
1907        $(document).on("scroll", function ()
1908        {
1909            if
1910                ($(document).scrollTop() > 50)
1911            {
1912                lHeader.addClass("shrink");
1913            }
1914            else
1915            {
1916                lHeader.removeClass("shrink");
1917            }
1918        });
1919
1920        $(window).resize(lUpdateStickyHeader);
1921        lUpdateStickyHeader();
1922    };//end SetupStickyHeader
1923
1924    // Sticky Header and Focus area with content that scrolls up between the two
1925    // - lUpdateFocusHeight helps define dimensions of a rotator 
1926    //   which doesn't have a height of its own when sticky due to absolute positioned images
1927    // - This does require some skin-level css
1928    FBUtilities.SetupStickyFocus = function (aHeaderSelector, aFocusWrapperSelector, aFocusSelector)
1929    {
1930        var lHeader = $(aHeaderSelector);
1931        if (lHeader.length === 0)
1932        {
1933            return;
1934        }
1935
1936        var lFocusWrapperElement = $(aFocusWrapperSelector);
1937        if (lFocusWrapperElement.length === 0)
1938        {
1939            return;
1940        }
1941
1942        var lFocusElement = $(aFocusSelector);
1943        if (lFocusElement.length === 0)
1944        {
1945            return;
1946        }
1947
1948        var lHeaderHeight = lHeader.outerHeight();
1949
1950        if (lHeaderHeight === 0)
1951        {
1952            return;
1953        }
1954
1955        var lFocusHeight = 0;
1956
1957        var lUpdateFocusHeight = function ()
1958        {
1959            lFocusWrapperElement.removeClass('fixed_focus_wrapper');
1960            lFocusElement.removeClass('fixed_focus');
1961            lFocusHeight = lFocusElement.outerHeight();
1962            if (lFocusHeight === 0)
1963            {
1964                return;
1965            }
1966        }
1967
1968        var lMainNavClass = 'nav-main';
1969        if ($('.' + lMainNavClass).length === 0)
1970        {
1971            lMainNavClass = 'main-nav';
1972        }
1973        if ($('.' + lMainNavClass).length === 0)
1974        {
1975            lMainNavClass = null;
1976        }
1977
1978        var lUpdateStickyContent = function ()
1979        {
1980            //if the page size is too small, remove modifiers
1981            if (window.innerWidth < 768)
1982            {
1983                lHeader.removeClass('fixed_header');
1984                $('.fixed_focus_wrapper').css('padding-bottom', '');
1985                if (lMainNavClass !== null)
1986                {
1987                    $('.' + lMainNavClass + ' .top_level_item > .drop').css('max-height', 'none');
1988                }
1989                lFocusWrapperElement.removeClass('fixed_focus_wrapper');
1990                lFocusElement.removeClass('fixed_focus');
1991                //if there might be some other dynamic styling, set the styled bits to "".
1992            }
1993            else
1994            {
1995                // if yes, modify the stuff
1996                lHeader.addClass('fixed_header');
1997                lUpdateFocusHeight();
1998                lFocusWrapperElement.addClass('fixed_focus_wrapper');
1999                lHeaderHeight = lHeader.outerHeight();
2000                $('.fixed_focus_wrapper').css('padding-bottom', lFocusHeight + 'px');
2001                lFocusElement.addClass('fixed_focus');
2002                $('.home_focus').css('padding-bottom', '0');
2003                if (lMainNavClass !== null)
2004                {
2005                    //$('.' + lMainNavClass + ' .top_level_item > .drop').attr("tabindex", "1");// Not sure why this was here. Removed for #16028
2006                    $('.' + lMainNavClass + ' .top_level_item > .drop').css('max-height', 'calc(100vh - ' + lHeaderHeight + 'px');
2007                }
2008            }
2009        };
2010
2011        // https://codepen.io/tomdurkin/pen/nvAjd
2012        $(document).on("scroll", function ()
2013        {
2014            if
2015                ($(document).scrollTop() > 50)
2016            {
2017                $("header").addClass("shrink");
2018            }
2019            else
2020            {
2021                $("header").removeClass("shrink");
2022            }
2023        });
2024
2025        $(window).resize(lUpdateStickyContent);
2026        lUpdateStickyContent();
2027    };//end SetupStickyFocus
2028
2029    // Date related functions.
2030    const REGEX_TIME_24 = new RegExp(/^([0-2][0-9]):([0-5][0-9])$/);
2031    const REGEX_TIME_12 = new RegExp(/^(?:1[0-2]|0?[1-9]):[0-5][0-9] [aApP][mM]$/);
2032
2033    FBUtilities.IsDateBeforeOrEqual = function (startDateString, endDateString, granularity)
2034    {
2035        if (FBUtilities.IsStringEmptyOrWhitespace(startDateString) || FBUtilities.IsStringEmptyOrWhitespace(endDateString))
2036        {
2037            return false;
2038        }
2039
2040        const luxonStart = FBUtilities.ParseDateString(startDateString);
2041        const luxonEnd = FBUtilities.ParseDateString(endDateString);
2042        return luxonStart.isValid && luxonEnd.isValid && (luxonStart.startOf(granularity) <= luxonEnd.startOf(granularity));
2043    };
2044
2045    FBUtilities.IsDateAndTimeBeforeOrEqual = function (startDate, startTime, endDate, endTime, granularity)
2046    {
2047        if (FBUtilities.IsStringEmptyOrWhitespace(startDate) 
2048            || FBUtilities.IsStringEmptyOrWhitespace(startTime)
2049            || FBUtilities.IsStringEmptyOrWhitespace(endDate)
2050            || FBUtilities.IsStringEmptyOrWhitespace(endTime)
2051        )
2052        {
2053            return false;
2054        }
2055
2056        const luxonStart = FBUtilities.ParseDateTimeString(`${startDate} ${startTime}`);
2057        const luxonEnd = FBUtilities.ParseDateTimeString(`${endDate} ${endTime}`);
2058        return luxonStart.isValid && luxonEnd.isValid && (luxonStart.startOf(granularity) <= luxonEnd.startOf(granularity));
2059    };
2060
2061
2062    FBUtilities.IsTimeString = function (timeString)
2063    {
2064        return !FBUtilities.IsStringEmptyOrWhitespace(timeString) && (REGEX_TIME_24.test(timeString) || REGEX_TIME_12.test(timeString));
2065    };
2066
2067    FBUtilities.ParseDateTimeString = function (dateTimeString)
2068    {
2069        const formats = [
2070            'yyyy-MM-dd HH:mm',
2071            'MM-dd-yyyy HH:mm',
2072            'yyyy-MM-dd H:mm',
2073            'MM-dd-yyyy H:mm',
2074            'yyyy-MM-dd hh:mm a',
2075            'MM-dd-yyyy hh:mm a',
2076            'yyyy-MM-dd h:mm a',
2077            'MM-dd-yyyy h:mm a'
2078        ];
2079        let dateTime = luxon.DateTime.invalid;
2080        for(const format of formats)
2081        {
2082            dateTime = luxon.DateTime.fromFormat(`${dateTimeString}`, format);
2083            if (dateTime.isValid)
2084            {
2085                break;
2086            }
2087        }
2088        return dateTime;
2089    };
2090
2091    FBUtilities.ParseDateString = function (dateString)
2092    {
2093        const formats = ['MM/dd/yyyy', 'MM-dd-yyyy', 'yyyy-MM-dd'];
2094        let date = luxon.DateTime.invalid;
2095        for(const format of formats)
2096        {
2097            date = luxon.DateTime.fromFormat(`${dateString}`, format);
2098            if (date.isValid)
2099            {
2100                break;
2101            }
2102        }
2103        return date;
2104    };
2105
2106    FBUtilities.IsDateString = function (dateString)
2107    {
2108        return FBUtilities.ParseDateString(dateString).isValid;
2109    };
2110
2111    FBUtilities.ReformatToStandardDateString = function (dateString)
2112    {
2113        const date = FBUtilities.ParseDateString(dateString);
2114        return date.toFormat('MM/dd/yyyy');
2115    };     
2116
2117    FBUtilities.ReformatToLongDateString = function (dateString)
2118    {
2119        const date = FBUtilities.ParseDateString(dateString);
2120        return date.toFormat('"cccc, MMMM d yyyy');
2121    };
2122
2123    FBUtilities.ParseTimeString = function (timeString)
2124    {
2125        const formats = ['HH:mm', 'H:mm', 'hh:mm a', 'h:mm a'];
2126        let time = luxon.DateTime.invalid;
2127        for(const format of formats)
2128        {
2129            time = luxon.DateTime.fromFormat(`${timeString}`, format);
2130            if (time.isValid)
2131            {
2132                break;
2133            }
2134        }
2135        return time;
2136    };
2137
2138    FBUtilities.ReformatToStandardTimeString = function (timeString)
2139    {
2140        const date = FBUtilities.ParseTimeString(timeString);
2141        return date.toFormat('h:mm a');
2142    };
2143
2144    FBUtilities.IsHtmlDateField = function (aField)
2145    {
2146        let isHtmlDateField;
2147        if (aField === undefined)
2148        {
2149            isHtmlDateField = false;
2150        }
2151        else if (aField === null)
2152        {
2153            isHtmlDateField = false;
2154        }
2155        else if ($(aField).attr('type') !== 'date')
2156        {
2157            isHtmlDateField = false;
2158        }
2159        else if ($(aField).prop('type') !== 'date')
2160        {
2161            isHtmlDateField = false;
2162        }
2163        else
2164        {
2165            isHtmlDateField = true;
2166        }
2167        return isHtmlDateField;
2168    };
2169
2170    FBUtilities.IsHtmlTimeField = function (aField)
2171    {
2172        let isTimeField;
2173        if (aField === undefined)
2174        {
2175            isTimeField = false;
2176        }
2177        else if (aField === null)
2178        {
2179            isTimeField = false;
2180        }
2181        else if ($(aField).attr('type') !== 'time')
2182        {
2183            isTimeField = false;
2184        }
2185        else if ($(aField).prop('type') !== 'time')
2186        {
2187            isTimeField = false;
2188        }
2189        else
2190        {
2191            isTimeField = true;
2192        }
2193        return isTimeField;
2194    };
2195
2196    
2197
2198    
2199    /**
2200     * Updates the style of the widget overlay underneath a jquery dialog. This method
2201     * ensures that the overlay has a reasonable zindex and is semi-opaque. 
2202     * @param {any} the jquery dialog.
2203     */
2204    FBUtilities.UpdateWidgetOverlayStyle = function (dialog)
2205    {
2206        let dialogIndex = window.getComputedStyle(dialog).getPropertyValue('z-index');
2207        if (!Number.isInteger(dialogIndex))
2208        {
2209            dialogIndex = $(dialog)[0].style.zIndex;
2210            if (!Number.isInteger(dialogIndex))
2211            {
2212                dialogIndex = 100;
2213            }
2214        }
2215
2216        let overlayIndex = dialogIndex - 1;
2217        $(".ui-widget-overlay").attr('style', `background-color: #000; opacity:0.6; z-index:${overlayIndex};`);
2218    };
2219    
2220    // appends the markup given to the DOM element given.
2221    // Does nothing if either is null or markup is whitespace.
2222    FBUtilities.AppendMarkup = function (aElement, aMarkup)
2223    {
2224        if (FBUtilities.IsParameterUndefinedOrNull(aElement) === true)
2225        {
2226            return;
2227        }
2228        var lNewElement = FBUtilities.BuildDomElement(aMarkup);
2229        if (lNewElement === null)
2230        {
2231            return;
2232        }
2233        aElement.appendChild(lNewElement);
2234    };
2235    
2236    // Returns a loose DOM element (child of a Template) built from the markup given.
2237    // Returns null if markup is null or whitespace.
2238    FBUtilities.BuildDomElement = function (aMarkup)
2239    {
2240        if (FBUtilities.IsStringEmptyOrWhitespace(aMarkup) === true)
2241        {
2242            return null;
2243        }
2244        var tempDiv = document.createElement('template');
2245        tempDiv.innerHTML = aMarkup.trim();
2246        return tempDiv.content.firstChild;
2247    };
2248    
2249    // Returns valid Foxbright Icons markup.
2250    // Returns an empty string if the icon url or title are null or empty.
2251    FBUtilities.BuildFBIconMarkup = function (aIconUrl, aTitle, aAdditionalClasses)
2252    {
2253        if (FBUtilities.IsStringEmptyOrWhitespace(aIconUrl) === true
2254            || FBUtilities.IsStringEmptyOrWhitespace(aTitle) === true)
2255        {
2256            return '';
2257        }
2258        let lAdditionalClasses = '';
2259        if (FBUtilities.IsStringEmptyOrWhitespace(aAdditionalClasses) === false)
2260        {
2261            lAdditionalClasses = ' ' + aAdditionalClasses.trim();
2262        }
2263        return `<span class="fb-icon${lAdditionalClasses}" title="${aTitle}"><load-file replaceWith src="${aIconUrl}"></load-file></span>`;
2264    };
2265
2266    function _getVersionedStorageObjectKey(itemName)
2267    {
2268        const version = typeof cmsVersion !== 'undefined' ? cmsVersion : "website";
2269        return `${itemName}_${version}`;
2270    }
2271
2272    FBUtilities.getLocalStorageObject = function (itemName)
2273    {        
2274        let result = undefined;
2275        const key = _getVersionedStorageObjectKey(itemName);
2276        const item = localStorage.getItem(key);
2277        if (item !== null)
2278        {
2279            const jsonString = FoxEncoder.decodeBase64andUTF8(item);
2280            result = JSON.parse(jsonString);
2281        }
2282        return result;
2283    };
2284
2285    FBUtilities.setLocalStorageObject = function (itemName, dataObject)
2286    {
2287        const key = _getVersionedStorageObjectKey(itemName);
2288        const jsonString = JSON.stringify(dataObject);
2289        const base64value = FoxEncoder.encodeUTF8andBase64(jsonString);
2290        localStorage.setItem(key, base64value);
2291    };
2292
2293    FBUtilities.removeLocalStorageObject = function (itemName)
2294    {
2295        const key = _getVersionedStorageObjectKey(itemName);
2296        localStorage.removeItem(key);
2297    };
2298
2299    let lastServerCheckedTime = null;
2300
2301    function _showCheckServerAlert(message)
2302    {
2303        const alertClasses = [
2304            'alert-primary',
2305            'alert-secondary',
2306            'alert-success',
2307            'alert-danger',
2308            'alert-warning',
2309            'alert-info',
2310            'alert-light',
2311            'alert-dark',
2312        ];
2313
2314        const alertElement = $('#admincp_alert');        
2315        const wantClass = 'alert-warning';        
2316        const previouslyShown = !alertElement.hasClass("d-none");
2317        const previouslySameClass = alertElement.hasClass(wantClass);
2318        const previouslySameMessage = alertElement.html() === message;
2319
2320        if (!previouslySameClass)
2321        {
2322            alertClasses.forEach(classname =>
2323            {
2324                if (classname !== wantClass)
2325                {
2326                    alertElement.removeClass(classname)
2327                }                    
2328            });
2329            alertElement.addClass(wantClass);
2330        }
2331
2332        if (!previouslySameMessage)
2333        {
2334            alertElement.html(message);
2335        }
2336
2337        if (!previouslyShown)
2338        {
2339            alertElement.removeClass("d-none");
2340        }
2341
2342        if (!previouslyShown || !previouslySameClass || !previouslySameMessage)
2343        {
2344            alert(message);
2345        }
2346    }
2347
2348    function _checkServer()
2349    {
2350        $.ajax({
2351            type: "get",
2352            url: "/Admin/Home/Check",
2353            dataType: "json",
2354            async: true,
2355            error: function (jqXHR, textStatus, errorThrown) 
2356            {
2357                if (jqXHR.status === 401 || jqXHR.status === 403)
2358                {
2359                    console.info(`Logging out. No longer authorized by server.`);
2360                    document.location = '/Admin/Logout';
2361                    return;
2362                }
2363                console.log(`${textStatus}: ${errorThrown}`);
2364            },
2365            success: function (data, _textStatus, _jqXHR)
2366            {
2367                if (data.version !== undefined && data.version !== cmsVersion)
2368                {
2369                    console.info(`Logging out. Server was upgraded to version ${data.version}.`);
2370                    document.location = '/Admin/Logout';
2371                    return;
2372                }
2373
2374                if (data.status !== undefined && data.status !== 'ok')
2375                {
2376                    console.info(`_checkServer: ${data.status} ${data.message}`);
2377                    _showCheckServerAlert(data.message);                
2378                }
2379            },
2380            complete: function ()
2381            {
2382                const currentTime = Date.now();
2383                if (lastServerCheckedTime !== null)
2384                {
2385                    var elapsedTime = currentTime - lastServerCheckedTime;
2386                    if (elapsedTime > cmsCheckServerMaxEllapsedMilli)
2387                    {
2388                        console.info(`Logging out. Server last checked ${elapsedTime}ms ago.`);
2389                        document.location = '/Admin/Logout';
2390                        return;
2391                    }
2392                }
2393                lastServerCheckedTime = currentTime;
2394            }
2395        });
2396        setTimeout(_checkServer, 15000);
2397    };
2398
2399    FBUtilities.startCheckServer = function ()
2400    {
2401        const pathname = document.location.pathname;
2402        if (pathname.toLowerCase().startsWith('/admin/'))
2403        {
2404            setTimeout(_checkServer, 10);
2405        }
2406    };
2407    
2408}(jQuery));
2409
2410///////////////////////////////////////////////////////////////////////////////
2411// FBAjaxDialog
2412///////////////////////////////////////////////////////////////////////////////
2413function FBAjaxDialog()
2414{
2415    this._vars = {
2416        AjaxRequestObject: null,
2417        BackdropClass: "backdrop",
2418        CloseButtonClass: 'fbd_close',
2419        CurrentCallbacks: null,
2420        DataOptionsName: "fbd-dialog-options",
2421        DialogClass: 'fbd_dialog',
2422        DialogId: 'fbd_ajaxdialog',
2423        TitleClass: 'fbd_title'
2424    };
2425
2426    this.options = {
2427        ActionType: 'GET',
2428        AjaxUrl: null,
2429        Data: null,
2430        ErrorContent: null,
2431        ErrorDialogCallbacks: null,
2432        ErrorDialogTitle: 'Error',
2433        ExtraDialogClasses: null,
2434        LoadingContent: null,
2435        LoadingDialogCallbacks: null,
2436        LoadingDialogTitle: 'Loading...',
2437        OnCloseCallback: null,
2438        SuccessDialogCallbacks: null,
2439        SuccessDialogTitle: 'Detail',
2440        ParentSelector: "body",
2441        SelectionElement: null
2442    };
2443}
2444
2445(function ($)
2446{
2447    FBAjaxDialog._activeDialogs = {
2448    };
2449
2450    FBAjaxDialog.FindAndClose = function (aDialogId)
2451    {
2452        let dialogId = aDialogId === undefined ? 'fbd_ajaxdialog' : aDialogId;
2453        if (FBAjaxDialog._activeDialogs[dialogId] !== undefined)
2454        {
2455            FBAjaxDialog._activeDialogs[dialogId].Close();
2456        }
2457    };
2458
2459    FBAjaxDialog.prototype.Start = function (options)
2460    {
2461        let self = this;
2462        self.options = $.extend({}, self.options, options);
2463
2464        if (FBUtilities.IsParameterUndefinedOrEmpty(self.options.LoadingContent))
2465        {
2466            console.debug("The LoadingContent value was not specified.");
2467            return;
2468        }
2469
2470        if (FBUtilities.IsParameterUndefinedOrEmpty(self.options.AjaxUrl))
2471        {
2472            console.debug("The AjaxUrl value was not specified.");
2473            return;
2474        }
2475
2476        if (FBUtilities.IsParameterUndefinedOrEmpty(self.options.ErrorContent))
2477        {
2478            console.debug("The ErrorContent value was not specified.");
2479            return;
2480        }
2481
2482        if (FBUtilities.IsParameterUndefinedOrEmpty(self.options.ActionType))
2483        {
2484            console.debug("The ActionType value was not specified.");
2485            return;
2486        }
2487
2488        self.options.Data = self.options.Data === undefined ? {} : self.options.Data;
2489        self._showLoadingContent();
2490        self._vars.AjaxRequestObject = $.ajax(
2491            self.options.AjaxUrl,
2492            {
2493                success: function (aData, _aTextStatus, _aJqXHR)
2494                {
2495                    self._showSuccessContent(aData);
2496                },
2497                error: function (_aJqXHR, _aTextStatus, _aErrorThrown)
2498                {
2499                    self._showErrorContent();
2500                },
2501                complete: function (_aData, _aTextStatus, _aJqXHR)
2502                {
2503                    self._vars.AjaxRequestObject = null;
2504                },
2505                type: self.options.ActionType,
2506                data: self.options.Data
2507            });
2508    };
2509
2510    FBAjaxDialog.prototype.Close = function ()
2511    {
2512        let dialog = $(`#${this._vars.DialogId}`);
2513        if (dialog.length === 0)
2514        {
2515            return;
2516        }
2517
2518        if (this._vars.AjaxRequestObject !== undefined && this._vars.AjaxRequestObject !== null)
2519        {
2520            this._vars.AjaxRequestObject.abort();
2521            this._vars.AjaxRequestObject = null;
2522        }
2523
2524        if (dialog.is(':visible'))
2525        {
2526            dialog.hide();
2527            dialog.next('.backdrop').remove();
2528        }
2529
2530        FBUtilities.RunCallback(this._vars.CurrentCallbacks, 'OnClose', dialog);
2531
2532        if (this._vars.OnCloseCallback !== undefined && this._vars.OnCloseCallback !== null && typeof this._vars.OnCloseCallback === 'function')
2533        {
2534            this._vars.OnCloseCallback();
2535        }
2536
2537        $(dialog).vpFocusManager("dropFocus");
2538        $(dialog).vpFocusManager("destroy");        
2539        dialog.remove();          
2540        delete FBAjaxDialog._activeDialogs[this._vars.DialogId];
2541
2542
2543    };
2544
2545    FBAjaxDialog.prototype._showErrorContent = function ()
2546    {
2547        this._show(
2548            this.options.ErrorContent,
2549            {
2550                ExtraDialogClasses: this.options.ExtraDialogClasses,
2551                Title: this.options.ErrorDialogTitle,
2552                CloseButtonText: 'Close',
2553                Callbacks: this.options.ErrorDialogCallbacks,
2554                OnCloseCallback: this.options.OnCloseCallback
2555            }
2556        );
2557    };
2558
2559    FBAjaxDialog.prototype._showSuccessContent = function (content)
2560    {
2561        this._show(
2562            content,
2563            {
2564                ExtraDialogClasses: this.options.ExtraDialogClasses,
2565                Title: this.options.SuccessDialogTitle,
2566                CloseButtonText: 'Close',
2567                Callbacks: this.options.SuccessDialogCallbacks,
2568                OnCloseCallback: this.options.OnCloseCallback
2569            }
2570        );
2571    };
2572
2573    FBAjaxDialog.prototype._showLoadingContent = function ()
2574    {
2575        this._show(
2576            this.options.LoadingContent,
2577            {
2578                ExtraDialogClasses: this.options.ExtraDialogClasses,
2579                Title: this.options.LoadingDialogTitle,
2580                CloseButtonText: 'Cancel',
2581                Callbacks: this.options.LoadingDialogCallbacks,
2582                OnCloseCallback: this.options.OnCloseCallback
2583            }
2584        );
2585    };
2586
2587    FBAjaxDialog.prototype._show = function (content, parameters)
2588    {
2589
2590        let self = this;
2591        FBAjaxDialog._activeDialogs[self._vars.DialogId] = this;
2592        let dialog = $(`#${self._vars.DialogId}`);
2593        if (dialog.length === 0)
2594        {
2595            const labelId = `${self._vars.DialogId}-label`;
2596            const contentId = `${self._vars.DialogId}-content`;
2597            dialog = $(`<dialog id="${self._vars.DialogId}" role="alertdialog" class="${self._vars.DialogClass}" aria-labelledby="${labelId}" aria-describedby="${contentId}"></dialog>`);
2598
2599            $(self.options.ParentSelector).prepend(dialog);
2600
2601            dialog = $(`.${self._vars.DialogClass}`);
2602            if (typeof dialogPolyfill !== 'undefined')
2603            {
2604                dialogPolyfill.registerDialog(dialog.get(0));
2605            }
2606
2607            dialog.on('click', `.${self._vars.CloseButtonClass}`, function (aEvent) 
2608            {
2609                aEvent.preventDefault();
2610                self.Close();
2611                return false;
2612            });
2613
2614            //dialog.append(`<div class="fbd_header"><span class="${self._vars.TitleClass}"></span><a href="javascript:" aria-label="Close Dialog" class="${self._vars.CloseButtonClass}"><span class="fas fa-window-close"></span><span class="screen-reader-text">Close Dialog</span></a></div>`);
2615            dialog.append(`<div class="fbd_header"><span id="${labelId}" class="${self._vars.TitleClass}"></span><a href="javascript:" aria-label="Close Dialog" class="${self._vars.CloseButtonClass}"><img src="/img/dialog-icon-close.svg"/><span class="screen-reader-text">Close Dialog</span></a></div>`);
2616            dialog.append(`<div id="${contentId}" class="fbd_content fbcms_content"></div>`);
2617        }
2618        else
2619        {
2620            if (dialog.is(':visible'))
2621            {
2622                dialog.hide();
2623                dialog.next(`.${self._vars.BackdropClass}`).remove();
2624            }
2625            dialog.find('.fbd_content').html('');
2626        }
2627
2628        this._vars.CurrentCallbacks = parameters.Callbacks;
2629        this._vars.OnCloseCallback = parameters.OnCloseCallback;
2630        if (!FBUtilities.IsParameterUndefinedOrEmpty(parameters.ExtraDialogClasses))
2631        {
2632            dialog.addClass(parameters.ExtraDialogClasses);
2633        }
2634
2635        dialog.find(`.${self._vars.TitleClass}`).html(parameters.Title);
2636        dialog.find('.fbd_content').html(content);
2637        dialog.find(`.${self._vars.CloseButtonClass}`).attr('title', `${parameters.CloseButtonText} ${parameters.Title} dialog.`);
2638
2639        FBUtilities.RunCallback(this._vars.CurrentCallbacks, 'OnInit', dialog);
2640
2641        if (!dialog.is(':visible'))
2642        {
2643            dialog.after(`<div class="backdrop"></div>`);
2644            dialog.show();
2645        }
2646        
2647        if ($(dialog).vpFocusManager("instance") === undefined)
2648        {
2649            $(dialog).vpFocusManager(
2650                {
2651                    ExitFocusSpecificElement : self.options.SelectionElement
2652                    , OnEscapeCallback : function() { self.Close(); }
2653                }
2654            );
2655        }
2656        $(dialog).vpFocusManager("takeFocus");
2657
2658        FBUtilities.RunCallback(self._vars.CurrentCallbacks, 'OnShow', dialog);
2659    };
2660}(jQuery));
2661
2662///////////////////////////////////////////////////////////////////////////////
2663// FBModal widget
2664///////////////////////////////////////////////////////////////////////////////
2665
2666function FBModal()
2667{
2668    this._vars = {
2669        // true if it's initialized
2670        IsDialogInit: false,
2671        // true if open or closing, false otherwise
2672        IsDialogOpen: false,
2673        // true if opening.
2674        IsDialogOpening: false,
2675        // true if closing.
2676        IsDialogClosing: false,
2677        DialogClass: 'fbd_dialog',
2678        TitleClass: 'fbd_title',
2679        CloseButtonClass: 'fbd_close',
2680        OnCloseCallback: null,
2681        CurrentCallbacks: null,
2682        ButtonCallbacks: {},
2683        AjaxDefaults: {
2684            ActionType: 'GET',
2685            AjaxUrl: null,
2686            Data: null,
2687            ErrorContent: null,
2688            ErrorDialogCallbacks: null,
2689            ErrorDialogTitle: 'Error',
2690            LoadingContent: null,
2691            LoadingDialogCallbacks: null,
2692            LoadingDialogTitle: 'Loading...',
2693            SuccessDialogCallbacks: null,
2694            SuccessDialogTitle: 'Detail'
2695        }
2696    };
2697
2698    this.options = {
2699        DialogId: 'fbd_modaldialog',
2700        ParentSelector: "body",
2701        ExtraDialogClasses: null,
2702        Title: null,
2703        CloseButtonText: 'Close',
2704        AddFooterClose: false,
2705        Callbacks: null,
2706        OnCloseCallback: null,
2707        Buttons: null,
2708        Content: null,
2709        UseAjaxLoader: false,
2710        AjaxOptions: {
2711            ActionType: 'GET',
2712            AjaxUrl: null,
2713            Data: null,
2714            ErrorContent: null,
2715            ErrorDialogCallbacks: null,
2716            ErrorDialogTitle: 'Error',
2717            LoadingContent: null,
2718            LoadingDialogCallbacks: null,
2719            LoadingDialogTitle: 'Loading...',
2720            SuccessDialogCallbacks: null,
2721            SuccessDialogTitle: 'Detail'
2722        }
2723    };
2724}
2725
2726(function ($)
2727{
2728    FBModal._activeDialogs = {};
2729
2730    FBModal.FindAndClose = function (aDialogId)
2731    {
2732        let dialogId = aDialogId === undefined ? 'fbd_modaldialog' : aDialogId;
2733        if (FBModal._activeDialogs[dialogId] !== undefined)
2734        {
2735            FBModal._activeDialogs[dialogId].CloseDialog();
2736        }
2737    };
2738
2739    FBModal.FindAndUpdate = function (aDialogId)
2740    {
2741        let dialogId = aDialogId === undefined ? 'fbd_modaldialog' : aDialogId;
2742        if (FBModal._activeDialogs[dialogId] !== undefined)
2743        {
2744            FBModal._activeDialogs[dialogId].UpdateDialog();
2745        }
2746    };
2747
2748    FBModal.prototype.Start = function (options)        
2749    {
2750        let self = this;
2751        self.options = $.extend({}, self.options, options);
2752        if (this.options.UseAjaxLoader === true)
2753        {
2754            this.options.AjaxOptions = $.extend({}, this._vars.AjaxDefaults, this.options.AjaxOptions);
2755            this._useDialogForAjax();
2756        }
2757        else
2758        {
2759            this._startShowDialog();
2760        }
2761    };
2762
2763    FBModal.prototype.CloseDialog = function ()
2764    {
2765        this._closeDialog();
2766    };
2767
2768    FBModal.prototype.UpdateDialog = function ()
2769    {
2770        this._updateDialog();
2771    };
2772
2773    FBModal.prototype._getDialog = function ()
2774    {
2775        return $(`#${this.options.DialogId}`);
2776    };
2777
2778    FBModal.prototype._useDialogForAjax = function ()
2779    {
2780        let self = this;
2781
2782        FBUtilities.LogMessage('_useDialogForAjax - START');
2783        if (FBUtilities.IsParameterUndefinedOrEmpty(self.options.AjaxOptions.LoadingContent))
2784        {
2785            FBUtilities.LogMessage('_useDialogForAjax - REJECT - no loading content');
2786            return;
2787        }
2788
2789        if (FBUtilities.IsParameterUndefinedOrEmpty(self.options.AjaxOptions.AjaxUrl))
2790        {
2791            FBUtilities.LogMessage('_useDialogForAjax - REJECT - no ajax url');
2792            return;
2793        }
2794
2795        if (FBUtilities.IsParameterUndefinedOrEmpty(self.options.AjaxOptions.ErrorContent))
2796        {
2797            FBUtilities.LogMessage('_useDialogForAjax - REJECT - no error content');
2798            return;
2799        }
2800
2801        if (FBUtilities.IsParameterUndefinedOrEmpty(self.options.AjaxOptions.ActionType))
2802        {
2803            FBUtilities.LogMessage('_useDialogForAjax - REJECT - no action type');
2804            return;
2805        }
2806
2807        self.options.AjaxOptions.Data === undefined ? {} : self.options.AjaxOptions.Data;
2808
2809        self._vars.AjaxRequestObject = $.ajax(self.options.AjaxOptions.AjaxUrl,
2810            {
2811                success: function (aData, _aTextStatus, _aJqXHR)//ignore jslint
2812                {
2813                    FBUtilities.LogMessage('_useDialogForAjax - SUCCESS callback');
2814                    self.options.Content = aData;
2815                    self.options.Title = self.options.AjaxOptions.SuccessDialogTitle;
2816                    self.options.Callbacks = self.options.AjaxOptions.SuccessDialogCallbacks;
2817                    self._startShowDialog();
2818                },
2819                error: function (_aJqXHR, _aTextStatus, _aErrorThrown) //ignore jslint
2820                {
2821                    FBUtilities.LogMessage('_useDialogForAjax - ERROR callback');
2822                    self.options.Content = self.options.AjaxOptions.ErrorContent;
2823                    self.options.Title = self.options.AjaxOptions.ErrorDialogTitle;
2824                    self.options.Callbacks = self.options.AjaxOptions.ErrorDialogCallbacks;
2825                    self._startShowDialog();
2826                },
2827                complete: function (_aData, _aTextStatus, _aJqXHR)//ignore jslint
2828                {
2829                    FBUtilities.LogMessage('_useDialogForAjax - COMPLETE callback');
2830                    self._vars.AjaxRequestObject = null;
2831                },
2832                type: self.options.AjaxOptions.ActionType,
2833                data: self.options.AjaxOptions.Data
2834            });
2835        FBUtilities.LogMessage('_useDialogForAjax- COMPLETE');
2836    };
2837
2838    FBModal.prototype._startShowDialog = function ()
2839    {
2840        let self = this;
2841        FBUtilities.LogMessage('ShowDialog - START');
2842        if (FBUtilities.IsParameterUndefinedOrEmpty(self.options.Content))
2843        {
2844            FBUtilities.LogMessage('ShowDialog - REJECT - no content');
2845            return;
2846        }
2847
2848        if (FBUtilities.IsParameterUndefinedOrEmpty(self.options.Title))
2849        {
2850            //needs a title, don't null or undefine this thing.
2851            FBUtilities.LogMessage('ShowDialog - REJECT - no title');
2852            return;
2853        }
2854        if (FBUtilities.IsParameterUndefinedOrEmpty(self.options.CloseButtonText))
2855        {
2856            //needs a text, don't null or undefine this thing.
2857            FBUtilities.LogMessage('ShowDialog - REJECT - no close button text');
2858            return;
2859        }
2860        if (FBUtilities.IsParameterUndefinedOrEmpty(self.options.AddFooterClose))
2861        {
2862            //needs a value, don't null or undefine this thing.
2863            FBUtilities.LogMessage('ShowDialog - REJECT - bad footer close value');
2864            return;
2865        }
2866
2867        self._initDialog();
2868    };
2869
2870    FBModal.prototype._initDialog = function ()
2871    {
2872        let self = this;
2873        // we're here for business, let's see what's going on.
2874        FBUtilities.LogMessage('_initDialog - START');
2875
2876        // something is initialized... let's get it out of the way.
2877        if (self._vars.IsDialogInit === true)
2878        {
2879            FBUtilities.LogMessage('_initDialog - There\'s an existing dialog.');
2880
2881            // it's opening or open (this includes closing)...
2882            if (self._vars.IsDialogOpening === true || self._vars.IsDialogOpen === true)
2883            {
2884                let dialog = self._getDialog();
2885                // add a callback to get this show on the road once the old one is removed.
2886                dialog.on('hidden.bs.modal', function (e)
2887                {
2888                    FBUtilities.LogMessage('_initDialog Callback for initialization');
2889                    self._setupDialogHtml();
2890                });
2891
2892                // if it's not already closing, start that now.
2893                if (self._vars.IsDialogClosing === false)
2894                {
2895                    FBUtilities.LogMessage('_initDialog - Request it cease to be. (Callback will initialize)');
2896                    self._closeDialog();
2897                }
2898                return;
2899            }
2900
2901            FBUtilities.LogMessage('_initDialog - It\'s only initalized, destroy it.');
2902            self._destroyDialog();
2903        }
2904        FBUtilities.LogMessage('_initDialog - Init the dialog markup and events.');
2905
2906        // hey, nothing in the way, just set things to work.
2907        self._setupDialogHtml();
2908    };
2909
2910    FBModal.prototype._setupDialogHtml = function ()
2911    {
2912        let self = this;
2913
2914        FBUtilities.LogMessage('_setupDialogHtml - START.');
2915
2916
2917        let dialog = $(`<div id="${self.options.DialogId}" class="modal fade ${this._vars.DialogClass}" tabindex="-1" role="dialog" aria-labelledby="exampleModalLongTitle" aria-hidden="true"></div>`);
2918
2919        $(self.options.ParentSelector).prepend(dialog);
2920
2921        dialog = self._getDialog();
2922
2923        let footerMarkup = '';
2924        let buttonMarkup = '';
2925        if (!FBUtilities.IsParameterUndefinedOrEmpty(self.options.Buttons))
2926        {
2927            let hasValidButtons = false;
2928            for (var index in self.options.Buttons)
2929            {
2930                let buttonDeets = self.options.Buttons[index];
2931                if (FBUtilities.IsParameterUndefinedOrEmpty(buttonDeets['text']) || FBUtilities.IsParameterUndefinedOrEmpty(buttonDeets['click']))
2932                {
2933                    continue;
2934                }
2935
2936                let btnClasses = 'vp-btn-secondary';
2937                if (!FBUtilities.IsParameterUndefinedOrEmpty(buttonDeets['class']))
2938                {
2939                    btnClasses = buttonDeets['class'];
2940                }
2941
2942                let id = `bbdm_btn_${index}`;
2943                if (!FBUtilities.IsParameterUndefinedOrEmpty(buttonDeets['id']))
2944                {
2945                    id = buttonDeets.id;
2946                }
2947
2948                buttonMarkup += `        <button id="${id}" type="button" class="vp-btn ${btnClasses} bbdm_btn">${buttonDeets['text']}</button>`;
2949
2950                self._vars.ButtonCallbacks[id] = buttonDeets['click'];           
2951                hasValidButtons = true;
2952            }//endfor
2953
2954            if (hasValidButtons === true)
2955            {
2956                dialog.on('click', '.bbdm_btn', function (e)
2957                {
2958                    let button = $(e.target).closest('.bbdm_btn');
2959                    let buttonId = button.attr('id');
2960                    self._vars.ButtonCallbacks[buttonId](e);
2961                });
2962            }
2963        }
2964
2965        if (self.options.AddClose === true)
2966        {
2967            buttonMarkup += `        <button type="button" class="vp-btn vp-btn-secondary" data-dismiss="modal">${self.options.CloseButtonText}</button>`;
2968        }
2969
2970        if (buttonMarkup.length > 0)
2971        {
2972            footerMarkup = `      <div class="modal-footer">${buttonMarkup}</div>`;
2973        }
2974
2975        let dialogBody = `<div class="modal-dialog modal-lg modal-dialog-centered modal
2975-dialog-scrollable" role="document">
2976    <div class="modal-content">
2977        <div class="modal-header">
2978            <h5 class="modal-title ${self._vars.TitleClass}">Dialog Title</h5>
2979            <button type="button" class="close ${self._vars.CloseButtonClass}" data-dismiss="modal" aria-label="Close">
2980                <img src="/img/dialog-icon-close.svg" class="fbd_close-icon"/>
2981            </button>
2982        </div>
2983        <div class="modal-body fbd_content">
2984            <div class="container-fluid">
2985            </div>
2986        </div>
2987        ${footerMarkup}
2988    </div>
2989</div>`;
2990        dialog.append(dialogBody);
2991
2992        /* bootstrap events */
2993        dialog.off('shown.bs.modal').on('shown.bs.modal', function (e)
2994        {
2995            FBUtilities.RunCallback(self._vars.CurrentCallbacks, 'OnShow', dialog);
2996            self._vars.IsDialogOpening = false;
2997            self._vars.IsDialogOpen = true;
2998            FBUtilities.LogMessage('FBModal - dialog shown.');
2999        });
3000
3001        dialog.off('hidden.bs.modal').on('hidden.bs.modal', function (e)
3002        {
3003            FBUtilities.RunCallback(self._vars.CurrentCallbacks, 'OnClose', dialog);
3004
3005            if (self._vars.OnCloseCallback !== undefined
3006                && self._vars.OnCloseCallback !== null
3007                && typeof self._vars.OnCloseCallback === 'function')
3008            {
3009                self._vars.OnCloseCallback();
3010            }
3011            self._vars.IsDialogOpen = false;
3012            self._vars.IsDialogClosing = false;
3013            self._destroyDialog();
3014
3015            FBUtilities.LogMessage('FBModal - dialog hidden.');
3016        });
3017        /* bootstrap events */
3018
3019        self._vars.IsDialogInit = true;
3020
3021        // just to be thorough. Nothing from before should still exist.
3022        self._vars.CurrentCallbacks = self.options.Callbacks;
3023        self._vars.OnCloseCallback = self.options.OnCloseCallback;
3024
3025        if (!FBUtilities.IsParameterUndefinedOrEmpty(self.options.ExtraDialogClasses))
3026        {
3027            dialog.addClass(self.options.ExtraDialogClasses);
3028        }
3029
3030        dialog.find(`.${self._vars.TitleClass}`).html(self.options.Title);
3031        dialog.find('.fbd_content').html(self.options.Content);
3032        dialog.find(`.${self._vars.CloseButtonClass}`).attr('title', `${self.options.CloseButtonText} ${self.options.Title} dialog.`);
3033
3034        FBUtilities.RunCallback(self._vars.CurrentCallbacks, 'OnInit', dialog);
3035        self._showDialog();
3036        
3037        FBModal._activeDialogs[self.options.DialogId] = self;
3038
3039        FBUtilities.LogMessage('initDialogWorker - COMPLETE.');
3040        return dialog;
3041    };
3042
3043    FBModal.prototype._showDialog = function ()
3044    {
3045        let self = this;
3046        FBUtilities.LogMessage('_showDialog - START');
3047        if (self._vars.IsDialogInit === false)
3048        {
3049            FBUtilities.LogMessage('_showDialog - REJECT');
3050            return;
3051        }
3052
3053        let dialog = $(`.${self._vars.DialogClass}`);
3054        //dialog.after(`<div class="backdrop"></div>`);
3055        dialog.on('shown.bs.modal', function ()
3056        {
3057            FBUtilities.RunCallback(self._vars.CurrentCallbacks, 'OnShown', dialog);       
3058        });
3059        dialog.modal('show');
3060
3061        self._vars.IsDialogOpening = true;
3062        dialog.find('.modal-header button').focus();
3063        FBUtilities.LogMessage('_showDialog - COMPLETE (Requested)');
3064    };
3065
3066    FBModal.prototype._closeDialog = function ()
3067    {
3068        FBUtilities.LogMessage('_closeDialog - START');
3069        if (this._vars.IsDialogInit === false)
3070        {
3071            FBUtilities.LogMessage('_closeDialog - REJECT');
3072            return;
3073        }
3074
3075        if (this._vars.IsDialogOpen === true || this._vars.IsDialogOpening === true)
3076        {
3077            let dialog = this._getDialog();
3078            dialog.modal('hide');
3079            this._vars.IsDialogClosing = true;
3080        }
3081        FBUtilities.LogMessage('_closeDialog - COMPLETE (Requested)');
3082    };
3083
3084    FBModal.prototype._destroyDialog = function ()
3085    {
3086        FBUtilities.LogMessage('_destroyDialog - START');
3087        if (this._vars.IsDialogInit === false)
3088        {
3089            FBUtilities.LogMessage('_destroyDialog - REJECT');
3090            return;
3091        }
3092
3093        let dialog = this._getDialog();
3094        dialog.off('shown.bs.modal');
3095        dialog.off('hidden.bs.modal');
3096        dialog.off('click', '.bbdm_btn');
3097        this._vars.OnCloseCallback = null;
3098        this._vars.CurrentCallbacks = null;
3099        this._vars.ButtonCallbacks = {};
3100        dialog.next('.backdrop').remove();
3101        dialog.remove();
3102        this._vars.IsDialogInit = false;
3103
3104        delete FBModal._activeDialogs[this.options.DialogId];
3105        FBUtilities.LogMessage('_destroyDialog - COMPLETE');
3106    };
3107
3108    FBModal.prototype._updateDialog = function ()
3109    {
3110        FBUtilities.LogMessage('_updateDialog - START');
3111        if (this._vars.IsDialogInit === false)
3112        {
3113            FBUtilities.LogMessage('_updateDialog - REJECT');
3114            return;
3115        }
3116
3117        if (this._vars.IsDialogOpen === true && this._vars.IsDialogClosing === false)
3118        {
3119            this._getDialog().modal('handleUpdate');
3120        }
3121
3122        FBUtilities.LogMessage('_updateDialog - COMPLETE');
3123    };
3124}(jQuery));
3125
3126///////////////////////////////////////////////////////////////////////////////
3127// FBMenuManager - Menu Manager
3128///////////////////////////////////////////////////////////////////////////////
3129
3130(function ($)
3131{
3132    // Callbacks are OnSuccess, OnError, OnComplete
3133    FBMenuManager.LoadMenu = function (aParameters)
3134    {
3135        var lDefaults, lOptions, lData;
3136
3137        lDefaults =
3138        {
3139            AjaxUrl: '/Core/Menu/'
3140            , Tagname: null
3141            , CurrentUrl: null
3142            , TemplatePrefix: null
3143            , Preview: false
3144            , Callbacks: null
3145        };
3146
3147        lOptions = $.extend(lDefaults, aParameters);
3148
3149        if (FBUtilities.IsParameterUndefinedOrEmpty(lOptions.AjaxUrl))
3150        {
3151            return;
3152        }
3153        if (FBUtilities.IsParameterUndefinedOrEmpty(lOptions.Tagname))
3154        {
3155            return;
3156        }
3157
3158        lData =
3159        {
3160            tagname: lOptions.Tagname
3161        };
3162
3163        if (!FBUtilities.IsParameterUndefinedOrEmpty(lOptions.CurrentUrl))
3164        {
3165            lData.currenturl = lOptions.CurrentUrl;
3166        }
3167        if (!FBUtilities.IsParameterUndefinedOrEmpty(lOptions.TemplatePrefix))
3168        {
3169            lData.templateprefix = lOptions.TemplatePrefix;
3170        }
3171        if (!FBUtilities.IsParameterUndefinedOrEmpty(lOptions.Preview)
3172            && lOptions.Preview === true)
3173        {
3174            lData.preview = true;
3175        }
3176
3177        FBMenuManager.AjaxRequestObject = $.ajax(lOptions.AjaxUrl,
3178            {
3179                data: lData,
3180                success: function (aData, aTextStatus, aJqXHR)
3181                {
3182                    FBUtilities.LogMessage('Load Menu - Success');
3183                    FBUtilities.RunCallback(lOptions.Callbacks, 'OnSuccess', aData, aTextStatus, aJqXHR);
3184                },
3185                error: function (aJqXHR, aTextStatus, aErrorThrown)
3186                {
3187                    FBUtilities.LogMessage('Load Menu - Error');
3188                    FBUtilities.RunCallback(lOptions.Callbacks, 'OnError', aJqXHR, aTextStatus, aErrorThrown);
3189                },
3190                complete: function (aJqXHR, aTextStatus)
3191                {
3192                    FBUtilities.LogMessage('Load Menu - Complete');
3193                    FBUtilities.RunCallback(lOptions.Callbacks, 'OnComplete', aJqXHR, aTextStatus);
3194                },
3195                type: 'GET'
3196            });
3197    };//end LoadMenu
3198
3199}(jQuery));
3200
3201///////////////////////////////////////////////////////////////////////////////
3202// FBDateSelector
3203///////////////////////////////////////////////////////////////////////////////
3204
3205(function ($)
3206{
3207    $.widget("ui.FBDateSelector",
3208        {
3209            options:
3210            {
3211                ClearInputButtonClass: 'clear_input'
3212                , ClearInputButtonContent: '<span class="fas fa-times-circle"></span>'
3213                , ClearInputButtonLabel: 'Clear Input'
3214                , OnChangeCallback: null
3215                , DateFormat: null
3216            },
3217
3218            _destroy: function ()
3219            {
3220                return undefined;
3221            },
3222
3223            _init: function ()
3224            {
3225                return undefined;
3226            },
3227
3228            _create: function ()
3229            {
3230                var self = this,
3231                    jElement = $(self.element),
3232                    lPickerArgs = {},
3233                    lParentElement,
3234                    lClearInputButton;
3235
3236                if (self.options.ClearInputButtonClass === null
3237                    || self.options.ClearInputButtonContent === null)
3238                {
3239                    return;
3240                }
3241
3242
3243                lPickerArgs.gotoCurrent = true;
3244                if (self.options.DateFormat !== null)
3245                {
3246                    if (self.options.DateFormat === 'ISO_8601')
3247                    {
3248                        lPickerArgs.dateFormat = $.datepicker.ISO_8601;
3249                    }
3250                }
3251
3252                if (jElement.prop('type') !== 'date') 
3253                {
3254                    jElement.datepicker(lPickerArgs);
3255                }
3256
3257                if (!FBUtilities.IsParameterUndefinedOrEmpty(self.options.OnChangeCallback))
3258                {
3259                    jElement.change(function (aEvent)
3260                    {
3261                        self.options.OnChangeCallback(aEvent);
3262                    });
3263                }
3264
3265                lParentElement = jElement.parent();
3266                lParentElement.append('<span class="' + self.options.ClearInputButtonClass + '">' + self.options.ClearInputButtonContent + '</span>');
3267                lClearInputButton = lParentElement.find('.' + self.options.ClearInputButtonClass);
3268                lClearInputButton.attr('role', 'button');
3269                lClearInputButton.attr('tabindex', '0');
3270                lClearInputButton.attr('aria-controls', jElement.attr('id'));
3271                if (!FBUtilities.IsParameterUndefinedOrEmpty(self.options.ClearInputButtonLabel))
3272                {
3273                    lClearInputButton.attr('aria-label', self.options.ClearInputButtonLabel);
3274                }
3275                lClearInputButton.click(
3276                    function (aEvent) 
3277                    {
3278                        aEvent.preventDefault();
3279                        jElement.val('');
3280                        return false;
3281                    }
3282                );
3283            }
3284        });
3285}(jQuery));
3286
3287///////////////////////////////////////////////////////////////////////////////
3288// Expandable Content Sections
3289///////////////////////////////////////////////////////////////////////////////
3290
3291function FBExpando()
3292{
3293    return undefined;
3294}
3295
3296$(function () 
3297{
3298
3299    function _ExpandingContent_OpenWorker(container)
3300    {
3301        if (container.hasClass('fbcms_ec_collapsed'))
3302        {
3303            container.removeClass("fbcms_ec_collapsed");
3304            $(container).find('.fbcms_ec_title').attr("aria-expanded", true);
3305            $(container).find('.fbcms_ec_titleicon').attr("aria-pressed", true);
3306            let masons = container.find('.fbcms_ec_title ~ *.masonry, .fbcms_ec_title ~ .fbcms_pgc_gallery > .masonry');
3307            if (masons.length !== 0)
3308            {
3309                masons.masonry("layout");
3310            }
3311        }
3312    }
3313
3314    function _ExpandingContent_CloseWorker(container)
3315    {
3316        if (!container.hasClass('fbcms_ec_collapsed'))
3317        {
3318            container.addClass("fbcms_ec_collapsed");
3319            $(container).find('.fbcms_ec_title').attr("aria-expanded", false);
3320            $(container).find('.fbcms_ec_titleicon').attr("aria-pressed", false);
3321        }
3322    }
3323
3324    function _ExpandingContent_ToggleWorker(container)
3325    {
3326        if (!container.hasClass('fbcms_ec_collapsed'))
3327        {
3328            _ExpandingContent_CloseWorker(container);
3329        }
3330        else
3331        {
3332            _ExpandingContent_OpenWorker(container);
3333        }
3334    }
3335
3336    function toggleExpandingContent(event)
3337    {
3338        let parent = $(event.target).parents('.fbcms_ec');
3339        _ExpandingContent_ToggleWorker(parent);
3340    }
3341
3342    FBExpando.toggleExpandingContent = toggleExpandingContent
3343
3344    function _determineExpandableContainerToOpen()
3345    {
3346        let hash = $(location).attr('hash');
3347        let result = undefined;
3348        if (hash && hash !== '' && hash !== '#')
3349        {
3350            let name = hash.substring(1);
3351            let targetedBlock = null;
3352            try
3353            {
3354                targetedBlock = $(`.fbcms_content#${name}`);
3355            }
3356            catch (ignore)
3357            {
3358            }
3359
3360            if (targetedBlock !== null)
3361            {
3362                if (targetedBlock.hasClass('fbcms_ec'))
3363                {
3364                    result = targetedBlock;
3365                }
3366            }
3367        }
3368        return result;
3369    }
3370
3371    $(document).on('click', '.fbcms_ec .fbcms_ec_title', function (event)
3372    {
3373        event.preventDefault();
3374        toggleExpandingContent(event);
3375        return false;
3376    });
3377
3378    function _handleHome(event)
3379    {
3380        let startId = $(event.target).parent('.fbcms_ec').siblings('.fbcms_ec').first().attr('aria-labelledby');
3381        if (startId !== undefined)
3382        {
3383            setTimeout(function ()
3384            {
3385                $(`#${startId}`).get(0).focus();
3386            }, 100);
3387        }
3388    }
3389
3390    function _handleEnd(event)
3391    {
3392        let endId = $(event.target).parent('.fbcms_ec').siblings('.fbcms_ec').last().attr('aria-labelledby');
3393        if (endId !== undefined)
3394        {
3395            setTimeout(function ()
3396            {
3397                $(`#${endId}`).get(0).focus();
3398            }, 100);
3399        }
3400    }
3401
3402    function _handlePrevious(event)
3403    {
3404        let previousId = $(event.target).parent('.fbcms_ec').prev('.fbcms_ec').attr('aria-labelledby');
3405        if (previousId === undefined)
3406        {
3407            _handleEnd(event);
3408        }
3409        else
3410        {
3411            setTimeout(function ()
3412            {
3413                $(`#${previousId}`).get(0).focus();
3414            }, 100);
3415        }
3416    }
3417
3418    function _handleNext(event)
3419    {
3420        let nextId = $(event.target).parent('.fbcms_ec').next('.fbcms_ec').attr('aria-labelledby');
3421
3422        if (nextId === undefined)
3423        {
3424            _handleHome(event);
3425        }
3426        else
3427        {
3428            setTimeout(function ()
3429            {
3430                $(`#${nextId}`).get(0).focus();
3431            }, 100);
3432        }
3433    }
3434
3435    $(document).on('keydown', '.fbcms_ec .fbcms_ec_title', function (event)
3436    {
3437        if (event.altKey || event.shiftKey || event.ctrlKey)
3438        {
3439            return;
3440        }
3441        else if (event.keyCode === 13 || event.keyCode === 32)
3442        {
3443            toggleExpandingContent(event);
3444        }
3445        else if (event.keyCode === 36)
3446        {
3447            _handleHome(event);
3448        }
3449        else if (event.keyCode === 38)
3450        {
3451            _handlePrevious(event);
3452        }
3453        else if (event.keyCode === 40)
3454        {
3455            _handleNext(event);
3456        }
3457        else if (event.keyCode === 35)
3458        {
3459            _handleEnd(event);
3460        }
3461        else
3462        {
3463            return;
3464        }
3465
3466        event.preventDefault();
3467        event.stopPropagation();
3468        return false;
3469    });
3470
3471    //webkit hash change fix.
3472    //https://www.nczonline.net/blog/2013/01/15/fixing-skip-to-content-links/
3473    window.addEventListener(
3474        "hashchange",
3475        function (/*event*/) 
3476        {
3477            let element = document.getElementById(location.hash.substring(1));
3478            if (element) 
3479            {
3480                if (!/^(?:a|select|input|button|textarea)$/i.test(element.tagName))
3481                {
3482                    element.tabIndex = -1;
3483                }
3484
3485                element.focus();
3486            }
3487
3488            let jMyExpandable = _determineExpandableContainerToOpen();
3489            if (jMyExpandable !== undefined)
3490            {
3491                _ExpandingContent_OpenWorker(jMyExpandable);
3492            }
3493
3494            let jFixedHeader = $('body .navbar-fixed-top');
3495            if (jFixedHeader.length !== 0)
3496            {
3497                setTimeout(function ()
3498                {
3499                    window.scrollTo(window.scrollX, window.scrollY - jFixedHeader.height());
3500                }, 1);
3501            }
3502        },
3503        false
3504    );
3505
3506    // INITIALIZE EXPANDABLE CONTENT //
3507
3508    let jExpandable = _determineExpandableContainerToOpen();
3509    let jExpandableContentAreas = $('.fbcms_ec').not(jExpandable);
3510
3511    if (jExpandableContentAreas.length !== 0)
3512    {
3513        $.each(
3514            jExpandableContentAreas,
3515            function (_index, item)
3516            {
3517                if (!$(item).hasClass('fbcms_ec_collapsed'))
3518                {
3519                    if (jExpandable !== undefined)
3520                    {
3521                        $('html,body').scrollTop(jExpandable.offset().top);
3522                        jExpandable.removeClass("fbcms_ec_collapsed");
3523                    }
3524                }
3525            }
3526        );
3527    }
3528
3529    $('.fbcms_ec .fbcms_ec_title').next().attr('tabindex', 0);
3530
3531    if (jExpandable !== undefined)
3532    {
3533        if (jExpandable.hasClass('fbcms_ec_collapsed'))
3534        {
3535            jExpandable.removeClass("fbcms_ec_collapsed");
3536        }
3537        $('html,body').scrollTop(jExpandable.offset().top);
3538    }
3539});
3540
3541///////////////////////////////////////////////////////////////////////////////
3542// VP-Accordion
3543///////////////////////////////////////////////////////////////////////////////
3544
3545function VPAccordion()
3546{
3547    return undefined;
3548}
3549
3550$(function () 
3551{
3552    function _handleToggleEvent(event)
3553    {
3554        let $item = $(event.target).closest('.vp-accordion-item');
3555        let $toggler = $item.children(".vp-accordion-header").children(".vp-accordion-toggler");
3556        let $siblings = $item.siblings('.vp-accordion-item').not(`.vp-accordion-expanded-always`);
3557
3558        let wasExpanded = $item.hasClass('vp-accordion-expanded');
3559
3560        $siblings.removeClass('vp-accordion-expanded');
3561        $siblings.children(".vp-accordion-header").children(".vp-accordion-toggler").prop("ariaExpanded", false);
3562
3563        if (wasExpanded)
3564        {
3565            $item.removeClass('vp-accordion-expanded');
3566            $toggler.prop("ariaExpanded", false);
3567        }
3568        else
3569        {
3570            $item.addClass('vp-accordion-expanded');
3571            $toggler.prop("ariaExpanded", true);
3572        }
3573    }
3574
3575    function _initialize(selector)
3576    {
3577        $(selector).filter('.vp-accordion').each(function ()
3578        {
3579            $(this)
3580                .children('.vp-accordion-item')
3581                .not(`.vp-accordion-expanded-always`)
3582                .children('.vp-accordion-header')
3583                .children('.vp-accordion-toggler')
3584                .on('click', _handleToggleEvent);
3585        });
3586    }
3587
3588    VPAccordion.initialize = _initialize;
3589
3590    _initialize(".vp-accordion");
3591
3592});
3593///////////////////////////////////////////////////////////////////////////////
3594// FBRSearchBox - Foxbright Responsive Search Box
3595///////////////////////////////////////////////////////////////////////////////
3596
3597(function ($)
3598{
3599    /*
3600        FBRSearchBox - Foxbright Responsive Search Box
3601        
3602        Modifies the search form to use the submit button as an "opener" when the viewport 
3603        width is below a given threshold.  An HTML class will placed on the form when opened, 
3604        styling transitions can be used to animate a change.  Once the form is open, the 
3605        submit button will function as a normal submit button.  At or above the viewport 
3606        width threshold, the button will act normally.
3607        
3608        Options:
3609        
3610        OpenerAsSubmitterOnDesktop  default: true
3611                The opener button should behave as a normal submit button at or above 
3612                the DesktopWidthMinimum.  If true, DesktopWidthMinimum must be set.
3613        DesktopWidthMinimum         default: 768
3614                Size, in pixels, that normal behavior will be resumed.  Required if 
3615                OpenerAsSubmitterOnDesktop is set to true.
3616        FormActivatedClass          default: 'opened'
3617                Required. Class to add to the form to indicate/activate the open state.
3618        SubmitButtonClass           default: 'submit'
3619                Required. Class to be used to select the submit button(s) *inside* the form element.
3620        InputFieldClass             default: 'search-input'
3621                Required. Class to be used to select the search-input(s) *inside* the form element.
3622        
3623        Methods:
3624        
3625        The widget is fully functional on its own, but can be remotely activated with the following 
3626        methods.
3627        
3628        ActivateSearch
3629                Called to open the form for searching.
3630        DeactivateSearch
3631                Called to close the form.
3632        
3633        Examples:
3634        
3635        Since the function is built as a jQuery Widget, simply call the functions on the 
3636        form element directly.
3637        
3638        $('.search-form').FBRSearchBox();
3639        
3640        $('.search-form').FBRSearchBox({ FormActivatedClass : 'my_open_class' });
3641    */
3642    $.widget("ui.FBRSearchBox",
3643        {
3644            options:
3645            {
3646                OpenerAsSubmitterOnDesktop: true
3647                , DesktopWidthMinimum: 768
3648                , FormActivatedClass: 'opened'
3649                , SubmitButtonClass: 'submit'
3650                , InputFieldClass: 'search-input'
3651            },
3652
3653            _destroy: function ()
3654            {
3655                return undefined;
3656            },
3657
3658            _init: function ()
3659            {
3660                return undefined;
3661            },
3662
3663            _create: function ()
3664            {
3665                var self = this;
3666                var jElement = $(self.element);
3667
3668                //check options.
3669                if (FBUtilities.IsStringEmptyOrWhitespace(self.options.FormActivatedClass)
3670                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.SubmitButtonClass)
3671                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.InputFieldClass))
3672                {
3673                    FBUtilities.LogMessage('FBRSearchBox - required settings null or empty.');
3674                    return;
3675                }
3676
3677                if (self.options.OpenerAsSubmitterOnDesktop === true
3678                    && (FBUtilities.GetIntOrNull(self.options.DesktopWidthMinimum) === null
3679                        || FBUtilities.GetIntOrNull(self.options.DesktopWidthMinimum) <= 0))
3680                {
3681                    FBUtilities.LogMessage('FBRSearchBox - desktop threshold not set as requested.');
3682                    return;
3683                }
3684
3685                //set events
3686                jElement.find(`.${self.options.SubmitButtonClass}`)
3687                    .on('click', function (aEvent) 
3688                    {
3689                        aEvent.preventDefault();
3690                        self._ClickOpener();
3691                        return false;
3692                    });
3693
3694                jElement.find(`.${self.options.InputFieldClass}`)
3695                    .on('keypress', function (aEvent) 
3696                    {
3697                        if (aEvent.which === 13)
3698                        {
3699                            jElement.submit();
3700                            return false;
3701                        }
3702                    })
3703                    .on('keydown', function (aEvent) 
3704                    {
3705                        if (aEvent.which === 27)
3706                        {
3707                            self.DeactivateSearch();
3708                            jElement.find(`.${self.options.InputFieldClass}`).val('')
3709                            jElement.find(`.${self.options.SubmitButtonClass}`).focus();
3710                            return false;
3711                        }
3712                    });
3713
3714                jElement.on('focusout', function (aEvent)
3715                {
3716                    setTimeout(function ()
3717                    {
3718                        if ($(self.element).has($(document.activeElement)).length == 0)
3719                        {
3720                            self.DeactivateSearch();
3721                            $(self.element).find(`.${self.options.InputFieldClass}`).val('')
3722                        }
3723                    }, 0);
3724
3725                });
3726
3727                $(window).on('resize', function (aEvent)
3728                {
3729                    self._OnResize();
3730                });
3731                self._OnResize();
3732            },
3733
3734            _OnResize: function ()
3735            {
3736                var self = this;
3737                var jElement = $(self.element);
3738
3739                var lViewportWidth = FBUtilities.GetIntOrNull($("body").innerWidth());
3740
3741                if (lViewportWidth < self.options.DesktopWidthMinimum)
3742                {
3743                    jElement.find(`.${self.options.InputFieldClass}`).attr('tabindex', "-1");
3744                }
3745                else
3746                {
3747                    jElement.find(`.${self.options.InputFieldClass}`).removeAttr('tabindex');
3748                }
3749            },
3750
3751            _ClickOpener: function ()
3752            {
3753                var self = this;
3754                var jElement = $(self.element);
3755                var lViewportWidth = FBUtilities.GetIntOrNull($("body").innerWidth());
3756
3757                if (lViewportWidth >= self.options.DesktopWidthMinimum)
3758                {
3759                    jElement.submit();
3760                }
3761                else if (false === jElement.is(`.${self.options.FormActivatedClass}`))
3762                {
3763                    self.ActivateSearch();
3764                }
3765                else if (false === FBUtilities.IsStringEmptyOrWhitespace(jElement.find(`.${self.options.InputFieldClass}`).val()))
3766                {
3767                    jElement.submit();
3768                }
3769                else
3770                {
3771                    self.DeactivateSearch();
3772                }
3773            },
3774
3775            ActivateSearch: function ()
3776            {
3777                let jElement = $(this.element);
3778                jElement.addClass(this.options.FormActivatedClass);
3779                jElement.find(`.${this.options.InputFieldClass}`).focus();
3780            },
3781
3782            DeactivateSearch: function ()
3783            {
3784                $(this.element).removeClass(this.options.FormActivatedClass);
3785            }
3786        });
3787}(jQuery));
3788
3789///////////////////////////////////////////////////////////////////////////////
3790// FBRSlideOut - Foxbright Responsive Menu Slideout
3791///////////////////////////////////////////////////////////////////////////////
3792
3793(function ($)
3794{
3795    /*
3796        FBRSlideOut - Foxbright Responsive Menu Slideout
3797        
3798        Javascript controller for a responsive menu slideout.
3799        Options can create buttons for activation and place content in them.
3800    	
3801        The slider element is checked for display mode attributes.  If either 
3802        is not found, is found to be blank, or is found not to be "none" or 
3803        one of the key values present in DisplayModeClasses, the Compatibility 
3804        mode is activated.  All options listed below with a compatibility 
3805        value will be overwritten with that value in compatibility mode.
3806        All classes are present in compatibility mode unless otherwise noted.
3807        
3808        Options:
3809        
3810        SliderClass                         default: 'nav-slider'; compatibility: 'nav_slider'
3811                Required.  The class that should be added to the selected 
3812                element for CSS styling and transistions.
3813            	
3814        SlideoutClass                       default: 'fbcms_rslideout'
3815                Required.  The class that will be added upon initialization.
3816                Change at your peril.
3817                
3818        LinerClass                          default: 'slide-liner'; compatibility: 'slide_liner'
3819                Required.  The class to be put on the element that will wrap all content 
3820                inside the containing element.
3821            	
3822        SliderOpenClass                     default: 'slide-open'; compatibility: 'slideropen'
3823                Required.  The class to indicate the slider is open.
3824            	
3825        SliderOpenContainerSelector         default: 'body'
3826                A selector to use to place a class on indicating the slider 
3827                details.
3828            	
3829        SliderContainerActiveClass			default: 'nav-slider-active'
3830                A class to add to the container to indicate that there is a 
3831                working slider in place.
3832                Not present in compatibility mode.
3833            	
3834        SliderContainerInactiveClass		default: 'nav-slider-deactivated'
3835                A class to add to the container to indicate that the slider is 
3836                non-working.
3837                Not present in compatibility mode.
3838            	
3839        SliderOpenContainerClass            default: 'nav-slider-open'; compatibility: 'navslideropen'
3840                A class to place on the element(s) found by 
3841                SliderOpenContainerSelector.
3842            	
3843        AddSliderButton                     default: true
3844                True to add a slider button for opening the slider.  If set to 
3845                true, SliderButtonClass must be set.
3846            	
3847        SliderButtonClass                   default: 'nav-slider-button'; compatibility: 'slider-opener'
3848                Required if AddSliderButton is set to true.  The class to be 
3849                added to the slider opener/closer button(s) created.
3850            	
3851        SliderButtonActivatedClass          default: 'activated'
3852                A class to be added to the slider opener/closer button(s) when 
3853                activated (the slider open).
3854            	
3855        SliderButtonContent                 default: '<span></span><span class="screen-reader-text">Website Menu</span>'
3856                Content to be displayed in the slider opener/closer button(s).
3857            	
3858        SliderButtonExtraClasses            default: 'slider-button'; compatibility: 'changerbutton'
3859                Extra classes to be added to the slider opener/closer 
3860                button(s).
3861            	
3862        AdditionalButtonContainerSelector   default: null
3863                An additional container selector to add a slider opener/closer 
3864                button(s) to.
3865            	
3866        AdditionalSliderButtonClass         default: 'slider-button-secondary'; compatibility: 'secondary-opener'
3867                An additional class to add to additional slider opener/closer 
3868                button(s).
3869            	
3870        AdditionalSliderButtonContent       default: '<span></span><span class="screen-reader-text">Website Menu</span>'
3871                Content to be displayed in the additional slider opener/closer 
3872                button(s).
3873            	
3874        AdditionalSliderButtonExtraClasses  default: 'slider-button'; compatibility: 'changerbutton'
3875                Extra classes to be added to the additional slider 
3876                opener/closer button(s).
3877            	
3878        DisplayModeThreshold				default: 768
3879                Threshold at which to switch between display modes.
3880            	
3881        DisplayModeClasses					default: { 'slideout': 'navslideout', 'overlay': 'navoverlay' }
3882                List of display modes with classes to indicate their use.
3883                Not present in compatibility mode.
3884            	
3885        Methods:
3886        
3887        The widget is fully functional on its own, but can be remotely activated with the following 
3888        methods.
3889        
3890        ToggleSlider
3891                Called to toggle the slider open or closed.
3892        OpenSlider
3893                Called to open the slider (or do nothing if the slider is already open).
3894        CloseSlider
3895                Called to close the slider (or do nothing if the slider is already closed).
3896        
3897        Examples:
3898        
3899        Since the function is built as a jQuery Widget, simply call the functions on the 
3900        container element directly.
3901        
3902        $('.header_navigation').FBRSlideOut();
3903        
3904        $('.header_navigation').FBRSlideOut(
3905            {
3906                AdditionalButtonContainerSelector : '#header'
3907            }
3908        );
3909    */
3910    $.widget("ui.FBRSlideOut",
3911        {
3912            options:
3913            {
3914                SliderClass: 'nav-slider' //'nav_slider'
3915                , SlideoutClass: 'fbcms_rslideout' 
3916                , LinerClass: 'slide-liner' //'slide_liner'
3917                , SliderOpenClass: 'slide-open' //'slideropen'
3918                , SliderOpenContainerSelector: 'body'
3919                , SliderContainerActiveClass: 'nav-slider-active'
3920                , SliderContainerInactiveClass: 'nav-slider-deactivated'
3921                , SliderOpenContainerClass: 'nav-slider-open' //'navslideropen'
3922                , AddSliderButton: true
3923                , SliderButtonClass: 'nav-slider-button' //'slider-opener'
3924                , SliderButtonActivatedClass: 'activated'
3925                , SliderButtonContent: '<span></span><span class="screen-reader-text">Close Website Menu</span>'
3926                , SliderButtonExtraClasses: 'slider-button' //'changerbutton'
3927                , AddAdditionalButton: true
3928                , AdditionalButtonContainerSelector: null
3929                , AdditionalSliderButtonClass: 'slider-button-secondary' //'secondary-opener'
3930                , AdditionalSliderButtonId: 'navigation-slider-opener-button'
3931                , AdditionalSliderButtonContent: '<span></span><span class="screen-reader-text">Open Website Menu</span>'
3932                , AdditionalSliderButtonExtraClasses: 'slider-button' //'changerbutton'
3933                , DisplayModeThreshold: 768
3934                , DisplayModeClasses: { 'slider': 'navslideout', 'overlay': 'navoverlay' }
3935                , SkipToNavigationLinkId: 'skip-to-navigation'
3936            },
3937
3938            _prevFocusItem: null,
3939            _myExternalOpenerButtonItem: null,
3940
3941            _prevTabbableItem: null,
3942            _myFirstTabbableItem: null,
3943            _myLastTabbableItem: null,
3944            _nextTabbableItem: null,
3945            _lastBelowTheThreshold: false,
3946            _DesktopDisplayClass: null,
3947            _DesktopUseTabbingLoop: null,
3948            _MobileDisplayClass: false,
3949            _MobileUseTabbingLoop: false,
3950
3951            _destroy: function ()
3952            {
3953                return undefined;
3954            },
3955
3956            _init: function ()
3957            {
3958                return undefined;
3959            },
3960
3961            _create: function ()
3962            {
3963                var self = this;
3964                var jElement = $(self.element);
3965
3966                if ($('body.page_bodyonly').length === 1)
3967                {
3968                    jElement.remove();
3969                    return;
3970                }
3971
3972                if (FBUtilities.IsStringEmptyOrWhitespace(self.options.SliderClass)
3973                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.SlideoutClass)
3974                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.LinerClass)
3975                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.SliderOpenClass))
3976                {
3977                    FBUtilities.LogMessage('FBRSlideOut - required settings null or empty.');
3978                    return;
3979                }
3980
3981                if (self.options.AddSliderButton === true
3982                    && FBUtilities.IsStringEmptyOrWhitespace(self.options.SliderButtonClass))
3983                {
3984                    FBUtilities.LogMessage('FBRSlideOut - slider button setting not set as requested.');
3985                    return;
3986                }
3987
3988                var lMobileDisplayMode = jElement.data('slider-mobiledisplaymode');
3989                var lDesktopDisplayMode = jElement.data('slider-
3989desktopdisplaymode');
3990                if (FBUtilities.IsParameterUndefinedOrEmpty(lDesktopDisplayMode) === false
3991                    && FBUtilities.IsParameterUndefinedOrEmpty(self.options.DisplayModeClasses[lDesktopDisplayMode]) === false)
3992                {
3993                    self._DesktopDisplayClass = self.options.DisplayModeClasses[lDesktopDisplayMode];
3994                    self._DesktopUseTabbingLoop = true;
3995                }
3996                else if (lDesktopDisplayMode === "none")
3997                {
3998                    self._DesktopDisplayClass = 'none';
3999                }
4000                if (FBUtilities.IsParameterUndefinedOrEmpty(lMobileDisplayMode) === false
4001                    && FBUtilities.IsParameterUndefinedOrEmpty(self.options.DisplayModeClasses[lMobileDisplayMode]) === false)
4002                {
4003                    self._MobileDisplayClass = self.options.DisplayModeClasses[lMobileDisplayMode];
4004                    self._MobileUseTabbingLoop = true;
4005                }
4006                else if (lMobileDisplayMode === "none")
4007                {
4008                    self._MobileDisplayClass = 'none';
4009                }
4010
4011                if (FBUtilities.IsParameterUndefinedOrNull(self._DesktopDisplayClass)
4012                    || FBUtilities.IsParameterUndefinedOrNull(self._MobileDisplayClass))
4013                {
4014                    // trigger compatibility mode...
4015                    self.options.SliderClass = 'nav_slider';
4016                    self.options.LinerClass = 'slide_liner';
4017                    self.options.SliderOpenClass = 'slideropen';
4018                    self.options.SliderOpenContainerClass = 'navslideropen';
4019                    self.options.SliderButtonClass = 'slider-opener';
4020                    self.options.SliderButtonExtraClasses = 'changerbutton';
4021                    self.options.AdditionalSliderButtonClass = 'secondary-opener';
4022                    self.options.AdditionalSliderButtonExtraClasses = 'changerbutton';
4023                }
4024
4025                //window.console.log('FBRSlideOut - DesktopMode: "' + self._DesktopDisplayClass + '"; MobileMode: "' + self._MobileDisplayClass + '"');
4026
4027                if (self.options.AddSliderButton === true)
4028                {
4029                    jElement.prepend('<a class="' + self.options.SliderButtonClass + '" href="#"></a>');
4030
4031                    let lMenuButton = jElement.children('.' + self.options.SliderButtonClass);
4032                    if (FBUtilities.IsStringEmptyOrWhitespace(self.options.SliderButtonExtraClasses) === false)
4033                    {
4034                        lMenuButton.addClass(self.options.SliderButtonExtraClasses);
4035                    }
4036                    if (FBUtilities.IsStringEmptyOrWhitespace(self.options.SliderButtonContent) === false)
4037                    {
4038                        lMenuButton.html(self.options.SliderButtonContent);
4039                    }
4040
4041                    if (self.options.AddAdditionalButton === true)
4042                    {
4043                        let container = $(self.options.AdditionalButtonContainerSelector);
4044                        let haveContainer = (container.length > 0);
4045                        let html = `<a id="${self.options.AdditionalSliderButtonId}" class="${self.options.SliderButtonClass}" tabindex="0" href="javascript:"></a>`;
4046                        let button;
4047                        if (haveContainer)
4048                        {
4049                            let firstChild = container.children().first();
4050                            if (firstChild.length !== 0 && firstChild.attr('id') === "accessibility")
4051                            {
4052                                firstChild.after(html);
4053                            }
4054                            else
4055                            {
4056                                container.prepend(html);
4057                            }
4058                            button = container.children(`.${self.options.SliderButtonClass}`);
4059                        }
4060                        else
4061                        {
4062                            button = $(html);
4063                        }
4064
4065                        if (!FBUtilities.IsStringEmptyOrWhitespace(self.options.AdditionalSliderButtonClass))
4066                        {
4067                            button.addClass(self.options.AdditionalSliderButtonClass);
4068                        }
4069
4070                        if (!FBUtilities.IsStringEmptyOrWhitespace(self.options.AdditionalSliderButtonExtraClasse
4070s))
4071                        {
4072                            button.addClass(self.options.AdditionalSliderButtonExtraClasses);
4073                        }
4074
4075                        if (!FBUtilities.IsStringEmptyOrWhitespace(self.options.AdditionalSliderButtonContent))
4076                        {
4077                            button.html(self.options.AdditionalSliderButtonContent);
4078                        }
4079
4080                        if (!haveContainer)
4081                        {
4082                            jElement.before(button);
4083                        }
4084                    }
4085                }
4086
4087                if (self.options.AddSliderButton === true)
4088                {
4089                    jElement.find(`.${self.options.SliderButtonClass}`).on(
4090                        'click',
4091                        function (_event) 
4092                        {
4093                            self.CloseSlider();
4094                            return false;
4095                        }
4096                    );
4097
4098                    if (self.options.AddAdditionalButton === true)
4099                    {
4100                        self._myExternalOpenerButtonItem = $(`.${self.options.AdditionalSliderButtonClass}`);
4101
4102                        $(`.${self.options.AdditionalSliderButtonClass}`).on(
4103                            'click',
4104                            function (_event) 
4105                            {
4106                                self.ToggleSlider($(this).closest(`.${self.options.AdditionalSliderButtonClass}`));
4107                                return false;
4108                            }
4109                        );
4110                    }
4111                }
4112
4113                jElement.on("keydown", function (e)
4114                {
4115                    var lViewportWidth = FBUtilities.GetIntOrNull($("body").innerWidth());
4116                    if ($(self.options.SliderOpenContainerSelector).hasClass(self.options.SliderContainerInactiveClass))
4117                    {
4118                        return true;
4119                    }
4120
4121                    if (e.key === "Escape") // Escape
4122                    {
4123
4124                        if (jElement.hasClass(self.options.SliderOpenClass))
4125                        {
4126                            e.preventDefault();
4127                            e.stopPropagation();
4128                            self.CloseSlider();
4129                            return false;
4130                        }
4131                    }
4132
4133                    return true;
4134                });
4135
4136                // skip this if we're in compatibility mode.
4137                if (FBUtilities.IsParameterUndefinedOrNull(self._DesktopDisplayClass) === false
4138                    && FBUtilities.IsParameterUndefinedOrNull(self._MobileDisplayClass) === false)
4139                {
4140                    if (self._DesktopDisplayClass !== self._MobileDisplayClass)
4141                    {
4142                        $(window).resize(function ()
4143                        {
4144                            self._updateOnPageResizeOrLoad();
4145                        });
4146                    }
4147                    self._updateOnPageResizeOrLoad();
4148                }
4149
4150                // reconfigure jump to navigation link.                
4151                $(`#${self.options.SkipToNavigationLinkId}`).on('click', function (e)
4152                {
4153                    let viewportWidth = FBUtilities.GetIntOrNull(window.innerWidth);
4154                    if ((viewportWidth < self.options.DisplayModeThreshold && self._MobileUseTabbingLoop)
4155                        || (viewportWidth >= self.options.DisplayModeThreshold && self._DesktopUseTabbingLoop))
4156                    {
4157                        e.preventDefault();
4158                        document.location = `#${self.options.AdditionalSliderButtonId}`;
4159                        return false;
4160                    }
4161                });
4162
4163                jElement.addClass(self.options.SliderClass);
4164                jElement.wrapInner('<div class="' + self.options.LinerClass + '"></div>');
4165                $(self.options.SliderOpenContainerSelector).addClass(self.options.SliderContainerClass);
4166
4167
4168                self._lastBelowTheThreshold = self._isBelowThresehold();
4169
4170                $(window).on("resize", function ()
4171                {
4172                    let isBelow = self._isBelowThresehold();
4173
4174                    if (isBelow != self._lastBelowTheThreshold)
4175                    {
4176                        self._lastBelowTheThreshold = isBelow;
4177                        if (self._DesktopUseTabbingLoop !== self._MobileUseTabbingLoop)
4178                        {
4179                            self._updateTabbability();
4180                        }
4181                    }
4182                });
4183
4184                jElement.on("fb:changevisibility", function (aEvent)
4185                {
4186                    self._updateTabbability();
4187                    console.log('changevisibility caught: "' + aEvent.detail.name + '"');// TODO: seems to trigger on every TAB through the menu...
4188                });
4189
4190                setTimeout(function ()
4191                {
4192                    self._updateTabbability();
4193                }, 1)
4194
4195                jElement.addClass(self.options.SlideoutClass);
4196            },
4197
4198            _updateOnPageResizeOrLoad()
4199            {
4200                var self = this;
4201
4202                if (FBUtilities.IsParameterUndefinedOrNull(self._DesktopDisplayClass)
4203                    || FBUtilities.IsParameterUndefinedOrNull(self._MobileDisplayClass))
4204                {
4205                    // drop out if this is somehow called in compatibility mode.
4206                    return;
4207                }
4208
4209                let viewportWidth = FBUtilities.GetIntOrNull(window.innerWidth);
4210                if (viewportWidth >= self.options.DisplayModeThreshold)
4211                {
4212                    self._updateContainerClass(self._MobileDisplayClass, self._DesktopDisplayClass);
4213                }
4214                else
4215                {
4216                    self._updateContainerClass(self._DesktopDisplayClass, self._MobileDisplayClass);
4217                }
4218            },
4219
4220            _updateContainerClass: function (aRemoveClass, aAddClass)
4221            {
4222                var self = this;
4223
4224                var lContainerElement = $(self.options.SliderOpenContainerSelector);
4225
4226                // does it start inactive?
4227                var lStartInactive = (aRemoveClass === "none");
4228                // should it finish inactive?
4229                var lFinishInactive = (aAddClass === "none");
4230
4231                // (in)active classes...
4232                // if it doesn't have the class it should for the finish... updated it.
4233                if (lFinishInactive === true
4234                    && lContainerElement.hasClass(self.options.SliderContainerInactiveClass) === false)
4235                {
4236                    lContainerElement.removeClass(self.options.SliderContainerActiveClass).addClass(self.options.SliderContainerInactiveClass)
4237                }
4238                else if (lFinishInactive === false
4239                    && lContainerElement.hasClass(self.options.SliderContainerActiveClass) === false)
4240                {
4241                    lContainerElement.removeClass(self.options.SliderContainerInactiveClass).addClass(self.options.SliderContainerActiveClass)
4242                }
4243                // otherwise no changes are necessary...
4244
4245                // is there a class to remove?
4246                if (lStartInactive === false)
4247                {
4248                    lContainerElement.removeClass(aRemoveClass);
4249                }
4250                // is there a class to add?
4251                if (lFinishInactive === false)
4252                {
4253                    lContainerElement.addClass(aAddClass);
4254                }
4255            },
4256            _isBelowThresehold: function ()
4257            {
4258                return FBUtilities.GetIntOrNull(window.innerWidth) < this.options.DisplayModeThreshold;
4259            },
4260            _isSliderActive: function ()
4261            {
4262                if (FBUtilities.IsParameterUndefinedOrNull(this._DesktopDisplayClass)
4263                    || FBUtilities.IsParameterUndefinedOrNull(this._MobileDisplayClass))
4264                {
4265                    return this._isBelowThresehold();
4266                }
4267                return (false === $(this.options.SliderOpenContainerSelector).hasClass(this.options.SliderContainerInactiveClass));
4268            },
4269
4270            _isSliderOpened: function ()
4271            {
4272                return $(this.element).hasClass(this.options.SliderOpenClass);
4273            },
4274            _doBypass: function ()
4275            {
4276                return this._isSliderActive() && !this._isSliderOpened();
4277            },
4278            _doInternalLoop: function ()
4279            {
4280                return this._isSliderActive() && this._isSliderOpened();
4281            },
4282            _updateTabbability: function ()
4283            {
4284                // all the tabbing events.
4285                var self = this;
4286                var jElement = $(self.element);
4287
4288
4289                if (self._prevTabbableItem !== null)
4290                {
4291                    self._prevTabbableItem.off('keydown');
4292                }
4293
4294                if (self._nextTabbableItem !== null)
4295                {
4296                    self._nextTabbableItem.off('keydown');
4297                }
4298
4299                if (self._myFirstTabbableItem !== null)
4300                {
4301                    self._myFirstTabbableItem.off('keydown');
4302                }
4303
4304                if (self._myFirstTabbableItem !== null)
4305                {
4306                    self._myLastTabbableItem.off('keydown');
4307                }
4308
4309                if (!self._isSliderActive())
4310                {
4311                    return;
4312                }
4313
4314                var lAllTabbableItems = $(':tabbable');
4315
4316                self._myFirstTabbableItem = jElement.find(':tabbable').first();
4317                self._myLastTabbableItem = jElement.find(':tabbable').last();
4318
4319                var lMyFirstItemIndex = parseInt(lAllTabbableItems.index(self._myFirstTabbableItem), 10);
4320                var lPrevItemIndex = lMyFirstItemIndex - 1;
4321                if (lPrevItemIndex < 0)
4322                {
4323                    lPrevItemIndex = lAllTabbableItems.length - 1;
4324                }
4325                self._prevTabbableItem = lAllTabbableItems.eq(lPrevItemIndex);
4326
4327                var lMyLastItemIndex = parseInt(lAllTabbableItems.index(self._myLastTabbableItem), 10);
4328                var lNextItemIndex = lMyLastItemIndex + 1;
4329                if (lNextItemIndex < 0)
4330                {
4331                    lNextItemIndex = 0;
4332                }
4333                self._nextTabbableItem = lAllTabbableItems.eq(lNextItemIndex);
4334
4335                // set up the bypass if the slider is closed...
4336                // before to after.
4337                self._prevTabbableItem.on("keydown", function (e)
4338                {
4339                    let propagate = true;
4340                    if (e.keyCode === 9 && e.shiftKey === false && self._doBypass())
4341                    {
4342                        e.preventDefault();
4343                        e.stopPropagation();
4344                        self._nextTabbableItem.focus();
4345                        propagate = false;
4346                    }
4347                    return propagate;
4348                });
4349                // after to before.
4350                self._nextTabbableItem.on("keydown", function (e)
4351                {
4352                    let propagate = true;
4353                    if (e.keyCode === 9 && e.shiftKey === true && self._doBypass())
4354                    {
4355                        e.preventDefault();
4356                        e.stopPropagation();
4357                        self._prevTabbableItem.focus();
4358                        propagate = false;
4359                    }
4360                    return propagate;
4361                });
4362
4363                // set up the internal loop if the slider is closed...
4364                // bottom to top.
4365                self._myLastTabbableItem.on("keydown", function (e)
4366                {
4367                    let propagate = true;
4368                    if (e.keyCode === 9 && e.shiftKey === false && self._doInternalLoop())
4369                    {
4370                        e.preventDefault();
4371                        e.stopPropagation();
4372                        self._myFirstTabbableItem.focus();
4373                        propagate = false;
4374                    }
4375                    return propagate;
4376                });
4377                // top to bottom.
4378                self._myFirstTabbableItem.on("keydown", function (e)
4379                {
4380                    let propagate = true;
4381                    if (e.keyCode === 9 && e.shiftKey === true && self._doInternalLoop())
4382                    {
4383                        e.preventDefault();
4384                        e.stopPropagation();
4385                        self._myLastTabbableItem.focus();
4386                        propagate = false;
4387                    }
4388                    return propagate;
4389                });
4390            },
4391
4392            ToggleSlider: function (aActivatingElement)
4393            {
4394                var self = this;
4395                var jElement = $(self.element);
4396
4397                // toggle all the classes on the markup.
4398                jElement.toggleClass(self.options.SliderOpenClass);
4399
4400                if (!FBUtilities.IsStringEmptyOrWhitespace(self.options.SliderOpenContainerSelector)
4401                    && !FBUtilities.IsStringEmptyOrWhitespace(self.options.SliderOpenContainerClass))
4402                {
4403                    $(self.options.SliderOpenContainerSelector).toggleClass(self.options.SliderOpenContainerClass);
4404                }
4405
4406                jElement.children(`.${self.options.LinerClass}`).children(`.${self.options.SliderButtonClass}`).toggleClass(self.options.SliderButtonActivatedClass);
4407
4408                // and in the buttons...
4409                if (FBUtilities.IsStringEmptyOrWhitespace(self.options.AdditionalButtonContainerSelector) === false)
4410                {
4411                    let lAdditionalButtonContainers = $(self.options.AdditionalButtonContainerSelector);
4412                    if (lAdditionalButtonContainers.length > 0)
4413                    {
4414                        $.each(
4415                            lAdditionalButtonContainers,
4416                            function (_index, aValue)
4417                            {
4418                                $(aValue).children(`.${self.options.SliderButtonClass}`).toggleClass(self.options.SliderButtonActivatedClass);
4419                            }
4420                        );
4421                    }
4422                }
4423
4424                // based on the open status (class changes)...
4425                if (self.IsSliderOpen() === true)
4426                {
4427                    // if we're now open...
4428                    if (FBUtilities.IsParameterUndefinedOrEmpty(aActivatingElement) === false)
4429                    {
4430                        // if an activated item had been submitted...
4431                        self._prevFocusItem = aActivatingElement;
4432                        // save any submitted activating element for return to focus when closed.
4433                    }
4434                    else
4435                    {
4436                        // if no item has been submitted, clear the variable.
4437                        self._prevFocusItem = null;
4438                    }
4439                    // set focus on our starting item.
4440                    self._GetStartingFocusItem().focus();
4441                }
4442                else
4443                {
4444                    // if we're now closed...
4445                    if (self._prevFocusItem === null)
4446                    {
4447                        // and no activating item was submitted...
4448                        if (self._myExternalOpenerButtonItem !== null)
4449                        {
4450                            // set focus on our external opener button (if any)
4451                            self._myExternalOpenerButtonItem.focus();
4452                        }
4453                        else
4454                        {
4455                            // set focus on the tabbable item immediately previous to the menu.
4456                            self._prevTabbableItem.focus();
4457                        }
4458                    }
4459                    else
4460                    {
4461                        // an activating item was set, set focus there and clear the variable.
4462                        self._prevFocusItem.focus();
4463                        self._prevFocusItem = null;
4464                    }
4465                }
4466            },
4467
4468            OpenSlider: function (aActivatingElement)
4469            {
4470                var self = this,
4471                    jElement = $(self.element),
4472                    lAdditionalButtonContainers;
4473
4474                jElement.addClass(self.options.SliderOpenClass);
4475
4476                if (!FBUtilities.IsStringEmptyOrWhitespace(self.options.SliderOpenContainerSelector)
4477                    && !FBUtilities.IsStringEmptyOrWhitespace(self.options.SliderOpenContainerClass))
4478                {
4479                    $(self.options.SliderOpenContainerSelector).addClass(self.options.SliderOpenContainerClass);
4480                }
4481
4482                jElement.children('.' + self.options.LinerClass).children('.' + self.options.SliderButtonClass).addClass(self.options.SliderButtonActivatedClass);
4483
4484                if (FBUtilities.IsStringEmptyOrWhitespace(self.options.AdditionalButtonContainerSelector) === false)
4485                {
4486                    lAdditionalButtonContainers = $(self.options.AdditionalButtonContainerSelector);
4487                    if (lAdditionalButtonContainers.length > 0)
4488                    {
4489                        $.each(
4490                            lAdditionalButtonContainers,
4491                            function (aIndex, aValue)//ignore jslint
4492                            {
4493                                var lValue = $(aValue);
4494                                lValue.children('.' + self.options.SliderButtonClass).addClass(self.options.SliderButtonActivatedClass);
4495                            }
4496                        );
4497                    }
4498                }
4499
4500                // we're now open...
4501                if (FBUtilities.IsParameterUndefinedOrEmpty(aActivatingElement) === false)
4502                {
4503                    // if an activated item had been submitted...
4504                    self._prevFocusItem = aActivatingElement;
4505                    // save any submitted activating element for return to focus when closed.
4506                }
4507                else
4508                {
4509                    // if no item has been submitted, clear the variable.
4510                    self._prevFocusItem = null;
4511                }
4512                // set focus on our starting item.
4513                self._GetStartingFocusItem().focus();
4514            },
4515
4516            IsSliderOpen: function ()
4517            {
4518                return $(this.element).hasClass(this.options.SliderOpenClass);
4519            },
4520
4521            _GetStartingFocusItem: function ()
4522            {
4523                return $(this.element).find(':tabbable').not('.' + this.options.SliderButtonClass).first();
4524            },
4525
4526            CloseSlider: function ()
4527            {
4528                var self = this,
4529                    jElement = $(self.element),
4530                    lAdditionalButtonContainers;
4531
4532                jElement.removeClass(self.options.SliderOpenClass);
4533
4534                if (!FBUtilities.IsStringEmptyOrWhitespace(self.options.SliderOpenContainerSelector)
4535                    && !FBUtilities.IsStringEmptyOrWhitespace(self.options.SliderOpenContainerClass))
4536                {
4537                    $(self.options.SliderOpenContainerSelector).removeClass(self.options.SliderOpenContainerClass);
4538                }
4539
4540                jElement.children('.' + self.options.LinerClass).children('.' + self.options.SliderButtonClass).removeClass(self.options.SliderButtonActivatedClass);
4541
4542                if (FBUtilities.IsStringEmptyOrWhitespace(self.options.AdditionalButtonContainerSelector) === false)
4543                {
4544                    lAdditionalButtonContainers = $(self.options.AdditionalButtonContainerSelector);
4545                    if (lAdditionalButtonContainers.length > 0)
4546                    {
4547                        $.each(
4548                            lAdditionalButtonContainers,
4549                            function (aIndex, aValue)//ignore jslint
4550                            {
4551                                var lValue = $(aValue);
4552                                lValue.children('.' + self.options.SliderButtonClass).removeClass(self.options.SliderButtonActivatedClass);
4553                            }
4554                        );
4555                    }
4556                }
4557
4558                // we're now closed...
4559                if (self._prevFocusItem === null)
4560                {
4561                    // and no activating item was submitted...
4562                    if (self._myExternalOpenerButtonItem !== null)
4563                    {
4564                        // set focus on our external opener button (if any)
4565                        self._myExternalOpenerButtonItem.focus();
4566                    }
4567                    else
4568                    {
4569                        // set focus on the tabbable item immediately previous to the menu.
4570                        self._prevTabbableItem.focus();
4571                    }
4572                }
4573                else
4574                {
4575                    // an activating item was set, set focus there and clear the variable.
4576                    self._prevFocusItem.focus();
4577                    self._prevFocusItem = null;
4578                }
4579            }
4580
4581        });
4582}(jQuery));
4583
4584
4585///////////////////////////////////////////////////////////////////////////////
4586// FBRMainDropNav - Foxbright Responsive Main Drop Navigation
4587///////////////////////////////////////////////////////////////////////////////
4588
4589(function ($)
4590{
4591    /*
4592        FBRMainDropNav - Foxbright Responsive Main Drop Navigation
4593        
4594        Javascript controller for the responsive main drop menu that changes 
4595        form/functionality below 768 pixels for mobile and tablet touch screen use.
4596        
4597        Options:
4598        
4599        MenuClass                           default: 'fbcms_rmainmenu'
4600                Required.  Class to put in place when the menu is initialized.
4601        MenuOpenClass                       default: 'menuopen'
4602                Required.  Class to put in place when the menu is open.
4603        AddDropOpenerToMenuButton           default: true
4604                True to add a opener button for the whole menu.  Primarily intended to be 
4605                shown in mobile (below 768px viewport width) mode.
4606        MenuButtonSelector                  default: '.menu_title'
4607                Required if AddDropOpenerToMenuButton is true or AllowMenuOpenClose is true 
4608                AND MenuButtonClass is not set.
4609        MenuButtonClass                     default: 'main-opener'
4610                Required if AddDropOpenerToMenuButton is true or AllowMenuOpenClose is true 
4611                AND MenuButtonSelector is not set.
4612        MenuTopLevelContainerClass          default: 'top_level'
4613                Required.  Class to be used to locate the top level container (already in 
4614                markup).
4615        MenuTopLevelItemClass               default: 'top_level_item'
4616                Required.  Class to be used to locate the top level menu items (already in 
4617                markup).
4618        MenuSublistItemClass                default: 'sublist_item'
4619                Required.  Class to be used to locate menu items in sub menus (already in 
4620                markup).
4621        MenuItemActiveClass                 default: 'active'
4622                Required.  Class to be used to identify the currently active menu item 
4623                (already in markup).
4624        DropOpenerClass                     default: 'main-drop-opener'
4625                Required.  Class to be put into the buttons used to open the top level menu 
4626                item sub menus.
4627        OpenDropContent                     default: '<span class="fas fa-plus"></span>'
4628                Content to be shown when a opener button is closed and may be opened.
4629                Whatever content is put into place, the following content is added afterward.
4630                <span class="screen-reader-text">Open {Link Text} Menu Subpages</span>
4631        CloseDropContent                    default: '<span class="fas fa-minus"></span>'
4632                Content to be shown when a opener button is open and may be closed.
4633                Whatever content is put into place, the following content is added afterward.
4634                <span class="screen-reader-text">Close {Link Text} Menu Subpages</span>
4635        MenuLinkClass                       default: 'item_link'
4636                Required.  Class to be used to identify the menu link inside a menu item 
4637                (already in markup).
4638        MenuLinkWrapperClass                default: 'link_pair'
4639                Required.  This is a class to be attached to a new element that will wrap 
4640                the menu links.  Inside, the link will have the opener button added with it 
4641                (if that option is turned on).
4642        MenuDropClass                       default: 'drop'
4643                Required.  Class to be used to identify the sub menu (if any) inside a menu 
4644                item (already in markup).
4645        InitOpenActiveItems                 default: true
4646                True to open active menu items by default when the menu is loaded.  This 
4647                will open the menu items all the way down to the current page.
4648        MenuDefaultOpen                     default: true
4649                True to open the overall menu by default.
4650        HonorDefaultOpenOnActiveOpen        default: false
4651                True to honor the MenuDefaultOpen setting is the InitOpenActiveItems setting 
4652                is true.  False to open if any active items are present and the 
4653                InitOpenActiveItems setting is true, regardless of the MenuDefaultOpen 
4654                setting.
4655        AllowMenuOpenClose                  default: true
4656                True to allow the menu to be opened or closed.
4657        AllowMultipleItemsOpen              default: true
4658                True to allow multiple menu items to be open, false to close others when one 
4659                is opened.
4660        
4661        Methods:
4662        
4663        The widget is fully functional on its own, but can be remotely activated 
4663with the following 
4664        methods.
4665        
4666        ToggleMenu
4667                Called to toggle the menu open or closed.
4668        OpenMenu
4669                Called to open the menu (or do nothing if the menu is already open).
4670        CloseMenu
4671                Called to close the menu (or do nothing if the menu is already closed).
4672        ToggleNavItem
4673                Called to toggle a menu item open or closed.
4674        
4675        Examples:
4676        
4677        Since the function is built as a jQuery Widget, simply call the functions on the 
4678        container element directly.
4679        
4680        $('nav.main-nav').FBRMainDropNav();
4681    */
4682    $.widget("ui.FBRMainDropNav",
4683        {
4684            options:
4685            {
4686                MenuClass: 'fbcms_rmainmenu'
4687                , MenuOpenClass: 'menuopen'
4688                , AddDropOpenerToMenuButton: true
4689                , MenuButtonSelector: '.menu_title'
4690                , MenuButtonClass: 'main-opener'
4691                , MenuTopLevelContainerClass: 'top_level'
4692                , MenuTopLevelItemClass: 'top_level_item'
4693                , MenuTopLevelOpenClass: 'dropfocus'
4694                , MenuSublistItemClass: 'sublist_item'
4695                , MenuItemActiveClass: 'active'
4696                , DropOpenerClass: 'main-drop-opener'
4697                , OpenDropContent: '<span class="fas fa-plus"></span>'
4698                , CloseDropContent: '<span class="fas fa-minus"></span>'
4699                , MenuLinkClass: 'item_link'
4700                , MenuLinkWrapperClass: 'link_pair'
4701                , MenuDropClass: 'drop'
4702                , InitOpenActiveItems: true
4703                , MenuDefaultOpen: true
4704                , HonorDefaultOpenOnActiveOpen: false
4705                , AllowMenuOpenClose: true
4706                , AllowMultipleItemsOpen: true
4707            },
4708
4709            _destroy: function ()
4710            {
4711                return undefined;
4712            },
4713
4714            _init: function ()
4715            {
4716                return undefined;
4717            },
4718
4719            _create: function ()
4720            {
4721                var self = this,
4722                    jElement = $(self.element),
4723                    lMenuButton,
4724                    lButton;
4725
4726                if ($('body.page_bodyonly').length === 1)
4727                {
4728                    jElement.remove();
4729                    return;
4730                }
4731
4732                if (FBUtilities.IsStringEmptyOrWhitespace(self.options.MenuClass)
4733                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.MenuOpenClass)
4734                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.MenuTopLevelContainerClass)
4735                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.MenuTopLevelItemClass)
4736                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.MenuSublistItemClass)
4737                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.MenuItemActiveClass)
4738                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.DropOpenerClass)
4739                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.MenuLinkClass)
4740                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.MenuLinkWrapperClass)
4741                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.MenuDropClass))
4742                {
4743                    FBUtilities.LogMessage('FBRMainDropNav - primary required option null or empty.');
4744                    return;
4745                }
4746
4747                if ((self.options.AllowMenuOpenClose === true
4748                    || self.options.AddDropOpenerToMenuButton === true)
4749                    && (FBUtilities.IsStringEmptyOrWhitespace(self.options.MenuButtonSelector)
4750                        || FBUtilities.IsStringEmptyOrWhitespace(self.options.MenuButtonClass)))
4751                {
4752                    FBUtilities.LogMessage('FBRMainDropNav - menu button settings in disagreement.');
4753                    return;
4754                }
4755
4756
4757                lMenuButton = jElement.find(self.options.MenuButtonSelector);
4758                if (self.options.AllowMenuOpenClose === true
4759                    && lMenuButton.length === 0)
4760                {
4761                    FBUtilities.LogMessage('FBRMainDropNav - menu button not found as requested by settings.');
4762                    return;
4763                }
4764
4765                if (self.options.AllowMenuOpenClose === true)
4766                {
4767                    lMenuButton.addClass(self.options.MenuButtonClass);
4768                    lMenuButton.wrapInner('<span class="menu_title_text"></span>');
4769                    if (self.options.AddDropOpenerToMenuButton === true)
4770                    {
4771                        lMenuButton.append('<a href="#" class="' + self.options.DropOpenerClass + '"></a>');
4772                        lButton = lMenuButton.children('.' + self.options.DropOpenerClass);
4773                        self._setNavItemButtonText(lButton, jElement);
4774                    }
4775                }
4776
4777                $.each(
4778                    jElement.find('.' + self.options.MenuTopLevelItemClass + ',.' + self.options.MenuSublistItemClass)
4779                    , function (aIndex, aValue)//ignore jslint
4780                    {
4781                        var lValue, lLink, lLinkWrapper, lDrop, lMyButton;
4782
4783                        lValue = $(aValue);
4784
4785                        lLink = lValue.children('.' + self.options.MenuLinkClass);
4786                        lLink.wrap('<div class="' + self.options.MenuLinkWrapperClass + '"></div>');
4787                        lLinkWrapper = lValue.children('.' + self.options.MenuLinkWrapperClass);
4788                        lDrop = lValue.children('.' + self.options.MenuDropClass);
4789
4790                        if (lDrop.length === 1)
4791                        {
4792                            lLinkWrapper.append('<a href="#" class="' + self.options.DropOpenerClass + '"></a>');
4793                            lMyButton = lLinkWrapper.children('.' + self.options.DropOpenerClass);
4794                            self._setNavItemButtonText(lMyButton, lValue);
4795                        }
4796                    }
4797                );
4798
4799                jElement.on(
4800                    'click'
4801                    , '.' + self.options.DropOpenerClass
4802                    , function (aEvent) 
4803                    {
4804                        var lTarget, lItemToToggle;
4805
4806                        aEvent.preventDefault();
4807                        lTarget = $(aEvent.target);
4808                        lItemToToggle = lTarget.closest('.' + self.options.MenuSublistItemClass);
4809                        if (lItemToToggle.length === 0)
4810                        {
4811                            lItemToToggle = lTarget.closest('.' + self.options.MenuTopLevelItemClass);
4812                        }
4813                        if (lItemToToggle.length === 0)
4814                        {
4815                            self.ToggleMenu();
4816                            return false;
4817                        }
4818                        self.ToggleNavItem(lItemToToggle);
4819                        return false;
4820                    }
4821                );
4822
4823                //if (self.options.AllowMenuOpenClose)
4824                //{
4825                //    lMenuButtonSelector = '.' + self.options.MenuButtonClass;
4826                //    if (self.options.AddDropOpenerToMenuButton === true)
4827                //    {
4828                //        lMenuButtonSelector += ',.' + self.options.MenuButtonClass + ' .' + self.options.DropOpenerClass;
4829                //    }
4830                //}
4831
4832                if (self.options.InitOpenActiveItems)
4833                {
4834                    self._openActiveNavItems();
4835                }
4836                if (self.options.MenuDefaultOpen)
4837                {
4838                    self.OpenMenu();
4839                }
4840
4841                var lMainNavClass = 'nav-main';
4842                if ($('.' + lMainNavClass).length === 0)
4843                {
4844                    lMainNavClass = 'main-nav';
4845                }
4846                if ($('.' + lMainNavClass).length === 0)
4847                {
4848                    return;
4849                }
4850
4851                jElement.find('.' + self.options.MenuTopLevelItemClass).DropFocusManager(
4852                    {
4853                        TitleOpenerSelector: '> .' + self.options.MenuLinkWrapperClass + ' > .' + self.options.MenuLinkClass
4854                        , DropLinksSelector: '> .' + self.options.MenuDropClass + ' a'
4855                    }
4856                );
4857
4858                function handleTabKey(e)
4859                {
4860                    if (false === $('.secondary-opener').is(':visible'))
4861                    {
4862                        // ignore tab events on full menu.
4863                    }
4864                    else if ($(this).hasClass('main-drop-opener'))
4865                    {
4866                        if (false === $(this).parent().hasClass('main-opener'))
4867                        {
4868                            if ($(this).parent().parent().hasClass('top_level_item'))
4869                            {
4870                                if ($(this).parent().parent().next('.top_level_item').length === 0)
4871                                {
4872                                    let jTabbable = jElement.parent().find(':tabbable');
4873                                    let index = jTabbable.index(this);
4874                                    if (index === -1 || (index + 1) >= jTabbable.length)
4875                                    {
4876                                        jTabbable.first().get(0).focus();
4877                                        e.preventDefault();
4878                                        e.stopPropagation();
4879                                        return false;
4880                                    }
4881                                }
4882                            }
4883                            else if ($(this).parent().parent().next('.sublist_item').length === 0)
4884                            {
4885                                let jSublist = $(this).parent('.link_pair').parent('.sublist_item');
4886                                if (!jSublist.hasClass('menuopen'))
4887                                {
4888                                    let next = jSublist.parent('.main_sublist').parent('.drop').prev('.link_pair').find('.main-drop-opener').get(0);
4889                                    if ($(next).is(':tabbable'))
4890                                    {
4891                                        next.focus();
4892                                        e.preventDefault();
4893                                        e.stopPropagation();
4894                                        return false;
4895                                    }
4896                                }
4897                            }
4898                        }
4899                    }
4900                    else if ($(this).hasClass('top_level_link'))
4901                    {
4902                        let jTabbable = jElement.parent().find(':tabbable');
4903                        let index = jTabbable.index(this);
4904                        if (index === -1 || (index + 1) >= jTabbable.length)
4905                        {
4906                            jTabbable.first().get(0).focus();
4907                            e.preventDefault();
4908                            e.stopPropagation();
4909                            return false;
4910                        }
4911                    }
4912                    else if ($(this).hasClass('item_link'))
4913                    {
4914                        if (false === $(this).next().hasClass('main-drop-opener') && $(this).parent().parent().next('.sublist_item').length === 0)
4915                        {
4916                            let next = $(this)
4917                                .parent('.link_pair')
4918                                .parent('.sublist_item')
4919                                .parent('.main_sublist')
4920                                .parent('.drop')
4921                                .prev('.link_pair')
4922                                .find('.main-drop-opener')
4923                                .get(0);
4924
4925                            if ($(next).is(':tabbable'))
4926                            {
4927                                next.focus();
4928                                e.preventDefault();
4929                                e.stopPropagation();
4930                                return false;
4931                            }
4932                        }
4933                    }
4934                    return true;
4935                }
4936
4937                function handleEscapeKey(e)
4938                {
4939                    let lClosestOpenParentMenu = $(this).closest('.menuopen');
4940                    if (lClosestOpenParentMenu.length !== 0)
4941                    {
4942                        if (false === lClosestOpenParentMenu.hasClass(lMainNavClass))
4943                        {
4944                            let lClosestParentOpenDrop = $(this).closest('.drop');
4945                            lClosestParentOpenDrop.prev('.link_pair')
4946                                .find('.main-drop-opener')
4947                                .focus()
4948                                .click();
4949                            e.preventDefault();
4950                            e.stopPropagation();
4951                            return false;
4952                        }
4953                    }
4954                    return true;
4955                }
4956
4957                // handle escape on links.
4958                jElement.find('a').on('keydown', function (e)
4959                {
4960                    if (e.keyCode === 9 && false === e.shiftKey)
4961                    {
4962                        handleTabKey.call(this, e);
4963                    }
4964                    else if (e.key === "Escape")
4965                    {
4966                        handleEscapeKey.call(this, e);
4967                    }
4968                    return true;
4969                });
4970                
4971                jElement.addClass(self.options.MenuClass);
4972            },
4973
4974            ToggleMenu: function ()
4975            {
4976                var self = this,
4977                    jElement = $(self.element),
4978                    lMenuButton,
4979                    lButton;
4980
4981                jElement.toggleClass(self.options.MenuOpenClass);
4982                if (self.options.AddDropOpenerToMenuButton === true)
4983                {
4984                    lMenuButton = jElement.find(self.options.MenuButtonSelector);
4985                    lButton = lMenuButton.children('.' + self.options.DropOpenerClass);
4986                    self._setNavItemButtonText(lButton, jElement);
4987                }
4988                self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: jElement, name: 'FBRMainDropNav:ToggleMenu' } }));
4989            },
4990
4991            OpenMenu: function ()
4992            {
4993                var self = this,
4994                    jElement = $(self.element),
4995                    lMenuButton,
4996                    lButton;
4997
4998                jElement.addClass(self.options.MenuOpenClass);
4999
5000                if (self.options.AddDropOpenerToMenuButton === true)
5001                {
5002                    lMenuButton = jElement.find(self.options.MenuButtonSelector);
5003                    lButton = lMenuButton.children('.' + self.options.DropOpenerClass);
5004                    self._setNavItemButtonText(lButton, jElement);
5005                }
5006                self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: jElement, name: 'FBRMainDropNav:Ope
5006nMenu' } }));
5007            },
5008
5009            CloseMenu: function ()
5010            {
5011                var self = this,
5012                    jElement = $(self.element),
5013                    lMenuButton,
5014                    lButton;
5015
5016                jElement.removeClass(self.options.MenuOpenClass);
5017                if (self.options.AddDropOpenerToMenuButton === true)
5018                {
5019                    lMenuButton = jElement.find(self.options.MenuButtonSelector);
5020                    lButton = lMenuButton.children('.' + self.options.DropOpenerClass);
5021                    self._setNavItemButtonText(lButton, jElement);
5022                }
5023                self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: jElement, name: 'FBRMainDropNav:CloseMenu' } }));
5024            },
5025
5026            ToggleNavItem: function (aNavItem)
5027            {
5028                var self = this;
5029
5030                if (aNavItem.hasClass(self.options.MenuOpenClass) === false)
5031                {
5032                    self.OpenNavItem(aNavItem);
5033                }
5034                else
5035                {
5036                    self.CloseNavItem(aNavItem);
5037                }
5038            },
5039
5040            OpenNavItem: function (aNavItem)
5041            {
5042                var self = this,
5043                    jElement = $(self.element),
5044                    lButton = aNavItem.children('.' + self.options.MenuLinkWrapperClass).find('.' + self.options.DropOpenerClass);
5045
5046                if (self.options.AllowMultipleItemsOpen === false)
5047                {
5048                    var lAllOpenItems = jElement.find('.' + self.options.MenuSublistItemClass + '.has_subitems.' + self.options.MenuOpenClass);
5049                    var lParentOpenItems = aNavItem.parents('.' + self.options.MenuSublistItemClass + '.has_subitems.' + self.options.MenuOpenClass);
5050                    var lFilteredOpenItems = lAllOpenItems.not(lParentOpenItems);
5051                    $.each(
5052                        lFilteredOpenItems,
5053                        function (aIndex, aValue)
5054                        {
5055                            self.CloseNavItem($(aValue));
5056                        }
5057                    );
5058                }
5059
5060                self._openNavItem(lButton, aNavItem);
5061                self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: aNavItem, name: 'FBRMainDropNav:OpenNavItem' } }));
5062            },
5063
5064            _openNavItem: function (aButton, aNavItem)
5065            {
5066                var self = this;
5067
5068                aNavItem.addClass(self.options.MenuOpenClass);
5069                self._setNavItemButtonText(aButton, aNavItem);
5070            },
5071
5072            CloseNavItem: function (aNavItem)
5073            {
5074                var self = this,
5075                    lButton = aNavItem.children('.' + self.options.MenuLinkWrapperClass).find('.' + self.options.DropOpenerClass);
5076
5077                self._closeNavItem(lButton, aNavItem);
5078                self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: aNavItem, name: 'FBRMainDropNav:CloseNavItem' } }));
5079            },
5080
5081            _closeNavItem: function (aButton, aNavItem)
5082            {
5083                var self = this;
5084
5085                aNavItem.removeClass(self.options.MenuOpenClass);
5086                self._setNavItemButtonText(aButton, aNavItem);
5087            },
5088
5089            _setNavItemButtonText: function (aButton, aNavItem)
5090            {
5091                var self = this,
5092                    lLink = aNavItem.children('.' + self.options.MenuLinkWrapperClass).children('.' + self.options.MenuLinkClass);
5093
5094                if (aNavItem.is('.' + self.options.MenuOpenClass))
5095                {
5096                    aButton.html(self.options.CloseDropContent + '<span class="screen-reader-text">Close ' + lLink.text() + ' Menu Subpages</span>');
5097                }
5098                else
5099                {
5100                    aButton.html(self.options.OpenDropContent + '<span class="screen-reader-text">Open ' + lLink.text() + ' Menu Subpages</span>');
5101                }
5102            },
5103
5104            _openActiveNavItems: function ()
5105            {
5106                var self = this,
5107                    jElement = $(self.element),
5108                    lActiveItems = jElement.find('.' + self.options.MenuTopLevelItemClass + '.' + self.options.MenuItemActiveClass + ',.' + self.options.MenuSublistItemClass + '.' + self.options.MenuItemActiveClass);
5109
5110
5111                if (self.options.HonorDefaultOpenOnActiveOpen === false
5112                    && lActiveItems.length > 0)
5113                {
5114                    self.OpenMenu();
5115                }
5116
5117                $.each(
5118                    lActiveItems,
5119                    function (aIndex, aValue)//ignore jslint
5120                    {
5121                        var lValue = $(aValue),
5122                            lButton = lValue.children('.' + self.options.MenuLinkWrapperClass).find('.' + self.options.DropOpenerClass);
5123
5124                        self._openNavItem(lButton, lValue);
5125                    }
5126                );
5127            }
5128
5129        });
5130}(jQuery));
5131
5132///////////////////////////////////////////////////////////////////////////////
5133// FBRListNav - Foxbright Responsive List Navigation
5134///////////////////////////////////////////////////////////////////////////////
5135
5136(function ($)
5137{
5138    /*
5139        FBRListNav - Foxbright Responsive List Navigation
5140        
5141        Javascript controller for the responsive simple menu that changes 
5142        form/functionality below 768 pixels for mobile and tablet touch screen use.
5143        
5144        If the class fbcms_rlistmenu is used as a menu class, the menu will be a simple list 
5145        menu in the desktop view, and a stack of menu items that can be closed using their 
5146        title as a button.
5147        
5148        If the class fbcms_rdropmenu is used as a menu class, them menu will be a drop menu 
5149        in the desktop view as well.
5150        
5151        Options:
5152        
5153        MenuClass                           default: 'fbcms_rlistmenu'
5154                Required.  A class to be added to the menu once the widget has successfully 
5155                completed.  May be default or changed as noted above to change behavior.
5156        MenuInitClass                       default: 'fbcms_simplemenu'
5157                Required.  A class to be added to the menu once the widget has successfully 
5158                completed.  Change this at your peril.
5159        MenuOpenClass                       default: 'menuopen'
5160                Required.  A class to indicate when the menu is open, to be added or removed 
5161                by the widget as appropriate.
5162        MenuTitleClass                      default: 'menu_title'
5163                Required.  A class selector for finding the title element of the menu in the 
5164                menu element.  If the menu title is empty, the widget will do nothing.
5165        AddMenuButton                       default: false
5166                If true, add a button to the menu title with events to open and close the 
5167                menu. If true, MenuButtonClass, OpenMenuContent, and CloseMenuContent are 
5168                all required.
5169        MenuButtonClass                     default: 'menu_opener'
5170                Required if AddMenuButton is true.  A class used to identify the button added 
5171                to the menu title.
5172        OpenMenuContent                     default: '<span class="fas fa-chevron-down"></span>'
5173                Required if AddMenuButton is true.  Content to appear in the menu button when 
5174                the menu is closed and may be opened.
5175                Whatever content is put into place, the following content is added afterward.
5176                <span class="screen-reader-text">Open {Menu Title} Menu</span>
5177        CloseMenuContent                    default: '<span class="fas fa-chevron-up"></span>'
5178                Required if AddMenuButton is true.  Content to appear in the menu button when 
5179                the menu is open and may be closed.
5180                Whatever content is put into place, the following content is added afterward.
5181                <span class="screen-reader-text">Close {Menu Title} Menu</span>
5182        MenuContainerClass                  default: 'simplemenu_container'
5183                Required.  A class that should be found in the markup on the element wrapping 
5184                the menu items.
5185        InitOpen                            default: false
5186                If true, the menu will initialize open.
5187        MenuClickActive                     default: true
5188                If true, the menu button will be activated on mouse click events.
5189        MenuHoverActive                     default: false
5190                If true, the menu button will be activated on mouse hover events, closing on 
5191                hover out after a delay (MenuHoverDelay) if not hovered over in that time.
5192        MenuHoverDelay                      default: 300
5193                A hover delay for use if the MenuHoverActive is true.
5194        
5195        Methods:
5196        
5197        The widget is fully functional on its own, but can be remotely activated 
5197with the following 
5198        methods.
5199        
5200        ToggleMenu
5201                Called to toggle the menu open or closed.
5202        OpenMenu
5203                Called to open the menu (or do nothing if the menu is already open).
5204        CloseMenu
5205                Called to close the menu (or do nothing if the menu is already closed).
5206        
5207        Examples:
5208        
5209        Since the function is built as a jQuery Widget, simply call the functions on the 
5210        container element directly.
5211        
5212        $('.list-nav.menu_school').FBRListNav(
5213            {
5214                AddMenuButton : true
5215                ,InitOpen : true
5216            }
5217        );
5218        
5219        $('.drop-nav.menu_subpages').FBRListNav(
5220            {
5221                MenuClass: 'fbcms_rdropmenu'
5222                ,AddMenuButton : true
5223            }
5224        );
5225    */
5226    $.widget("ui.FBRListNav",
5227        {
5228            options:
5229            {
5230                MenuClass: 'fbcms_rlistmenu'
5231                , MenuInitClass: 'fbcms_simplemenu'
5232                , MenuOpenClass: 'menuopen'
5233                , MenuTitleClass: 'menu_title'
5234                , AddMenuButton: false
5235                , MenuButtonClass: 'menu_opener'
5236                , OpenMenuContent: '<span class="fas fa-chevron-down"></span>'
5237                , CloseMenuContent: '<span class="fas fa-chevron-up"></span>'
5238                , MenuContainerClass: 'simplemenu_container'
5239                , InitOpen: false
5240                , MenuClickActive: true
5241                , MenuHoverActive: false
5242                , MenuHoverDelay: 300
5243            },
5244
5245            _hoverTimer: null,
5246
5247            _destroy: function ()
5248            {
5249                return undefined;
5250            },
5251
5252            _init: function ()
5253            {
5254                return undefined;
5255            },
5256
5257            _create: function ()
5258            {
5259                var self = this;
5260                var jElement = $(self.element);
5261
5262                if ($('body.page_bodyonly').length === 1)
5263                {
5264                    jElement.remove();
5265                    return;
5266                }
5267
5268                if (FBUtilities.IsStringEmptyOrWhitespace(self.options.MenuClass)
5269                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.MenuInitClass)
5270                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.MenuOpenClass)
5271                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.MenuTitleClass)
5272                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.MenuContainerClass))
5273                {
5274                    FBUtilities.LogMessage('FBRListNav - required settings null or empty.');
5275                    return;
5276                }
5277
5278                if (self.options.AddMenuButton === true
5279                    && (FBUtilities.IsStringEmptyOrWhitespace(self.options.MenuButtonClass)
5280                        || FBUtilities.IsStringEmptyOrWhitespace(self.options.OpenMenuContent)
5281                        || FBUtilities.IsStringEmptyOrWhitespace(self.options.CloseMenuContent)))
5282                {
5283                    FBUtilities.LogMessage('FBRListNav - menu button settings not set as requested.');
5284                    return;
5285                }
5286
5287                let lTitleElement = jElement.find(`.${self.options.MenuTitleClass}`);
5288
5289                if (lTitleElement.length === 0)
5290                {
5291                    FBUtilities.LogMessage('FBRListNav - menu title not found.');
5292                    return;
5293                }
5294                lTitleElement.attr("aria-expanded", false);
5295
5296                self._MenuTitle = lTitleElement.text();
5297
5298                if (self.options.AddMenuButton === true)
5299                {
5300                    lTitleElement.append('<span class="' + self.options.MenuButtonClass + '"></span>');
5301                    self._setMenuButtonContent();
5302                }
5303
5304                let lButtonEventSelector = '.' + self.options.MenuTitleClass;
5305                if (self.options.AddMenuButton === true)
5306                {
5307                    lButtonEventSelector = lButtonEventSelector + ',.' + self.options.MenuButtonClass;
5308                }
5309
5310                if (self.options.MenuClickActive === true)
5311                {
5312                    jElement.on(
5313                        'click',
5314                        lButtonEventSelector,
5315                        function (aEvent) 
5316                        {
5317                            aEvent.preventDefault();
5318                            self.ToggleMenu();
5319                            return false;
5320                        }
5321                    );
5322                }
5323
5324                if (self.options.MenuHoverActive === true)
5325                {
5326                    jElement.hover(
5327                        function ()
5328                        {
5329                            self.OpenMenu();
5330
5331                            if (self._hoverTimer !== null)
5332                            {
5333                                $.clearTimer(self._hoverTimer);
5334                            }
5335                        }
5336                        , function ()
5337                        {
5338                            self._hoverTimer = $.timer(
5339                                self.options.MenuHoverDelay,
5340                                function ()
5341                                {
5342                                    self.CloseMenu();
5343                                }
5344                            );
5345                        }
5346                    );
5347                }
5348
5349                if (self.options.InitOpen)
5350                {
5351                    self.OpenMenu();
5352                }
5353
5354                jElement.DropFocusManager(
5355                    {
5356                        HoverFocusClass: self.options.MenuOpenClass
5357                        , OpenOnHoverFocus: false
5358                        , TitleOpenerSelector: '.' + self.options.MenuTitleClass
5359                        , DropLinksSelector: '.' + self.options.MenuContainerClass + ' a'
5360                    }
5361                );
5362
5363                jElement.addClass(self.options.MenuClass);
5364                jElement.addClass(self.options.MenuInitClass);
5365            },
5366
5367            _MenuTitle: null,
5368
5369            ToggleMenu: function ()
5370            {
5371                var self = this,
5372                    jElement = $(self.element);
5373
5374                jElement.toggleClass(self.options.MenuOpenClass);
5375                jElement.find(`.${self.options.MenuTitleClass}`).attr("aria-expanded", jElement.hasClass(self.options.MenuOpenClass));
5376
5377                self._setMenuButtonContent();
5378                self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: jElement, name: 'FBRListNav:ToggleMenu' } }));
5379            },
5380
5381            OpenMenu: function ()
5382            {
5383                var self = this,
5384                    jElement = $(self.element);
5385
5386                jElement.addClass(self.options.MenuOpenClass);
5387                jElement.find(`.${self.options.MenuTitleClass}`).attr("aria-expanded", true);
5388                
5389                self._setMenuButtonContent();
5390                self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: jElement, name: 'FBRListNav:OpenMenu' } }));
5391            },
5392
5393            CloseMenu: function ()
5394            {
5395                var self = this,
5396                    jElement = $(self.element);
5397
5398                jElement.removeClass(self.options.MenuOpenClass);
5399                jElement.find(`.${self.options.MenuTitleClass}`).attr("aria-expanded", false);
5400                
5401                self._setMenuButtonContent();
5402                self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: jElement, name: 'FBRListNav:CloseMenu' } }));
5403            },
5404
5405            _setMenuButtonContent: function ()
5406            {
5407                var self = this,
5408                    jElement = $(self.element),
5409                    lMenuButton;
5410
5411                if (self.options.AddMenuButton === true)
5412                {
5413                    lMenuButton = jElement.find('.' + self.options.MenuButtonClass);
5414
5415                    if (jElement.is('.' + self.options.MenuOpenClass))
5416                    {
5417                        lMenuButton.html(self.options.CloseMenuContent + '<span class="screen-reader-text">Close ' + self._MenuTitle + ' Menu</span>');
5418                    }
5419                    else
5420                    {
5421                        lMenuButton.html(self.options.OpenMenuContent + '<span class="screen-reader-text">Open ' + self._MenuTitle + ' Menu</span>');
5422                    }
5423                }
5424            }
5425
5426        });
5427}(jQuery));
5428
5429///////////////////////////////////////////////////////////////////////////////
5430// VisualNavManager - Visual Nav/"Mobile Menu" Manager
5431///////////////////////////////////////////////////////////////////////////////
5432
5433function VisualNavManager_Functions()
5434{
5435    return undefined;
5436}
5437
5438VisualNavManager_Functions._ExtraMenuButtonHandler = function (aEvent) 
5439{
5440    aEvent.preventDefault();
5441    $(aEvent.target).closest('.visual_nav_wrapper').VisualNavManager('ToggleMenu');
5442    return false;
5443};
5444
5445VisualNavManager_Functions._ExtraMenuEscapeHandler = function (aEvent) 
5446{
5447    if (aEvent.keyCode === 27 /*escape*/)
5448    {
5449        aEvent.preventDefault();
5450        var jNav = $(aEvent.target).closest('.visual_nav_wrapper');
5451        jNav.VisualNavManager('CloseMenu');
5452        jNav.find('.visual_nav_extraopener').focus();// warning, class from options here!!!
5453        return false;
5454    }
5455};
5456
5457VisualNavManager_Functions._ExtraMenuLoopBackHandler = function (aEvent) 
5458{
5459    if (aEvent.keyCode === 9 && aEvent.shiftKey === false)
5460    {
5461        aEvent.preventDefault();
5462        var jNav = $(aEvent.target).closest('.visual_nav_wrapper');
5463        jNav.find('.visual_nav_liner_extra .visual_nav_item_link').first().focus();// warning, class from options here!!!
5464    }
5465};
5466
5467VisualNavManager_Functions._ExtraMenuLoopFrontHandler = function (aEvent) 
5468{
5469    if (aEvent.keyCode === 9 && aEvent.shiftKey === true)
5470    {
5471        aEvent.preventDefault();
5472        var jNav = $(aEvent.target).closest('.visual_nav_wrapper');
5473        jNav.find('.visual_nav_liner_extra .visual_nav_item_link').last().focus();// warning, class from options here!!!
5474    }
5475};
5476
5477VisualNavManager_Functions._ExtraMenuGlobalFocusHandler = function (aEvent) 
5478{
5479    if ($(aEvent.target).closest('.visual_nav_wrapper .visual_nav_liner_extra').length === 0)
5480    {
5481        var jNav = $('.visual_nav_wrapper');
5482        jNav.VisualNavManager('CloseMenu');
5483        if (jQuery.contains( jNav.find('.visual_nav_extraopener'), aEvent.target ) === false)
5484        {
5485            jNav.VisualNavManager('CloseMenu');
5486        }
5487    }
5488};
5489
5490(function ($)
5491{
5492    /*
5493        VisualNavManager - Visual Nav/"Mobile Menu" Manager
5494        
5495        Javascript controller for any active components of the VisualNav menus.
5496        This currently consists of the Extra/Overflow menu.
5497        
5498        Options:
5499        
5500        MenuOpenClass                       default: 'visual_nav_open'
5501                Required.  A class to indicate when the menu is open, to be added or removed 
5502                by the widget as appropriate.
5503        MenuButtonClass                     default: 'visual_nav_extraopener'
5504                Required if AddMenuButton is true.  A class used to identify the button added 
5505                to the menu title.
5506        MenuContainerClass                  default: 'visual_nav_liner_extra'
5507                Required.  A class that should be found in the markup on the element wrapping 
5508                the menu items.
5509        
5510        Methods:
5511        
5512        The widget is fully functional on its own, but can be remotely activated with the following 
5513        methods.
5514        
5515        ToggleMenu
5516                Called to toggle the menu open or closed.
5517        OpenMenu
5518                Called to open the menu (or do nothing if the menu is already open).
5519        CloseMenu
5520                Called to close the menu (or do nothing if the menu is already closed).
5521        
5522        Examples:
5523        
5524        Since the function is built as a jQuery Widget, simply call the functions on the 
5525        container element directly.
5526        
5527        $('.visual_nav_wrapper').VisualNavManager();
5528
5529    */
5530    $.widget("ui.VisualNavManager",
5531        {
5532            options:
5533            {
5534                MenuOpenClass: 'visual_nav_open'
5535                , MenuButtonClass: 'visual_nav_extraopener'
5536                , MenuContainerClass: 'visual_nav_liner_extra'
5537                , MenuLinkClass: 'visual_nav_item_link'
5538            },
5539
5540            _hoverTimer: null,
5541
5542            _destroy: function ()
5543            {
5544                var self = this;
5545                var jElement = $(self.element);
5546                
5547                jElement.off(
5548                    'click',
5549                    '.' + self.options.MenuButtonClass,
5550                    VisualNavManager_Functions._ExtraMenuButtonHandler
5551                );
5552
5553                let jExtraMenuOpener = jElement.find('.visual_nav_extraopener');
5554                let jExtraMenu = jElement.find('.visual_nav_liner_extra');
5555                if (jExtraMenuOpener.length > 0
5556                    && jExtraMenu.length > 0)
5557                {
5558                    let lExtraMenu = jExtraMenu.get(0);
5559                    let jExtraLinks = jExtraMenu.find('.visual_nav_item_link');
5560                    
5561                    jExtraLinks.off('keydown', VisualNavManager_Functions._ExtraMenuEscapeHandler);
5562                    
5563                    jExtraLinks.first().off('keydown', VisualNavManager_Functions._ExtraMenuLoopFrontHandler);
5564
5565                    jExtraLinks.last().off('keydown', VisualNavManager_Functions._ExtraMenuLoopBackHandler);
5566                    
5567                    $('body').off('click', VisualNavManager_Functions._ExtraMenuGlobalFocusHandler);
5568                }
5569                
5570                return undefined;
5571            },
5572
5573            _init: function ()
5574            {
5575                return undefined;
5576            },
5577
5578            _create: function ()
5579            {
5580                var self = this;
5581                var jElement = $(self.element);
5582
5583                //TODO: should this remain???
5584                if ($('body.page_bodyonly').length === 1)
5585                {
5586                    jElement.remove();
5587                    return;
5588                }
5589
5590                if (FBUtilities.IsStringEmptyOrWhitespace(self.options.MenuOpenClass)
5591                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.MenuButtonClass)
5592                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.MenuContainerClass)
5593                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.MenuLinkClass))
5594                {
5595                    FBUtilities.LogMessage('FBRListNav - required settings null or empty.');
5596                    return;
5597                }
5598
5599                jElement.on(
5600                    'click',
5601                    '.' + self.options.MenuButtonClass,
5602                    VisualNavManager_Functions._ExtraMenuButtonHandler
5603                );
5604                
5605                var lElement = jElement.get(0);
5606                $("body").css("--VisualNav-Height", "" + lElement.offsetHeight + "px");
5607                
5608                let jExtraMenuOpener = jElement.find('.visual_nav_extraopener');
5609                let jExtraMenu = jElement.find('.visual_nav_liner_extra');
5610                if (jExtraMenuOpener.length > 0
5611                    && jExtraMenu.length > 0)
5612                {
5613                    let lExtraMenu = jExtraMenu.get(0);
5614                    let jExtraLinks = jExtraMenu.find('.visual_nav_item_link');
5615                    
5616                    // if it's ESC, close menu.
5617                    jExtraLinks.on('keydown', VisualNavManager_Functions._ExtraMenuEscapeHandler);
5618                    
5619
5620                    // on first item, loop to last on backward tab.
5621                    jExtraLinks.first().on('keydown', VisualNavManager_Functions._ExtraMenuLoopFrontHandler);
5622
5623                    // on last item, loop to first on forward tab.
5624                    jExtraLinks.last().on('keydown', VisualNavManager_Functions._ExtraMenuLoopBackHandler);
5625                    
5626                    
5627                    // on focus loss, close menu?                    
5628                    jExtraMenu.on('focusout', function(event)
5629                    {
5630                        // unknown if this is usefully populated.
5631                        if (jQuery.contains( lExtraMenu, event.relatedTarget ) === false)
5632                        {
5633                            self.CloseMenu();
5634                        }
5635                    });
5636                    $('body').on('click', VisualNavManager_Functions._ExtraMenuGlobalFocusHandler);
5637                }
5638            },
5639
5640            ToggleMenu: function ()
5641            {
5642                var self = this,
5643                    jElement = $(self.element);
5644
5645                jElement.toggleClass(self.options.MenuOpenClass);
5646
5647                self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: jElement, name: 'VisualNavManager:ToggleMenu' } }));
5648            },
5649
5650            OpenMenu: function ()
5651            {
5652                var self = this,
5653                    jElement = $(self.element);
5654
5655                jElement.addClass(self.options.MenuOpenClass);
5656
5657                self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: jElement, name: 'VisualNavManager:OpenMenu' } }));
5658            },
5659
5660            CloseMenu: function ()
5661            {
5662                var self = this,
5663                    jElement = $(self.element);
5664
5665                jElement.removeClass(self.options.MenuOpenClass);
5666
5667                self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: jElement, name: 'VisualNavManager:CloseMenu' } }));
5668            }
5669        });
5670        
5671    $(window).on('load', function ()
5672    {
5673        $('.visual_nav_wrapper').VisualNavManager();
5674    });
5675}(jQuery));
5676
5677
5678///////////////////////////////////////////////////////////////////////////////
5679// FBRFlyUp - Foxbright Responsive Flyup
5680///////////////////////////////////////////////////////////////////////////////
5681
5682(function ($)
5683{
5684    /*
5685        FBRFlyUp - Foxbright Responsive Flyup
5686        
5687        Fly-up widget.
5688        
5689        The element selected by the widget (container element) will have a class added to it 
5690        to indicate that it is opened (OpenClass). Selecting the fly-up tab or mouse hovering 
5691        over any part of the slider will open the fly-up and put the class in place.
5692        
5693        Options:
5694        
5695        FlyupTitleSelector                  default: null - must be set to a valid selector.
5696                Required.  A selector to be used to find the title element(s) inside the element.
5697        ContainerClass                      default: 'flyup_container'
5698                Required.  The class to be set to the widget element.
5699        OpenClass                           default: 'flyup_expanded'
5700                Required.  The class to be set on the drawer element when open.
5701        DrawerClass                         default: 'flyup_drawer'
5702                Required.  The class to be set on a content wrapper that does all the sliding.
5703                This element will be added to the DOM by the widget.
5704        TabWrapperClass                     default: 'flyup_tabcontainer'
5705                Required.  The class to be set on a wrapper around the tab.
5706                This element will be added to the DOM by the widget.
5707        TabClass                            default: 'flyup_tab'
5708                Required.  The class to be added to the flyup title element.
5709        SlideClass                          default: 'flyup_slide'
5710                Required.  The class for the element to be wrapped around all content under the 
5711                flyup title.  This element will be added to the DOM by the widget.
5712        AnimationSpeed                      default: 500
5713                Required.  Number of milliseconds that the slide animation should take.
5714        UseClickOnly                        default: false
5715                Required.  True if the flyup should work only on click instead of by hover.
5716        
5717        Methods:
5718        
5719        The widget is fully functional on its own, but can be remotely activated with the following 
5720        methods.
5721        
5722        ToggleFlyup
5723                Called to toggle the widget open or closed.
5724        OpenFlyup
5725                Called to open the widget (or do nothing if the widget is already open).
5726        CloseFlyup
5727                Called to close the widget (or do nothing if the widget is already closed).
5728        
5729        Examples:
5730        
5731        Since the function is built as a jQuery Widget, simply call the functions on the 
5732        container element directly.  New markup and classes will be added in place.  This example 
5733        has the markup followed by the jQuery call to activate it.
5734        
5735        <div class="slider_box">
5736            <h2 class="slider_box_title">{TITLE CONTENT}</h2>
5737            {SLIDER CONTENT - Must be HTML elements, may be multiple.}
5738        </div>
5739        
5740        $('.slider_box').FBRFlyUp(
5741            {
5742                FlyupTitleSelector : '.slider_box_title'
5743            }
5744        );
5745    */
5746    $.widget("ui.FBRFlyUp",
5747        {
5748            options:
5749            {
5750                FlyupTitleSelector: null
5751                , ContainerClass: 'flyup_container'
5752                , OpenClass: 'flyup_expanded'
5753                , DrawerClass: 'flyup_drawer'
5754                , TabWrapperClass: 'flyup_tabcontainer'
5755                , TabClass: 'flyup_tab'
5756                , SlideClass: 'flyup_slide'
5757                , AnimationSpeed: 500
5758                , UseClickOnly: false
5759            },
5760
5761            _destroy: function ()
5762            {
5763                var self, jElement, lTitleElement, lIsEditMode, lTab, lDrawer, lSlide;
5764
5765                self = this;
5766                jElement = $(self.element);
5767                lTitleElement = jElement.find(self.options.FlyupTitleSelector);
5768                if (lTitleElement.length === 0)
5769                {
5770                    return;
5771                }
5772
5773                lIsEditMode = FBCommon_1_2.isInEditor();
5774                lTab = jElement.find('.' + self.options.TabClass);
5775                lDrawer = jElement.find('.' + self.options.DrawerClass);
5776                lSlide = jElement.find('.' + self.options.SlideClass);
5777
5778                if (lTab.length === 0
5779                    || lDrawer.length === 0
5780                    || lSlide.length === 0)
5781                {
5782                    return;
5783                }
5784
5785                if (lIsEditMode === false
5786                    && FBUtilities.IsStringEmptyOrWhitespace(lTab.text()))
5787                {
5788                    jElement.show();
5789                    return;
5790                }
5791
5792                jElement.css(
5793                    {
5794                        position: ''
5795                    }
5796                );
5797
5798                lDrawer.css(
5799                    {
5800                        position: '',
5801                        top: ''
5802                    }
5803                );
5804
5805                jQuery(document).off('click');
5806                lTab.off('keypress');
5807
5808                if (self.options.UseClickOnly)
5809                {
5810                    lTab.off('click', self.ToggleFlyup);
5811                }
5812                else
5813                {
5814                    jElement.off(
5815                        "mouseout",
5816                        '.' + self.options.DrawerClass
5817                    );
5818                    jElement.off(
5819                        "mouseover",
5820                        '.' + self.options.TabClass
5821                    );
5822                }
5823
5824                jElement.off(
5825                    "focusin",
5826                    '.' + self.options.SlideClass
5827                );
5828
5829
5830                lTitleElement.unwrap();
5831                lTitleElement.removeAttr('tabindex');
5832                lTitleElement.removeClass(self.options.TabClass);
5833                jElement.find('.' + self.options.DrawerClass).unwrap();
5834                jElement.find('.' + self.options.SlideClass).children().unwrap();
5835                jElement.removeClass(self.options.ContainerClass);
5836            },
5837
5838            _init: function ()
5839            {
5840                return undefined;
5841            },
5842
5843            _create: function ()
5844            {
5845                var self, jElement, lTitleElement, lTab, lDrawer, lSlide;
5846
5847                self = this;
5848                jElement = $(self.element);
5849
5850                if (FBUtilities.IsStringEmptyOrWhitespace(self.options.FlyupTitleSelector)
5851                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.ContainerClass)
5852                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.OpenClass)
5853                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.DrawerClass)
5854                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.TabWrapperClass)
5855                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.TabClass)
5856                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.SlideClass)
5857                    || FBUtilities.IsParameterUndefined(self.options.AnimationSpeed)
5858                    || FBUtilities.IsParameterUndefined(self.options.UseClickOnly))
5859                {
5860                    FBUtilities.LogMessage('FBRFlyUp - required settings null or empty.');
5861                    jElement.hide();
5862                    return;
5863                }
5864
5865                lTitleElement = jElement.find(self.options.FlyupTitleSelector);
5866                if (lTitleElement.length === 0)
5867                {
5868                    jElement.hide();
5869                    return;
5870                }
5871
5872                lTitleElement.wrap($('<div></div>').addClass(self.options.TabWrapperClass));
5873                lTitleElement.addClass(self.options.TabClass);
5874                lTitleElement.attr('tabindex', '0');
5875                jElement.children().not('.' + self.options.TabWrapperClass).wrapAll($('<div></div>').addClass(self.options.SlideClass));
5876                jElement.children().wrapAll($('<div></div>').addClass(self.options.DrawerClass));
5877                jElement.addClass(self.options.ContainerClass);
5878
5879
5880                lTab = jElement.find('.' + self.options.TabClass);
5881                lDrawer = jElement.find('.' + self.options.DrawerClass);
5882                lSlide = jElement.find('.' + self.options.SlideClass);
5883
5884                if (lTab.length === 0
5885                    || lDrawer.length === 0)
5886                {
5887                    jElement.hide();
5888                    return;
5889                }
5890
5891                if (FBCommon_1_2.isInEditor() === false
5892                    && FBUtilities.IsStringEmptyOrWhitespace(lTab.text()))
5893                {
5894                    jElement.hide();
5895                    return;
5896                }
5897
5898                if (lSlide.length === 0)
5899                {
5900                    jElement.hide();
5901                    return;
5902                }
5903
5904                jElement.css(
5905                    {
5906                        position: 'relative'
5907                    }
5908                );
5909
5910                lDrawer.css(
5911                    {
5912                        position: 'absolute',
5913                        top: -lTab.innerHeight()
5914                    }
5915                );
5916
5917                jQuery(document).click(
5918                    function (aEvent)
5919                    {
5920                        var lTarget = jQuery(aEvent.target);
5921                        if (!lTarget.parents().is(lDrawer))
5922                        {
5923                            self.CloseFlyup();
5924                        }
5925                    }
5926                );
5927
5928                lTab.keypress(
5929                    function (aEvent)
5930                    {
5931                        if (aEvent.which === 13)
5932                        {
5933                            self.ToggleFlyup();
5934                            return false;
5935                        }
5936                    }
5937                );
5938
5939                if (self.options.UseClickOnly)
5940                {
5941                    lTab.on('click', self.ToggleFlyup);
5942                }
5943                else
5944                {
5945                    jElement.on(
5946                        "mouseout",
5947                        '.' + self.options.DrawerClass,
5948                        function (aEvent)
5949                        {
5950                            var lDrawerParent = $(aEvent.relatedTarget).closest('.' + self.options.DrawerClass);
5951                            if (lDrawerParent.length === 1
5952                                && lDrawerParent.get(0) === lDrawer.get(0))
5953                            {
5954                                return false;
5955                            }
5956                            self.CloseFlyup();
5957                        }
5958                    );
5959                    jElement.on(
5960                        "mouseover",
5961                        '.' + self.options.TabClass,
5962                        function (/*aEvent*/)
5963                        {
5964                            self.OpenFlyup();
5965                        }
5966                    );
5967                }
5968
5969                jElement.on(
5970                    "focusin",
5971                    '.' + self.options.SlideClass,
5972                    function (/*aFIEvent*/)
5973                    {
5974                        self.OpenFlyup();
5975                        lSlide.on(
5976                            "focusout",
5977                            function (aFOEvent)
5978                            {
5979                                var lSlideParent = $(aFOEvent.target).closest('.' + self.options.SlideClass);
5980                                if (lSlideParent.length === 1
5981                                    && lSlideParent.get(0) === lSlide.get(0))
5982                                {
5983                                    return false;
5984                                }
5985
5986                                self.CloseFlyup();
5987                                lSlide.off("focusout");
5988                            }
5989                        );
5990                    }
5991                );
5992            },
5993
5994            ToggleFlyup: function ()
5995            {
5996                var self, jElement, lDrawer;
5997
5998                self = this;
5999                jElement = $(self.element);
6000                lDrawer = jElement.find('.' + self.options.DrawerClass);
6001
6002                if (lDrawer.hasClass(self.options.OpenClass))
6003                {
6004                    self.CloseFlyup();
6005                }
6006                else
6007                {
6008                    self.OpenFlyup();
6009                }
6010            },
6011
6012            OpenFlyup: function ()
6013            {
6014                var self, jElement, lDrawer;
6015
6016                self = this;
6017                jElement = $(self.element);
6018                lDrawer = jElement.find('.' + self.options.DrawerClass);
6019
6020                lDrawer.animate({
6021                    top: -lDrawer.innerHeight()
6022                }, {
6023                    queue: false,
6024                    duration: self.options.AnimationSpeed,
6025                    complete: function ()
6026                    {
6027                        lDrawer.addClass(self.options.OpenClass);
6028                        self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: jElement, name: 'FBRFlyUp:OpenFlyup' } }));
6029                    }
6030                });
6031            },
6032
6033            CloseFlyup: function ()
6034            {
6035                var self, jElement, lTab, lDrawer;
6036
6037                self = this;
6038                jElement = $(self.element);
6039                lTab = jElement.find('.' + self.options.TabClass);
6040                lDrawer = jElement.find('.' + self.options.DrawerClass);
6041
6042                lDrawer.animate({
6043                    top: -lTab.innerHeight()
6044                }, {
6045                    queue: false,
6046                    duration: self.options.AnimationSpeed,
6047                    complete: function ()
6048                    {
6049                        lDrawer.removeClass(self.options.OpenClass);
6050                        self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: jElement, name: 'FBRFlyUp:CloseFlyup' } }));
6051                    }
6052                });
6053            }
6054
6055        });
6056}(jQuery));
6057
6058///////////////////////////////////////////////////////////////////////////////
6059// DropFocusManager - Foxbright Drop Focus Manager
6060///////////////////////////////////////////////////////////////////////////////
6061
6062(function ($)
6063{
6064    /*            
6065        DropFocusManager - Foxbright Drop Focus Manager
6066        
6067        Javascript controller for managing simple drop logic and incorporating keyboard focus 
6068        into the drop behavior as well.
6069        
6070        The element selected by the widget (container element) will have a class added to it 
6071        to indicate that it is opened (HoverFocusClass). Selecting or focusing on the element 
6072        or elements indicated by TitleOpenerSelector will put the class in place. Selection 
6073        of the element(s) indicated by DropLinksSelector will keep the open status in place.
6074        A delay for closing the element (FocusCloseDelay) is available for tuning the 
6075        behavior of the widget and ensuring time for events to propagate.
6076        
6077        Options:
6078        
6079        HoverFocusClass                     default: 'hoverfocus'
6080                Required.  A class to be added to the element when focu
6080s is in the element.
6081        OpenOnHoverFocus                    default: true
6082                Required.  True if the hoverfocus class is to be added on hover as well.
6083        FocusCloseDelay                     default: 10
6084                Required.  The number of milliseconds after loss of focus to wait before 
6085                closing the element.  Necessary to give time for new focus events that occur 
6086                inside the element to propagate and stop the closure.
6087        TitleOpenerSelector                 default: null
6088                Required.  A jQuery selector indicating the element or elements to use as an 
6089                opener button for the element.
6090        DropLinksSelector                   default: null
6091                Required.  A jQuery selector indicating the elements inside the element that 
6092                should keep the element open if selected.
6093        
6094        Methods:
6095        
6096        The widget is fully functional on its own, but can be remotely activated with the following 
6097        methods.
6098        
6099        ToggleDrop
6100                Called to toggle the widget open or closed.
6101        OpenDrop
6102                Called to open the widget (or do nothing if the widget is already open).
6103        CloseDrop
6104                Called to close the widget (or do nothing if the widget is already closed).
6105        
6106        Examples:
6107        
6108        Since the function is built as a jQuery Widget, simply call the functions on the 
6109        container element directly.
6110        
6111        $('.list-nav.menu_quicklinks').DropFocusManager(
6112            {
6113                TitleOpenerSelector : '.menu_title'
6114                ,DropLinksSelector : '.drop a'
6115            }
6116        );
6117    */
6118    $.widget("ui.DropFocusManager",
6119        {
6120            options:
6121            {
6122                HoverFocusClass: 'hoverfocus'
6123                , OpenOnHoverFocus: true
6124                , FocusCloseDelay: 10
6125                , TitleOpenerSelector: null
6126                , DropLinksSelector: null
6127            },
6128
6129            _focusTimer: null,
6130
6131            _destroy: function ()
6132            {
6133                return undefined;
6134            },
6135
6136            _init: function ()
6137            {
6138                return undefined;
6139            },
6140
6141            _create: function ()
6142            {
6143                var self, jElement;
6144
6145                self = this;
6146                jElement = $(self.element);
6147
6148                if (FBUtilities.IsStringEmptyOrWhitespace(self.options.HoverFocusClass)
6149                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.TitleOpenerSelector)
6150                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.DropLinksSelector))
6151                {
6152                    FBUtilities.LogMessage('DropFocusManager - required settings null or empty.');
6153                    return;
6154                }
6155
6156                jElement.on(
6157                    "focusin",
6158                    self.options.TitleOpenerSelector,
6159                    function (/*aEvent*/)
6160                    {
6161                        if (self.options.OpenOnHoverFocus === true)
6162                        {
6163                            $.clearTimer(self._focusTimer);
6164                            self.OpenDrop();
6165                        }
6166                    }
6167                );
6168
6169                jElement.on(
6170                    "focusin",
6171                    self.options.DropLinksSelector,
6172                    function (/*aEvent*/)
6173                    {
6174                        $.clearTimer(self._focusTimer);
6175                        self.OpenDrop();
6176                    }
6177                );
6178
6179                jElement.on(
6180                    "focusout",
6181                    self.options.TitleOpenerSelector + ',' + self.options.DropLinksSelector,
6182                    function (/*aEvent*/)
6183                    {
6184                        self._focusTimer = $.timer(
6185                            self.options.FocusCloseDelay,
6186                            function ()
6187                            {
6188                                self.CloseDrop();
6189                            }
6190                        );
6191                    }
6192                );
6193
6194            },
6195
6196            ToggleDrop: function ()
6197            {
6198                var self, jElement;
6199
6200                self = this;
6201                jElement = $(self.element);
6202                jElement.toggleClass(self.options.HoverFocusClass);
6203                self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: jElement, name: 'DropFocusManager:ToggleDrop' } }));
6204            },
6205
6206            OpenDrop: function ()
6207            {
6208                var self, jElement;
6209
6210                self = this;
6211                jElement = $(self.element);
6212
6213                if (jElement.hasClass(self.options.HoverFocusClass))
6214                {
6215                    return;
6216                }
6217                jElement.addClass(self.options.HoverFocusClass);
6218                self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: jElement, name: 'DropFocusManager:OpenDrop' } }));
6219            },
6220
6221            CloseDrop: function ()
6222            {
6223                var self, jElement;
6224
6225                self = this;
6226                jElement = $(self.element);
6227                jElement.removeClass(self.options.HoverFocusClass);
6228                self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: jElement, name: 'DropFocusManager:CloseDrop' } }));
6229            }
6230
6231        });
6232}(jQuery));
6233
6234///////////////////////////////////////////////////////////////////////////////
6235// ItemFocusManager - Foxbright Item Focus Manager
6236///////////////////////////////////////////////////////////////////////////////
6237
6238(function ($)
6239{
6240    /*            
6241        ItemFocusManager - Foxbright Item Focus Manager
6242        
6243        Javascript controller for managing simple drop logic and incorporating keyboard focus 
6244        into the drop behavior as well. Based on DropFocusManager.
6245        
6246        The element selected by the widget (container element) will have a class added to it 
6247        to indicate that it is opened (HoverFocusClass). Selecting or focusing on the element 
6248        or elements inside it will put the class in place. 
6249        A delay for closing the element (FocusCloseDelay) is available for tuning the 
6250        behavior of the widget and ensuring time for events to propagate.
6251        
6252        Options:
6253        
6254        HoverFocusClass                     default: 'hoverfocus'
6255                Required.  A class to be added to the element when focu
6255s is in the element.
6256        OpenOnHoverFocus                    default: true
6257                Required.  True if the hoverfocus class is to be added on hover as well.
6258        FocusCloseDelay                     default: 10
6259                Required.  The number of milliseconds after loss of focus to wait before 
6260                closing the element.  Necessary to give time for new focus events that occur 
6261                inside the element to propagate and stop the closure.
6262        
6263        Methods:
6264        
6265        The widget is fully functional on its own, but can be remotely activated with the following 
6266        methods.
6267        
6268        ToggleDrop
6269                Called to toggle the widget open or closed.
6270        OpenDrop
6271                Called to open the widget (or do nothing if the widget is already open).
6272        CloseDrop
6273                Called to close the widget (or do nothing if the widget is already closed).
6274        
6275        Examples:
6276        
6277        Since the function is built as a jQuery Widget, simply call the functions on the 
6278        container element directly.
6279        
6280        $('.item').ItemFocusManager();
6281    */
6282    $.widget("ui.ItemFocusManager",
6283        {
6284            options:
6285            {
6286                HoverFocusClass: 'hoverfocus'
6287                , OpenOnHoverFocus: true
6288                , FocusCloseDelay: 10
6289            },
6290
6291            _focusTimer: null,
6292
6293            destroy: function ()
6294            {
6295                return undefined;
6296            },
6297
6298            _init: function ()
6299            {
6300                return undefined;
6301            },
6302
6303            _create: function ()
6304            {
6305                var self, jElement;
6306
6307                self = this;
6308                jElement = $(self.element);
6309
6310                if (FBUtilities.IsStringEmptyOrWhitespace(self.options.HoverFocusClass))
6311                {
6312                    FBUtilities.LogMessage('ItemFocusManager - required settings null or empty.');
6313                    return;
6314                }
6315
6316                jElement.on(
6317                    "focusin",
6318                    function (/*aEvent*/)
6319                    {
6320                        if (self.options.OpenOnHoverFocus === true)
6321                        {
6322                            $.clearTimer(self._focusTimer);
6323                            self.OpenDrop();
6324                        }
6325                    }
6326                );
6327
6328                jElement.on(
6329                    "focusout",
6330                    function (/*aEvent*/)
6331                    {
6332                        self._focusTimer = $.timer(
6333                            self.options.FocusCloseDelay,
6334                            function ()
6335                            {
6336                                self.CloseDrop();
6337                            }
6338                        );
6339                    }
6340                );
6341
6342            },
6343
6344            ToggleDrop: function ()
6345            {
6346                var self, jElement;
6347
6348                self = this;
6349                jElement = $(self.element);
6350                jElement.toggleClass(self.options.HoverFocusClass);
6351                self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: jElement, name: 'ItemFocusManager:ToggleDrop' } }));
6352            },
6353
6354            OpenDrop: function ()
6355            {
6356                var self, jElement;
6357
6358                self = this;
6359                jElement = $(self.element);
6360
6361                if (jElement.hasClass(self.options.HoverFocusClass))
6362                {
6363                    return;
6364                }
6365                jElement.addClass(self.options.HoverFocusClass);
6366                self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: jElement, name: 'ItemFocusManager:OpenDrop' } }));
6367            },
6368
6369            CloseDrop: function ()
6370            {
6371                var self, jElement;
6372
6373                self = this;
6374                jElement = $(self.element);
6375                jElement.removeClass(self.options.HoverFocusClass);
6376                self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: jElement, name: 'ItemFocusManager:CloseDrop' } }));
6377            }
6378
6379        });
6380}(jQuery));
6381
6382///////////////////////////////////////////////////////////////////////////////
6383// jQuery functions extensions.
6384///////////////////////////////////////////////////////////////////////////////
6385
6386(function ($)
6387{
6388    // --[ Truncate text ]-------------------------------------------------------------------------
6389
6390    jQuery.fn.truncate = function (max, settings)
6391    {
6392        settings = jQuery.extend({
6393            chars: /\s/,
6394            trail: ["...", ""]
6395        }, settings);
6396
6397
6398        var myResults = {},
6399            ie = jQuery.browser.msie;
6400
6401        function fixIE(o)
6402        {
6403            if (ie)
6404            {
6405                o.style.removeAttribute("filter");
6406            }
6407        }
6408        return this.each(function ()
6409        {
6410            var jQuerythis = jQuery(this),
6411                myStrOrig = jQuerythis.html().replace(/\r\n/gim, ""),
6412                myStr = myStrOrig,
6413                myRegEx = /<\/?[^<>]*\/?>/gim,
6414                myRegExArray,
6415                myRegExHash = {},
6416                myResultsKey = jQuery("*").index(this),
6417                c,
6418                endCap = 0,
6419                eachEl;
6420
6421
6422            //while ( ( myRegExArray = myRegEx.exec( myStr ) ) !== null )
6423            for (myRegExArray = myRegEx.exec(myStr); myRegExArray !== null; myRegExArray = myRegEx.exec(myStr))
6424            {
6425                myRegExHash[myRegExArray.index] = myRegExArray[0];
6426            }
6427
6428            myStr = jQuery.trim(myStr.split(myRegEx).join(""));
6429            if (myStr.length > max)
6430            {
6431                while (max < myStr.length)
6432                {
6433                    c = myStr.charAt(max);
6434                    if (c.match(settings.chars))
6435                    {
6436                        myStr = myStr.substring(0, max);
6437                        break;
6438                    }
6439                    max -= 1;
6440                }
6441
6442                if (myStrOrig.search(myRegEx) !== -1)
6443                {
6444                    endCap = 0;
6445                    for (eachEl in myRegExHash)
6446                    {
6447                        if (myRegExHash.hasOwnProperty(eachEl))
6448                        {
6449                            myStr = [myStr.substring(0, eachEl), myRegExHash[eachEl], myStr.substring(eachEl, myStr.length)].join("");
6450                            if (eachEl < myStr.length)
6451                            {
6452                                endCap = myStr.length;
6453                            }
6454                        }
6455                    }
6456                    jQuerythis.html([myStr.substring(0, endCap), myStr.substring(endCap, myStr.length).replace(/<(\w+)[^>]*>.*<\/\1>/gim, "").replace(/<(br|hr|img|input)[^<>]*\/?>/gim, "")].join(""));
6457                } else
6458                {
6459                    jQuerythis.html(myStr);
6460                }
6461                myResults[myResultsKey] = myStrOrig;
6462                jQuerythis.html(["<div class='truncate_less'>", jQuerythis.html(), settings.trail[0], "</div>"].join(""))
6463                    .find(".truncate_show", this).click(function ()
6464                    {
6465                        if (jQuerythis.find(".truncate_more").length === 0)
6466                        {
6467                            jQuerythis.append(["<div class='truncate_more' style='display: none;'>", myResults[myResultsKey], settings.trail[1], "</div>"].join(""))
6468                                .find(".truncate_hide").click(function ()
6469                                {
6470                                    jQuerythis.find(".truncate_more").css("background", "#fff").fadeOut("normal", function ()
6471                                    {
6472                                        jQuerythis.find(".truncate_less").css("background", "#fff").fadeIn("normal", function ()
6473                                        {
6474                                            fixIE(this);
6475                                            jQuery(this).css("background", "none");
6476                                        });
6477                                        fixIE(this);
6478                                    });
6479                                    return false;
6480                                });
6481                        }
6482                        jQuerythis.find(".truncate_less").fadeOut("normal", function ()
6483                        {
6484                            jQuerythis.find(".truncate_more").fadeIn("normal", function ()
6485                            {
6486                                fixIE(this);
6487                            });
6488                            fixIE(this);
6489                        });
6490                        jQuery(".truncate_show", jQuerythis).click(function ()
6491                        {
6492                            jQuerythis.find(".truncate_less").css("background", "#fff").fadeOut("normal", function ()
6493                            {
6494                                jQuerythis.find(".truncate_more").css("background", "#fff").fadeIn("normal", function ()
6495                                {
6496                                    fixIE(this);
6497                                    jQuery(this).css("background", "none");
6498                                });
6499                                fixIE(this);
6500                            });
6501                            return false;
6502                        });
6503                        return false;
6504                    });
6505            }
6506        });
6507    };
6508
6509    $.fn.extend({
6510        getRadioValue: function ()
6511        {
6512            return $(this).filter(':checked').val();
6513        },
6514        getYesNoRadioIsYes: function (alternateYesValue)
6515        {
6516            var yesValue, jCheckedElement;
6517            yesValue = (alternateYesValue === undefined) ? "True" : alternateYesValue;
6518            if ($(this).is('[type=hidden]'))
6519            {
6520                return ($(this).val() === yesValue);
6521            }
6522            jCheckedElement = $(this).filter(':checked');
6523            return (jCheckedElement.length !== 0 && jCheckedElement.val() === yesValue);
6524        },
6525        getYesNoCheckIsYes: function (alternateYesValue)
6526        {
6527            var yesValue, jCheckedElement;
6528            yesValue = (alternateYesValue === undefined) ? "True" : alternateYesValue;
6529            if ($(this).is('[type=hidden]'))
6530            {
6531                return ($(this).val() === yesValue);
6532            }
6533            jCheckedElement = $(this).filter(':checked');
6534            return (jCheckedElement.length !== 0 && jCheckedElement.val() === yesValue);
6535        },
6536        getYesNoDefaultRadio: function ()
6537        {
6538            var jCheckedElement;
6539            if ($(this).is('[type=hidden]'))
6540            {
6541                return FBUtilities.GetBoolOrNull($(this).val());
6542            }
6543            jCheckedElement = $(this).filter(':checked');
6544            if (jCheckedElement.length !== 0)
6545            {
6546                return FBUtilities.GetBoolOrNull(jCheckedElement.val());
6547            }
6548            return null;
6549        },
6550        getSelectBoxValue: function ()
6551        {
6552            var selectedValue, selectedOption;
6553            selectedValue = "";
6554            selectedOption = $(this).find("option:selected");
6555            if (selectedOption.length !== 0)
6556            {
6557                selectedValue = selectedOption.val();
6558            }
6559            return selectedValue;
6560        },
6561        getMultipleCheckedValues: function ()
6562        {
6563            return $(this).filter(":checked").map(function ()
6564            {
6565                return $(this).val();
6566            }).get();
6567        }
6568    });
6569}(jQuery));
6570
6571///////////////////////////////////////////////////////////////////////////////
6572// Masonry
6573///////////////////////////////////////////////////////////////////////////////
6574/*
6575$( window ).on( 'load', function ()
6576{
6577    $('.fbcms_contentbody.masonry').each(function()
6578    {
6579        var self = $(this);
6580        self.isotope(
6581            {
6582                itemSelector: '.fbcms_contentitem',
6583                masonry: 
6584                {
6585                    columnWidth: 1
6586                }
6587            }
6588        );
6589    });
6590} );
6591*/
6592
6593/*
6594    RotatorManager - Foxbright Rotator Manager
6595    TODO: update docs
6596    Javascript controller for managing simple drop logic and incorporating keyboard focus 
6597    into the drop behavior as well.
6598    
6599    The element selected by the widget (container element) will have a class added to it 
6600    to indicate that it is opened (HoverFocusClass). Selecting or focusing on the element 
6601
6602    
6603    Options:
6604    
6605    PermaLinkUrl                        default: null - must be set when called.
6606            Required.  The uri location of a permalink for this photo gallery.  Since this 
6607            is required and has no default, it must be set when initializing the manager.
6608    GalleryTitle                        default: null
6609            Optional.  A title for the gallery.
6610    RotatorContainerSelector            default: '.fbcms_imagerotator'
6611            Required.  Selector indicating the element that is to be used as the rotator 
6612            container.
6613    ShowButtons                         default: false
6614            Optional.  True to show buttons, false otherwise.
6615    ButtonPositionsPlayPauseModifer    default: 'upperright' [upperleft|upperright|lowerleft|lowerright]
6616            Optional; Required if ShowButtons is true.  Play/Pause button position. 
6617    ButtonPositionsPermaLinkModifer    default: 'upperright' [upperleft|upperright|lowerleft|lowerright]
6618            Optional; Required if ShowButtons is true.  Permalink button position. 
6619    ButtonPositionsNextPrevModifer     default: 'middle' [upperleft|upperright|middle|lowerleft|lowerright]
6620            Optional; Required if ShowButtons is true.  Next/Previous buttons position.
6621    ButtonPositionsPagerModifer        default: 'upperright' [upperleft|upperright|lowerleft|lowerright]
6622            Optional; Required if ShowButtons is true.  Pager button postion.
6623    ButtonClass                         default: 'fbpgro_button'
6624            Optional; Required if ShowButtons is true.  Class to be put on all buttons.
6625    PrevButtonClass                     default: 'fbpgro_prev'
6626            Optional; Required if ShowButtons is true.  Previous button class.
6627    PrevButtonIcon                      default: '<span class="fas fa-angle-left" aria-hidden="true"></span>'
6628            Optional; Required if ShowButtons is true.  Previous button icon.
6629    PrevButtonContent                   default: '<span class="screen-reader-text">Previous</span>'
6630            Optional; Required if ShowButtons is true.  Previous button content.
6631    NextButtonClass                     default: 'fbpgro_next'
6632            Optional; Required if ShowButtons is true.  Next button class.
6633    NextButtonIcon                      default: '<span class="fas fa-angle-right" aria-hidden="true"></span>'
6634            Optional; Required if ShowButtons is true.  Next button icon.
6635    NextButtonContent                   default: '<span class="screen-reader-text">Next</span>'
6636            Optional; Required if ShowButtons is true.  Next button content.
6637    PlayPauseButtonClass                default: 'fbpgro_playpause'
6638            Optional; Required if ShowButtons is true.  Play Pause button class.
6639    PlayPauseButtonIconPause            default: '<span class="fas fa-pause" aria-hidden="true"></span>'
6640            Optional; Required if ShowButtons is true.  Play Pause button icon when playing.
6641    PlayPauseButtonContentPause         default: '<span class="screen-reader-text">Pause</span>'
6642            Optional; Required if ShowButtons is true.  Play Pause button content when playing.
6643    PlayPauseButtonIconPlay             default: '<span class="fas fa-play" aria-hidden="true"></span>'
6644            Optional; Required if ShowButtons is true.  Play Pause button icon when paused.
6645    PlayPauseButtonContentPlay          default: '<span class="screen-reader-text">Play</span>'
6646            Optional; Required if ShowButtons is true.  Play Pause button content when paused.
6647    PermaLinkButtonClass                default: 'fbpgro_permalink'
6648            Optional; Required if ShowButtons is true.  Permalink button class.
6649    PermaLinkButtonIcon                 default: '<span class="fas fa-link" aria-hidden="true"></span>'
6650            Optional; Required if ShowButtons is true.  Permalink button icon.
6651    PermaLinkButtonContent              default: '<span class="screen-reader-text">Gallery Permalink</span>'
6652            Optional; Required if ShowButtons is true.  Permalink button content.
6653    ShowPager                           default: false
6654            Optional.  True to show pager, false otherwise.
6655    PagerClass                          default: 'fbpgro_pager'
6656            Optional; Required if ShowPager is true.  Pager class.
6657    ShowOverlay                         default: false
6658            Optional.  True to show overlay, false otherwise.
6659    OverlayClass                        default: 'fbpgro_overlay'
6660            Optional; Required if ShowOverlay is true.  Overlay class.
6661    OverlayTextWrapperClass             default: 'fbpgro_textwrapper'
6662            Optional; Required if ShowOverlay is true.  Class for wrapper around title and 
6663            description text elements.
6664    OverlayTitleClass                   default: 'fbpgro_title'
6665            Optional; Required if ShowOverlay is true.  Text element class.
6666    OverlayDescClass                    default: 'fbpgro_desc'
6667            Optional; Required if ShowOverlay is true.  Description element class.
6668    OverlayLinkWrapperClass             default: 'fbpgro_linkwrapper'
6669            Optional; Required if ShowOverlay is true.  Overlay link wrapper class.
6670    OverlayLinkClass                    default: 'fbpgro_link'
6671            Optional; Required if ShowOverlay is true.  Overlay link class.
6672    OverlayUpdateFunction               default: null 
6673            Optional.  An override for the overlay update function.  This will be called each 
6674            time a slide is changed.  
6675            The signature is: function(aContentBlock, aTitle, aDesc, aUrl, aUrlTarget, aUrlText)
6676    OnPreInitFunctions                  default: null
6677            Optional. Functions that may be called upon before initialization of the rotator, 
6678            but after the markup has been updated by the widget.
6679            The signature is: function(aCycleEvent, aCycleOptions)
6680    
6681    Methods:
6682    
6683    The widget is fully functional on its own, but can be remotely activated with the following 
6684    methods.
6685    
6686    OverlayUpdateFunction(aTitle, aDesc, aUrl, aUrlTarget, aUrlText)
6687            Called to toggle the widget open or closed.
6688    
6689    Examples:
6690    
6691    Since the function is built as a jQuery Widget, simply call the functions on the 
6692    container element directly.
6693    
6694    $('#content_1337').RotatorManager(
6695        {
6696            PermaLinkUrl: '/Core/Gallery/Index/1701/'
6697        }
6698    );
6699*/
6700(function ($)
6701{
6702    $.widget("ui.RotatorManager",
6703        {
6704            options:
6705            {
6706                PermaLinkUrl: null
6707                , GalleryTitle: null
6708                , RotatorContainerSelector: '.fbcms_imagerotator'
6709                , ShowButtons: false
6710                , ButtonPositionsPlayPauseModifer: 'upperright'
6711                , ButtonPositionsPermaLinkModifer: 'upperright'
6712                , ButtonPositionsNextPrevModifer: 'middle'
6713                , ButtonPositionsPagerModifer: 'upperright'
6714                , ButtonClass: 'fbpgro_button'
6715                , PrevButtonClass: 'fbpgro_prev'
6716                , NextButtonClass: 'fbpgro_next'
6717                , PlayPauseButtonClass: 'fbpgro_playpause'
6718                , PlayPauseButtonPlayClass: 'fbpgro_play'
6719                , PermaLinkButtonClass: 'fbpgro_permalink'
6720                , ShowPager: false
6721                , PagerClass: 'fbpgro_pager'
6722                , ShowOverlay: false
6723                , OverlayClass: 'fbpgro_overlay'
6724                , OverlayTextWrapperClass: 'fbpgro_textwrapper'
6725                , OverlayTitleClass: 'fbpgro_title'
6726                , OverlayDescClass: 'fbpgro_desc'
6727                , OverlayLinkWrapperClass: 'fbpgro_linkwrapper'
6728                , OverlayLinkClass: 'fbpgro_link'
6729                , OverlayUpdateFunction: null
6730                , OnPreInitFunctions: null
6731            },
6732
6733            _AddButtons: false,
6734            _AddPager: false,
6735            _AddOverlay: false,
6736
6737            _destroy: function ()
6738            {
6739                $(this.element).find(this.options.RotatorContainerSelector).cycle('destroy');
6740            },
6741
6742            _init: function ()
6743            {
6744                var self = this;
6745                var jElement = $(self.element);
6746                var lElementId = $(jElement).attr('id');
6747
6748                var lRotatorContainer = jElement.find(self.options.RotatorContainerSelector);
6749
6750                if (lRotatorContainer.length !== 1)
6751                {
6752                    FBUtilities.LogMessage('RotatorManager._init - incorrect number of rotators found.');
6753                    return;
6754                }
6755
6756                jElement.addClass('fbcmsrotator');
6757                lRotatorContainer.cycle();
6758                self._updatePlayPauseButton();
6759            },
6760
6761            _create: function ()
6762            {
6763                var self = this;
6764                var jElement = $(self.element);
6765                var lElementId = $(jElement).attr('id');
6766
6767                var lRotatorContainer = jElement.find(self.options.RotatorContainerSelector);
6768
6769                if (lRotatorContainer.length !== 1)
6770                {
6771                    FBUtilities.LogMessage('RotatorManager._create - incorrect number of rotators found.');
6772                    return;
6773                }
6774
6775                if (self._initOptions() === false)
6776                {
6777                    FBUtilities.LogMessage('RotatorManager._create - options not initialized.');
6778                    return;
6779                }
6780
6781                /*if (FBUtilities.IsStringEmptyOrWhitespace(self.options.PermaLinkUrl))
6782                {
6783                    FBUtilities.LogMessage('RotatorManager - required settings null or empty.');
6784                    return;
6785                }*/
6786
6787                self._createButtons(true);
6788                self._createPager();
6789                self._createOverlay();
6790                self._createGeneralEvents(true);
6791            },
6792
6793            ReInit: function ()
6794            {
6795                var self = this;
6796                var jElement = $(self.element);
6797                var lElementId = $(jElement).attr('id');
6798
6799                var lRotatorContainer = jElement.find(self.options.RotatorContainerSelector);
6800
6801                if (lRotatorContainer.length !== 1)
6802                {
6803                    FBUtilities.LogMessage('RotatorManager.ReInit - incorrect number of rotators found.');
6804                    return;
6805                }
6806
6807                if (self._initOptions() === false)
6808                {
6809                    FBUtilities.LogMessage('RotatorManager.ReInit - options not initialized.');
6810                    return;
6811                }
6812
6813                /*if (FBUtilities.IsStringEmptyOrWhitespace(self.options.PermaLinkUrl))
6814                {
6815                    FBUtilities.LogMessage('RotatorManager.ReInit - required settings null or empty.');
6816                    return;
6817                }*/
6818
6819                self._createButtons(false);
6820                self._createPager();
6821                self._createOverlay();
6822                self._createGeneralEvents(false);
6823            },
6824
6825            _initOptions: function ()
6826            {
6827                var self = this;
6828                var jElement = $(self.element);
6829                var lElementId = $(jElement).attr('id');
6830
6831                var lRotatorContainer = jElement.find(self.options.RotatorContainerSelector);
6832
6833                if (lRotatorContainer.length !== 1)
6834                {
6835                    FBUtilities.LogMessage('RotatorManager - incorrect number of rotators found.');
6836                    return false;
6837                }
6838
6839                if (!FBUtilities.IsStringEmptyOrWhitespace(lRotatorContainer.data('fbpgr-permalink')))
6840                {
6841                    self.options.PermaLinkUrl = lRotatorContainer.data('fbpgr-permalink');
6842                }
6843                if (!FBUtilities.IsStringEmptyOrWhitespace(lRotatorContainer.data('fbpgr-title')))
6844                {
6845                    self.options.GalleryTitle = lRotatorContainer.data('fbpgr-title');
6846                }
6847                if (!FBUtilities.IsStringEmptyOrWhitespace(lRotatorContainer.data('fbpgr-showbuttons')))
6848                {
6849                    self.options.ShowButtons = lRotatorContainer.data('fbpgr-showbuttons');
6850                }
6851                if (!FBUtilities.IsStringEmptyOrWhitespace(lRotatorContainer.data('fbpgr-playpausemod')))
6852                {
6853                    self.options.ButtonPositionsPlayPauseModifer = lRotatorContainer.data('fbpgr-playpausemod');
6854                }
6855                if (!FBUtilities.IsStringEmptyOrWhitespace(lRotatorContainer.data('fbpgr-nextprevmod')))
6856                {
6857                    self.options.ButtonPositionsNextPrevModifer = lRotatorContainer.data('fbpgr-nextprevmod');
6858                }
6859                if (!FBUtilities.IsStringEmptyOrWhitespace(lRotatorContainer.data('fbpgr-permalinkmod')))
6860                {
6861                    self.options.ButtonPositionsPermaLinkModifer = lRotatorContainer.data('fbpgr-permalinkmod');
6862                }
6863                if (!FBUtilities.IsStringEmptyOrWhitespace(lRotatorContainer.data('fbpgr-showpager')))
6864                {
6865                    self.options.ShowPager = lRotatorContainer.data('fbpgr-showpager');
6866                }
6867                if (!FBUtilities.IsStringEmptyOrWhitespace(lRotatorContainer.data('fbpgr-pagermod')))
6868                {
6869                    self.options.ButtonPositionsPagerModifer = lRotatorContainer.data('fbpgr-pagermod');
6870                }
6871                if (!FBUtilities.IsStringEmptyOrWhitespace(lRotatorContainer.data('fbpgr-showoverlay')))
6872                {
6873                    self.options.ShowOverlay = lRotatorContainer.data('fbpgr-showoverlay');
6874                }
6875                if (!FBUtilities.IsStringEmptyOrWhitespace(lRotatorContainer.data('fbpgr-overlayfunc')))
6876                {
6877                    self.options.OverlayUpdateFunction = lRotatorContainer.data('fbpgr-overlayfunc');
6878                }
6879
6880                if (!FBUtilities.IsStringEmptyOrWhitespace(self.options.ButtonPositionsPlayPauseModifer)
6881                    && !FBUtilities.IsStringEmptyOrWhitespace(self.options.ButtonPositionsPermaLinkModifer)
6882                    && !FBUtilities.IsStringEmptyOrWhitespace(self.options.ButtonPositionsNextPrevModifer)
6883                    && !FBUtilities.IsStringEmptyOrWhitespace(self.options.ButtonPositionsPagerModifer)
6884                    && !FBUtilities.IsStringEmptyOrWhitespace(self.options.ButtonClass)
6885                    && !FBUtilities.IsStringEmptyOrWhitespace(self.options.PrevButtonClass)
6886                    && !FBUtilities.IsStringEmptyOrWhitespace(self.options.NextButtonClass)
6887                    && !FBUtilities.IsStringEmptyOrWhitespace(self.options.PlayPauseButtonClass)
6888                    && !FBUtilities.IsStringEmptyOrWhitespace(self.options.PlayPauseButtonPlayClass)
6889                    && !FBUtilities.IsStringEmptyOrWhitespace(self.options.PermaLinkButtonClass))
6890                {
6891                    self._AddButtons = self.options.ShowButtons;
6892                }
6893
6894                if (!FBUtilities.IsStringEmptyOrWhitespace(self.options.PagerClass))
6895                {
6896                    self._AddPager = self.options.ShowPager;
6897                }
6898
6899                if (!FBUtilities.IsStringEmptyOrWhitespace(self.options.OverlayClass)
6900                    && !FBUtilities.IsStringEmptyOrWhitespace(self.options.OverlayTextWrapperClass)
6901                    && !FBUtilities.IsStringEmptyOrWhitespace(self.options.OverlayTitleClass)
6902                    && !FBUtilities.IsStringEmptyOrWhitespace(self.options.OverlayDescClass)
6903                    && !FBUtilities.IsStringEmptyOrWhitespace(self.options.OverlayLinkWrapperClass)
6904                    && !FBUtilities.IsStringEmptyOrWhitespace(self.options.OverlayLinkClass))
6905                {
6906                    self._AddOverlay = self.options.ShowOverlay;
6907                }
6908
6909                //initialize the pre-init callbacks list.
6910                if (FBUtilities.IsParameterUndefinedOrEmpty(self.options.OnPreInitFunctions))
6911                {
6912                    //it's null or empty, create it.
6913                    self.options.OnPreInitFunctions = [];
6914                }
6915                else if (
6916                    self.options.OnPreInitFunctions !== undefined
6917                    && self.options.OnPreInitFunctions.constructor !== undefined
6918                    && self.options.OnPreInitFunctions.call !== undefined
6919                    && self.options.OnPreInitFunctions.apply !== undefined
6920                ) 
6921                {
6922                    //it's a function, add it to the array.
6923                    var lFunction = self.options.OnPreInitFunctions;
6924                    self.options.OnPreInitFunctions = [];
6925                    self.options.OnPreInitFunctions.push(lFunction);
6926                }
6927                else if ($.isArray(self.options.OnPreInitFunctions))
6928                {
6929                    //it's an array, we can add to it.
6930                }
6931                else
6932                {
6933                    //dunno what it is. nuke it and create it anew.
6934                    self.options.OnPreInitFunctions = [];
6935                }
6936            },
6937
6938            _createButtons: function (aIsInitialCreation)
6939            {
6940                var self = this;
6941                var jElement = $(self.element);
6942                var lElementId = $(jElement).attr('id');
6943                var lRotatorContainer = jElement.find(self.options.RotatorContainerSelector);
6944
6945                if (self._AddButtons !== true)
6946                {
6947                    FBUtilities.LogMessage('RotatorManager - not adding buttons.');
6948                    return;
6949                }
6950
6951                //TODO: cleanup
6952                /*
6953                var lPermalinkLabel = '';
6954                if (!FBUtilities.IsStringEmptyOrWhitespace(self.options.GalleryTitle))
6955                {
6956                    lPermalinkLabel = ' aria-label="' + self.options.GalleryTitle + '"';
6957                }
6958
6959                jElement.append('<a class="' + self.options.ButtonClass + ' ' + self.options.PrevButtonClass + '" href="#">
6959' + self.options.PrevButtonIcon + self.options.PrevButtonContent + '</a>');
6960                jElement.append('<a class="' + self.options.ButtonClass + ' ' + self.options.NextButtonClass + '" href="#">' + self.options.NextButtonIcon + self.options.NextButtonContent + '</a>');
6961                jElement.append('<a class="' + self.options.ButtonClass + ' ' + self.options.PlayPauseButtonClass + '" href="#">' + self.options.PlayPauseButtonIconPause + self.options.PlayPauseButtonContentPause + '</a>');
6962                if (!FBUtilities.IsStringEmptyOrWhitespace(self.options.PermaLinkUrl))
6963                {
6964                    jElement.append('<a class="' + self.options.ButtonClass + ' ' + self.options.PermaLinkButtonClass + '" href="' + self.options.PermaLinkUrl + '" ' + lPermalinkLabel + '>' + self.options.PermaLinkButtonIcon + self.options.PermaLinkButtonContent + '</a>');
6965                }
6966                */
6967
6968                if (FBCommon_1_2.isInEditor() === false
6969                    && aIsInitialCreation === true)
6970                {
6971                    self.options.OnPreInitFunctions.push(
6972                        function (aCycleEvent, aCycleOptions)
6973                        {
6974                            jElement.on(
6975                                "click",
6976                                '.' + self.options.PrevButtonClass,
6977                                function (aEvent)
6978                                {
6979                                    aEvent.preventDefault();
6980                                    lRotatorContainer.cycle('prev');
6981                                    return false;
6982                                }
6983                            );
6984                            jElement.on(
6985                                "click",
6986                                '.' + self.options.NextButtonClass,
6987                                function (aEvent)
6988                                {
6989                                    aEvent.preventDefault();
6990                                    lRotatorContainer.cycle('next');
6991                                    return false;
6992                                }
6993                            );
6994                            jElement.on(
6995                                "click",
6996                                '.' + self.options.PlayPauseButtonClass,
6997                                function (aEvent)
6998                                {
6999                                    aEvent.preventDefault();
7000                                    var lMyButton = $(aEvent.target).closest('.' + self.options.PlayPauseButtonClass);
7001                                    if (aCycleOptions.paused === true)
7002                                    {
7003                                        lRotatorContainer.cycle('resume');
7004                                    }
7005                                    else
7006                                    {
7007                                        lRotatorContainer.cycle('pause');
7008                                    }
7009                                    self._updatePlayPauseButton();
7010                                    return false;
7011                                }
7012                            );
7013                        }
7014                    );
7015                }
7016
7017                var lPrevButton = jElement.find('.' + self.options.PrevButtonClass);
7018                var lNextButton = jElement.find('.' + self.options.NextButtonClass);
7019                var lPlayPauseButton = jElement.find('.' + self.options.PlayPauseButtonClass);
7020                var lPermaLinkButton = jElement.find('.' + self.options.PermaLinkButtonClass);
7021
7022                var lNextPrevHPosition = (self.options.ButtonPositionsNextPrevModifer.indexOf('left') !== -1) === true ? 'left' : 'right';
7023                var lPlayPauseHPosition = (self.options.ButtonPositionsPlayPauseModifer.indexOf('left') !== -1) === true ? 'left' : 'right';
7024                var lPermaLinkHPosition = (self.options.ButtonPositionsPermaLinkModifer.indexOf('left') !== -1) === true ? 'left' : 'right';
7025
7026                var lNextPrevVPosition = (self.options.ButtonPositionsNextPrevModifer.indexOf('upper') !== -1) === true ? 'top' :
7027                    ((self.options.ButtonPositionsNextPrevModifer.indexOf('lower') !== -1) === true ? 'bottom' : 'middle');
7028                var lPlayPauseVPosition = (self.options.ButtonPositionsPlayPauseModifer.indexOf('upper') !== -1) === true ? 'top' : 'bottom';
7029                var lPermaLinkVPosition = (self.options.ButtonPositionsPermaLinkModifer.indexOf('upper') !== -1) === true ? 'top' : 'bottom';
7030
7031                lPermaLinkButton.css(lPermaLinkVPosition, 0);
7032                lPlayPauseButton.css(lPlayPauseVPosition, 0);
7033
7034                if (lNextPrevVPosition === "top"
7035                    || lNextPrevVPosition === "bottom")
7036                {
7037                    lPrevButton.css(lNextPrevVPosition, 0);
7038                    lNextButton.css(lNextPrevVPosition, 0);
7039                }
7040                else
7041                {
7042                    lPrevButton.addClass('fbcpgro_over fbcpgro_large');
7043                    lNextButton.addClass('fbcpgro_over fbcpgro_large');
7044                }
7045
7046                // permalink button position.
7047                var lPLHPosition = 0;
7048                var lPLBWidth = 0;
7049                if (lPermaLinkButton.length > 0)
7050                {
7051                    lPermaLinkButton.css(lPermaLinkHPosition, lPLHPosition + (lPLHPosition > 0 ? 'px' : ''));
7052                    lPLBWidth = lPermaLinkButton.width();
7053                }
7054
7055                // play/pause button position.
7056                var lPPHPosition = 0;
7057                if (lPermaLinkHPosition === lPlayPauseHPosition
7058                    && lPermaLinkVPosition === lPlayPauseVPosition)
7059                {
7060                    lPPHPosition += lPLBWidth;
7061                }
7062                lPlayPauseButton.css(lPlayPauseHPosition, lPPHPosition + (lPPHPosition > 0 ? 'px' : ''));
7063
7064                // next/prev button position.
7065                if (lNextPrevVPosition.indexOf('top') >= 0
7066                    || lNextPrevVPosition.indexOf('bottom') >= 0)
7067                {
7068                    var lNPHPosition = 0;
7069                    if (lPermaLinkHPosition === lNextPrevHPosition
7070                        && lPermaLinkVPosition === lNextPrevVPosition)
7071                    {
7072                        lNPHPosition += lPLBWidth;
7073                    }
7074                    if (lPlayPauseHPosition === lNextPrevHPosition
7075                        && lPlayPauseVPosition === lNextPrevVPosition)
7076                    {
7077                        lNPHPosition += lPlayPauseButton.width();
7078                    }
7079                    if (lNextPrevHPosition.indexOf('right') >= 0)
7080                    {
7081                        lNextButton.css(lNextPrevHPosition, lNPHPosition + (lNPHPosition > 0 ? 'px' : ''));
7082                        lNPHPosition += lNextButton.width();
7083                        lPrevButton.css(lNextPrevHPosition, lNPHPosition + (lNPHPosition > 0 ? 'px' : ''));
7084                    }
7085                    else
7086                    {
7087                        lPrevButton.css(lNextPrevHPosition, lNPHPosition + (lNPHPosition > 0 ? 'px' : ''));
7088                        lNPHPosition += lPrevButton.width();
7089                        lNextButton.css(lNextPrevHPosition, lNPHPosition + (lNPHPosition > 0 ? 'px' : ''));
7090                    }
7091                }
7092            },
7093
7094            _createPager: function ()
7095            {
7096                var self = this;
7097                var jElement = $(self.element);
7098                var lElementId = $(jElement).attr('id');
7099                var lRotatorContainer = jElement.find(self.options.RotatorContainerSelector);
7100
7101                if (self._AddPager !== true)
7102                {
7103                    FBUtilities.LogMessage('RotatorManager - not adding pager.');
7104                    return;
7105                }
7106
7107                jElement.append('<div class="' + self.options.PagerClass + '"></div>');
7108
7109                var lPrevButton = jElement.find('.' + self.options.PrevButtonClass);
7110                var lNextButton = jElement.find('.' + self.options.NextButtonClass);
7111                var lPlayPauseButton = jElement.find('.' + self.options.PlayPauseButtonClass);
7112                var lPermaLinkButton = jElement.find('.' + self.options.PermaLinkButtonClass);
7113                var lPager = jElement.find('.' + self.options.PagerClass);
7114
7115                var lPLBWidth = 0;
7116                if (lPermaLinkButton.length > 0)
7117                {
7118                    lPLBWidth = lPermaLinkButton.width();
7119                }
7120
7121                var lNextPrevHPosition = (self.options.ButtonPositionsNextPrevModifer.indexOf('left') !== -1) === true ? 'left' : 'right';
7122                var lPlayPauseHPosition = (self.options.ButtonPositionsPlayPauseModifer.indexOf('left') !== -1) === true ? 'left' : 'right';
7123                var lPermaLinkHPosition = (self.options.ButtonPositionsPermaLinkModifer.indexOf('left') !== -1) === true ? 'left' : 'right';
7124                var lPagerHPosition = (self.options.ButtonPositionsPagerModifer.indexOf('left') !== -1) === true ? 'left' :
7125                    ((self.options.ButtonPositionsPagerModifer.indexOf('right') !== -1) === true ? 'right' : 'center');
7126
7127                var lNextPrevVPosition = (self.options.ButtonPositionsNextPrevModifer.indexOf('upper') !== -1) === true ? 'top' :
7128                    ((self.options.ButtonPositionsNextPrevModifer.indexOf('lower') !== -1) === true ? 'bottom' : 'middle');
7129                var lPlayPauseVPosition = (self.options.ButtonPositionsPlayPauseModifer.indexOf('upper') !== -1) === true ? 'top' : 'bottom';
7130                var lPermaLinkVPosition = (self.options.ButtonPositionsPermaLinkModifer.indexOf('upper') !== -1) === true ? 'top' : 'bottom';
7131                var lPagerVPosition = (self.options.ButtonPositionsPagerModifer.indexOf('upper') !== -1) === true ? 'top' : 'bottom';
7132
7133                lPager.css(lPagerVPosition, 0);
7134
7135                //pager position.
7136                var lPHPosition = 0;
7137                if (lPermaLinkHPosition === lPagerHPosition
7138                    && lPermaLinkVPosition === lPagerVPosition)
7139                {
7140                    lPHPosition += lPLBWidth;
7141                }
7142                if (lPlayPauseHPosition === lPagerHPosition
7143                    && lPlayPauseVPosition === lPagerVPosition)
7144                {
7145                    lPHPosition += lPlayPauseButton.width();
7146                }
7147                if (lNextPrevHPosition === lPagerHPosition
7148                    && lNextPrevVPosition === lPagerVPosition)
7149                {
7150                    lPHPosition += lNextButton.width();
7151                    lPHPosition += lPrevButton.width();
7152                }
7153
7154                if (lPagerHPosition.indexOf('center') >= 0)
7155                {
7156                    lPager.css('left', '50%');
7157                    lPager.css({ 'transform': 'translate(-50%, 0)' });
7158                }
7159                else
7160                {
7161                    lPager.css(lPagerHPosition, lPHPosition + (lPHPosition > 0 ? 'px' : ''));
7162                }
7163            },
7164
7165            _createOverlay: function ()
7166            {
7167                var self = this;
7168                var jElement = $(self.element);
7169                var lElementId = $(jElement).attr('id');
7170                var lRotatorContainer = jElement.find(self.options.RotatorContainerSelector);
7171
7172                if (self._AddOverlay !== true)
7173                {
7174                    FBUtilities.LogMessage('RotatorManager - not adding overlay.');
7175                    return;
7176                }
7177
7178                jElement.append('<div class="' + self.options.OverlayClass + '"></div>');
7179
7180                var lOverlay = jElement.find('.' + self.options.OverlayClass);
7181
7182                lOverlay.append('<div class="' + self.options.OverlayTextWrapperClass + '"></div>');
7183                var lTextWrapper = lOverlay.find('.' + self.options.OverlayTextWrapperClass);
7184
7185                lTextWrapper.append('<div class="' + self.options.OverlayTitleClass + '"></div>');
7186                lTextWrapper.append('<div class="' + self.options.OverlayDescClass + '"></div>');
7187            },
7188
7189            _createGeneralEvents: function (aIsInitialCreation)
7190            {
7191                var self = this;
7192                var jElement = $(self.element);
7193                var lElementId = $(jElement).attr('id');
7194                var lRotatorContainer = jElement.find(self.options.RotatorContainerSelector);
7195
7196                if (FBCommon_1_2.isInEditor() === false
7197                    && aIsInitialCreation === true)
7198                {
7199                    self.options.OnPreInitFunctions.push(
7200                        function (aCycleEvent, aCycleOptions)
7201                        {
7202                            jElement.on(
7203                                "click",
7204                                '[data-fbpg-url]',
7205                                function (aEvent)
7206                                {
7207                                    aEvent.preventDefault();
7208                                    var lUrl = $(aEvent.target).data('fbpg-url');
7209                                    var lTarget = $(aEvent.target).data('fbpg-urltarget');
7210                                    if (FBUtilities.IsParameterUndefinedOrEmpty(lTarget)
7211                                        && !FBUtilities.IsParameterUndefinedOrEmpty(lUrl))
7212                                    {
7213                                        document.location = lUrl;
7214                                    }
7215                                    else if (!FBUtilities.IsParameterUndefinedOrEmpty(lUrl))
7216                                    {
7217                                        window.open(
7218                                            $(aEvent.target).data('fbpg-url'),
7219                                            $(aEvent.target).data('fbpg-urltarget')
7220                                        );
7221                                    }
7222                                    return false;
7223                                }
7224                            );
7225                        }
7226                    );
7227                }
7228
7229                lRotatorContainer.on(
7230                    'cycle-pre-initialize',
7231                    function (aCycleEvent, aCycleOptions)
7232                    {
7233                        if (FBUtilities.IsParameterUndefinedOrEmpty(self.options.OnPreInitFunctions))
7234                        {
7235                            return;
7236                        }
7237                        for (var lIndex = 0, lLength = self.options.OnPreInitFunctions.length; lIndex < lLength; lIndex++)
7238                        {
7239                            self.options.OnPreInitFunctions[lIndex](aCycleEvent, aCycleOptions);
7240                        }//endfor
7241                    }
7242                );
7243
7244                lRotatorContainer.on(
7245                    'cycle-update-view',
7246                    function (_event, _options, _slideOptions, aCurrentSlideElement)
7247                    {
7248                        let slide = $(aCurrentSlideElement);
7249                        let img = slide.find('img');
7250                        let title = img.data('fbpg-title');
7251                        let desc = img.data('fbpg-desc');
7252                        let url = img.data('fbpg-url');
7253                        let urlTarget = img.data('fbpg-urltarget');
7254                        let linkText = img.data('fbpg-linktext');
7255                        let linkAriaLabel = img.data('fbpg-linkarialabel');
7256
7257                        if (!FBUtilities.IsParameterUndefinedOrEmpty(self.options.OverlayUpdateFunction))
7258                        {
7259                            self.options.OverlayUpdateFunction(jElement, title, desc, url, urlTarget, linkText, linkAriaLabel);
7260                        }
7261                        else
7262                        {
7263                            self.UpdateRotatorOverlay(title, desc, url, urlTarget, linkText, linkAriaLabel);
7264                        }
7265                    }
7266                );
7267            },
7268
7269            _addOverlayLink: functio
7269n (aLinkText, aUrl, aUrlTarget, aLinkAriaLabel)
7270            {
7271                var self = this;
7272                var jElement = $(self.element);
7273
7274                var lShowLink = !FBUtilities.IsStringEmptyOrWhitespace(aUrl)
7275                    && !FBUtilities.IsStringEmptyOrWhitespace(aLinkText);
7276                if (lShowLink === false)
7277                {
7278                    return;
7279                }
7280
7281                var lOverlay = jElement.find('.' + self.options.OverlayClass);
7282                var lTextWrapper = lOverlay.find('.' + self.options.OverlayTextWrapperClass);
7283                lTextWrapper.append('<div class="' + self.options.OverlayLinkWrapperClass + '"></div>');
7284                var lLinkWrapper = lOverlay.find('.' + self.options.OverlayLinkWrapperClass);
7285                lLinkWrapper.append('<a class="' + self.options.OverlayLinkClass + '" href="#"></a>');
7286                var lLinkElement = lLinkWrapper.find('.' + self.options.OverlayLinkClass);
7287
7288                lLinkElement.html(aLinkText);
7289                lLinkElement.attr('href', aUrl);
7290
7291                if (!FBUtilities.IsStringEmptyOrWhitespace(aUrlTarget) && aUrlTarget !== '_self')
7292                {
7293                    lLinkElement.attr('target', aUrlTarget);
7294                }
7295
7296                if (!FBUtilities.IsStringEmptyOrWhitespace(aLinkAriaLabel))
7297                {
7298                    lLinkElement.attr('aria-label', aLinkAriaLabel);
7299                }
7300            },
7301
7302            _updatePlayPauseButton: function ()
7303            {
7304                var self = this;
7305                var jElement = $(self.element);
7306                var lButton = jElement.find('.' + self.options.PlayPauseButtonClass);
7307                var lRotatorContainer = jElement.find(self.options.RotatorContainerSelector);
7308                var lCycleOptions = lRotatorContainer.data('cycle.opts');
7309                if (lCycleOptions.paused === true)
7310                {
7311                    lButton.removeClass(self.options.PlayPauseButtonPlayClass);
7312                }
7313                else
7314                {
7315                    lButton.addClass(self.options.PlayPauseButtonPlayClass);
7316                }
7317            },
7318
7319            UpdateRotatorOverlay: function (aTitle, aDesc, aUrl, aUrlTarget, aLinkText, aLinkAriaLabel)
7320            {
7321                var self = this;
7322                var jElement = $(self.element);
7323
7324                var lOverlay = jElement.find('.' + self.options.OverlayClass);
7325
7326                if (lOverlay.length === 0)
7327                {
7328                    return;
7329                }
7330                lOverlay.hide();
7331
7332                if (FBUtilities.IsStringEmptyOrWhitespace(aTitle)
7333                    && FBUtilities.IsStringEmptyOrWhitespace(aDesc)
7334                    && (FBUtilities.IsStringEmptyOrWhitespace(aUrl) || FBUtilities.IsStringEmptyOrWhitespace(aLinkText)))
7335                {
7336                    return;
7337                }
7338
7339                var lTitleElement = lOverlay.find('.' + self.options.OverlayTitleClass);
7340                var lDescElement = lOverlay.find('.' + self.options.OverlayDescClass);
7341                var lLinkElement = lOverlay.find('.' + self.options.OverlayLinkClass);
7342
7343                var lTextWrapElement = lOverlay.find('.' + self.options.OverlayTextWrapperClass);
7344                var lLinkWrapElement = lOverlay.find('.' + self.options.OverlayLinkWrapperClass);
7345                lLinkWrapElement.remove();
7346
7347                var lShowTitle = !FBUtilities.IsStringEmptyOrWhitespace(aTitle);
7348                var lShowDesc = !FBUtilities.IsStringEmptyOrWhitespace(aDesc);
7349                var lShowLink = !FBUtilities.IsStringEmptyOrWhitespace(aUrl);
7350
7351                if (lShowTitle)
7352                {
7353                    lTitleElement.html(aTitle);
7354                }
7355                if (lShowDesc)
7356                {
7357                    lDescElement.html(aDesc);
7358                }
7359
7360                if (lShowTitle)
7361                {
7362                    lTitleElement.show();
7363                }
7364                else
7365                {
7366                    lTitleElement.hide();
7367                }
7368                if (lShowDesc)
7369                {
7370                    lDescElement.show();
7371                }
7372                else
7373                {
7374                    lDescElement.hide();
7375                }
7376
7377                self._addOverlayLink(aLinkText, aUrl, aUrlTarget, aLinkAriaLabel);
7378
7379                if (lShowTitle
7380                    || lShowDesc
7381                    || lShowLink)
7382                {
7383                    lOverlay.show();
7384                }
7385            }
7386        });
7387
7388    // load it on all instances found when the page is loaded.
7389    $(document).ready(
7390        function ()
7391        {
7392            $.each(
7393                $('.fbcms_photogallery .fbcms_imagerotator'),
7394                function (aIndex, aValue)
7395                {
7396                    var lSelector = '#' + $(aValue).parent('.fbcms_photogallery').attr('id');
7397                    $(lSelector).RotatorManager();
7398                }
7399            );
7400            /*
7401            $.each(
7402                $('.fbcms_media .fbcms_imagerotator'),
7403                function (aIndex, aValue)
7404                {
7405                    var lSelector = '#' + $(aValue).parent('.fbcms_media').attr('id');
7406                    $(lSelector).RotatorManager();
7407                }
7408            );*/
7409        }
7410    );
7411
7412    // to reinit:
7413    /*
7414    if ($('#ElementId').is('.fbcmsrotator'))
7415    {
7416        $('#ElementId').RotatorManager('ReInit');
7417    }
7418    else
7419    {
7420        $('#ElementId').RotatorManager();
7421    }
7422    */
7423
7424})(jQuery);
7425
7426
7427
7428
7429/*
7430    SpotlightManager - Foxbright Spotlight Manager
7431    
7432    Javascript controller for managing spotlights.
7433    TODO: update docs
7434
7435    Options:
7436    
7437    PermaLinkUrl                        default: null - must be set when called.
7438            Required.  The uri location of a permalink for this spotlight gallery.  Since 
7439            this is required and has no default, it must be set when initializing the 
7440            manager.
7441    GalleryTitle                        default: null
7442            Optional.  A title for the spotlight gallery.
7443    ContentHeaderSelector               default: '.fbcms_contentheader'
7444            Required.  Selector indicating the element that is to be used as the content 
7445            header container.
7446    ContentFooterSelector               default: '.fbcms_contentfooter'
7447            Required.  Selector indicating the element that is to be used as the content 
7448            footer container.
7449    RotatorContainerSelector            default: '.fbcms_spotlights_list'
7450            Required.  Selector indicating the element that is to be used as the rotator 
7451            container.
7452    ControlsClass                       default: 'fbcms_spotlight_controls'
7453            Required.  Class to be used for controls container in the header and/or 
7454            footer.
7455    ButtonPositionsHeaderModifer        default: 'left' [left|right]
7456            Optional; Required if any Show*Button* option is true.  Header Controls 
7457            alignment. 
7458    ButtonPositionsFooterModifer        default: 'left' [left|right]
7459            Optional; Required if any Show*Button* option is true.  Footer Controls 
7460            alignment. 
7461    ButtonClass                         default: 'spotlight_button'
7462            Optional; Required if any Show*Button* option is true.  Class to be put on 
7463            all buttons.
7464    ShowNextPrevButtons                 default: true
7465            Optional.  True to show next & previous buttons, false otherwise.
7466    ButtonPositionsNextPrevModifer      default: 'bottom' [top|bottom|middle|both]
7467            Optional; Required if ShowNextPrevButtons option is true.  Next & previous 
7468            button position. 
7469    PrevButtonClass                     default: 'spotlight_prev'
7470            Optional; Required if ShowNextPrevButtons is true.  Previous button class.
7471    PrevButtonIcon                      default: '<span class="fas fa-angle-left" aria-hidden="true"></span>'
7472            Optional; Required if ShowNextPrevButtons is true.  Previous button icon.
7473    PrevButtonContent                   default: '<span class="screen-reader-text">Previous</span>'
7474            Optional; Required if ShowNextPrevButtons is true.  Previous button content.
7475    NextButtonClass                     default: 'spotlight_next'
7476            Optional; Required if ShowNextPrevButtons is true.  Next button class.
7477    NextButtonIcon                      default: '<span class="fas fa-angle-right" aria-hidden="true"></span>'
7478            Optional; Required if ShowNextPrevButtons is true.  Next button icon.
7479    NextButtonContent                   default: '<span class="screen-reader-text">Next</span>'
7480            Optional; Required if ShowNextPrevButtons is true.  Next button content.
7481    ShowPlayPauseButton                 default: true
7482            Optional.  True to show play/pause button, false otherwise.
7483    ButtonPositionsPlayPauseModifer     default: 'bottom' [top|bottom|both]
7484            Optional; Required if ShowPlayPauseButton option is true.  Play/Pause button 
7485            position. 
7486    PlayPauseButtonClass                default: 'spotlight_playpause'
7487            Optional; Required if ShowPlayPauseButton is true.  Play Pause button class.
7488    PlayPauseButtonIconPause            default: '<span class="fas fa-pause" aria-hidden="true"></span>'
7489            Optional; Required if ShowPlayPauseButton is true.  Play Pause button icon when playing.
7490    PlayPauseButtonContentPause         default: '<span class="screen-reader-text">Pause</span>'
7491            Optional; Required if ShowPlayPauseButton is true.  Play Pause button content when playing.
7492    PlayPauseButtonIconPlay             default: '<span class="fas fa-play" aria-hidden="true"></span>'
7493            Optional; Required if ShowPlayPauseButton is true.  Play Pause button icon when paused.
7494    PlayPauseButtonContentPlay          default: '<span class="screen-reader-text">Play</span>'
7495            Optional; Required if ShowPlayPauseButton is true.  Play Pause button content when paused. 
7496    ShowPager                           default: true
7497            Optional.  True to show the pager, false otherwise.
7498    PagerClass                          default: 'fbcms_spotlight_pager'
7499            Optional; Required if ShowPager or ShowNextPrevButtons is true.  Class to 
7500            be used for element to contain pager and next/prev classes.
7501    PagesClass                          default: 'spotlight_pages'
7502            Optional; Required if ShowPager is true.  Class to be used for the element 
7503            to contain page buttons.
7504    ButtonPositionsPagerModifer      default: 'bottom' [top|bottom|both]
7505            Optional; Required if ShowPager option is true.  Pager position. 
7506    ShowPermalinkButton                 default: true
7507            Optional.  True to show Permalink/View All button, false otherwise.
7508    ButtonPositionsPermaLinkModifer      default: 'bottom' [top|bottom|both]
7509            Optional; Required if ShowPermalinkButton option is true.  Permalink 
7510            button position. 
7511    PermaLinkButtonClass                default: 'spotlight_viewall'
7512            Optional; Required if ShowPermalinkButton is true.  Permalink button class.
7513    PermaLinkButtonIcon                 default: ''
7514            Optional; Required if ShowPermalinkButton is true.  Permalink button icon.
7515    PermaLinkButtonContent              default: 'View All'
7516            Optional; Required if ShowPermalinkButton is true.  Permalink button content.
7517    OnPreInitFunctions                  default: null
7518            Optional. Functions that may be called upon before initialization of the rotator, 
7519            but after the markup has been updated by the widget.
7520            The signature is: function(aCycleEvent, aCycleOptions)
7521    
7522    Methods:
7523    
7524    The widget is fully functional on its own, but can be remotely activated with the following 
7525    methods.
7526        
7527    Examples:
7528    
7529    Since the function is built as a jQuery Widget, simply call the functions on the 
7530    container element directly.
7531    
7532    $('#content_1337').SpotlightManager(
7533        {
7534            PermaLinkUrl: '/Core/Gallery/Spotlights/1701/'
7535        }
7536    );
7537*/
7538(function ($)
7539{
7540    $.widget("ui.SpotlightManager",
7541        {
7542            options:
7543            {
7544                PermaLinkUrl: null
7545                , GalleryTitle: null
7546                , ContentHeaderSelector: '.fbcms_contentheader'
7547                , ContentFooterSelector: '.fbcms_contentfooter'
7548                , RotatorContainerSelector: '.fbcms_spotlights_list'
7549                , ControlsClass: 'fbcms_spotlight_controls'
7550                , ButtonPositionsHeaderModifer: 'left'
7551                , ButtonPositionsFooterModifer: 'left'
7552                , ButtonClass: 'spotlight_button'
7553                , PrevButtonClass: 'spotlight_prev'
7554                , NextButtonClass: 'spotlight_next'
7555                , PlayPauseButtonClass: 'spotlight_playpause'
7556                , PlayPauseButtonPlayClass: 'spotlight_play'
7557                , PagerClass: 'fbcms_spotlight_pager'
7558                , PagesClass: 'spotlight_pages'
7559                , PermaLinkButtonClass: 'spotlight_viewall'
7560                , OnPreInitFunctions: null
7561            },
7562
7563            _destroy: function ()
7564            {
7565            },
7566
7567            _init: function ()
7568            {
7569            },
7570
7571            _create: function ()
7572            {
7573                var self = this;
7574                var jElement = $(self.element);
7575                var lElementId = $(jElement).attr('id');
7576                var lAddButtons = false;
7577                var lAddPager = false;
7578
7579                if (FBUtilities.IsStringEmptyOrWhitespace(self.options.PermaLinkUrl)
7580                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.RotatorContainerSelector))
7581                {
7582                    FBUtilities.LogMessage('SpotlightManager - required settings null or empty.');
7583                    return;
7584                }
7585
7586                var lRotatorContainer = jElement.find(self.options.RotatorContainerSelector);
7587
7588                if (lRotatorContainer.length !== 1)
7589                {
7590                    FBUtilities.LogMessage('SpotlightManager - incorrect number of rotators found.');
7591                    return;
7592                }
7593
7594                //initialize the pre-init callbacks list.
7595                if (FBUtilities.IsParameterUndefinedOrEmpty(self.options.OnPreInitFunctions))
7596                {
7597                    //it's null or empty, create it.
7598                    self.options.OnPreInitFunctions = [];
7599                }
7600                else if (
7601                    self.options.OnPreInitFunctions !== undefined
7602                    && self.options.OnPreInitFunctions.constructor !== undefined
7603                    && self.options.OnPreInitFunctions.call !== undefined
7604                    && self.options.OnPreInitFunctions.apply !== undefined
7605                ) 
7606                {
7607                    //it's a function, add it to the array.
7608                    var lFunction = self.options.OnPreInitFunctions;
7609                    self.options.OnPreInitFunctions = [];
7610                    self.options.OnPreInitFunctions.push(lFunction);
7611                }
7612                else if ($.isArray(self.options.OnPreInitFunctions))
7613                {
7614                    //it's an array, we can add to it.
7615                }
7616                else
7617                {
7618                    //dunno what it is. nuke it and create it anew.
7619                    self.options.OnPreInitFunctions = [];
7620                }
7621
7622                self._createControls(lAddButtons, lAddPager);
7623
7624                if (FBCommon_1_2.isInEditor() === false)
7625                {
7626                    self.options.OnPreInitFunctions.push(
7627                        function (aCycleEvent, aCycleOptions)
7628                        {
7629                            jElement.on(
7630                                "click",
7631                                '[data-fbpg-url]',
7632                                function (aEvent)
7633                                {
7634                                    aEvent.preventDefault();
7635                                    window.open(
7636                                        $(aEvent.target).data('fbpg-url'),
7637                                        $(aEvent.target).data('fbpg-urltarget')
7638                                    );
7639                                    return false;
7640                                }
7641                            );
7642                        }
7643                    );
7644                }
7645
7646                lRotatorContainer.on(
7647                    'cycle-pre-initialize',
7648                    function (aCycleEvent, aCycleOptions)
7649                    {
7650                        if (FBUtilities.IsParameterUndefinedOrEmpty(self.options.OnPreInitFunctions))
7651                        {
7652                            return;
7653                        }
7654                        for (var lIndex = 0, lLength = self.options.OnPreInitFunctions.length; lIndex < lLength; lIndex++)
7655                        {
7656                            self.options.OnPreInitFunctions[lIndex](aCycleEvent, aCycleOptions);
7657                        }//endfor
7658                    }
7659                );
7660
7661                lRotatorContainer.cycle();
7662                self._updatePlayPauseButton();
7663            },
7664
7665            _createControls: function (aAddButtons, aAddPager)
7666            {
7667                var self = this;
7668                var jElement = $(self.element);
7669                var lElementId = $(jElement).attr('id');
7670                var lRotatorContainer = jElement.find(self.options.RotatorContainerSelector);
7671
7672                if (FBCommon_1_2.isInEditor() === false)
7673                {
7674                    self.options.OnPreInitFunctions.push(
7675                        function (aCycleEvent, aCycleOptions)
7676                        {
7677                            jElement.on(
7678                                "click",
7679                                '.' + self.options.PrevButtonClass,
7680                                function (aEvent)
7681                                {
7682                                    aEvent.preventDefault();
7683                                    lRotatorContainer.cycle('prev');
7684                                    return false;
7685                                }
7686                            );
7687                            jElement.on(
7688                                "click",
7689                                '.' + self.options.NextButtonClass,
7690                                function (aEvent)
7691                                {
7692                                    aEvent.preventDefault();
7693                                    lRotatorContainer.cycle('next');
7694                                    return false;
7695                                }
7696                            );
7697                            jElement.on(
7698                                "click",
7699                                '.' + self.options.PlayPauseButtonClass,
7700                                function (aEvent)
7701                                {
7702                                    aEvent.preventDefault();
7703                                    if (aCycleOptions.paused === true)
7704                                    {
7705                                        lRotatorContainer.cycle('resume');
7706                                    }
7707                                    else
7708                                    {
7709                                        lRotatorContainer.cycle('pause');
7710                                    }
7711                                    self._updatePlayPauseButton();
7712                                    return false;
7713                                }
7714                            );
7715                        }
7716                    );
7717                }
7718            },
7719
7720            _updatePlayPauseButton: function ()
7721            {
7722                var self = this;
7723                var jElement = $(self.element);
7724                var lButton = jElement.find('.' + self.options.PlayPauseButtonClass);
7725                var lRotatorContainer = jElement.find(self.options.RotatorContainerSelector);
7726                var lCycleOptions = lRotatorContainer.data('cycle.opts');
7727                if (lCycleOptions.paused === true)
7728                {
7729                    lButton.removeClass(self.options.PlayPauseButtonPlayClass);
7730                }
7731                else
7732                {
7733                    lButton.addClass(self.options.PlayPauseButtonPlayClass);
7734                }
7735            }
7736        });
7737})(jQuery);
7738
7739
7740var waitForFinalEvent = (
7741    function ()
7742    {
7743        var timers = {};
7744        return function (callback, ms, uniqueId)
7745        {
7746            if (!uniqueId)
7747            {
7748                uniqueId = "Don't call this twice without a uniqueId";
7749            }
7750            if (timers[uniqueId])
7751            {
7752                clearTimeout(timers[uniqueId]);
7753            }
7754            timers[uniqueId] = setTimeout(callback, ms);
7755        };
7756    }
7757)();
7758
7759/*
7760    SpotlistNavManager - Foxbright Spotlist Navigation Manager
7761    
7762    Javascript controller for managing spotlights.
7763
7764    Options:
7765    
7766    SliderNavClass                      default: 'fb_audnav'
7767        Required. Class added to slider content area once the javascript is successfully 
7768        in place.
7769    SlideListSelector                   default: '.fbcms_spotlist_items'
7770        Required. Selector for the list of items.
7771    SlideSelector                       default: '.fbcms_spotlist_item'
7772        Required. Selector for the items.
7773    PositionUnits                       default: 'px'
7774        Required. Units to use for CSS positioning.
7775    ActiveSlideClass                    default: 'audnav_active'
7776        Required. Class to indicate currently selected item.
7777    SmallTabHeight                      default: 50
7778        Required. Tab height (width when turned sideways) for small display.
7779    SmallSlideWidth                     default: 300
7780        Required. Slide width for small display.
7781    SmallSizeCutoff                     default: 767
7782        Required. Size at which the small display ends (inclusive).
7783    MediumSizeClass                     default: 'audnavmd'
7784        Required. Class to indicate medium display size.
7785    MediumTabHeight                     default: 75
7786        Required. Tab height (width when turned sideways) for medium display.
7787    MediumSlideWidth                    default: 400
7788        Required. Slide width for medium display.
7789    MediumSizeCutoff                    default: 1023
7790        Required. Size at which the medium display ends (inclusive).
7791    LargeSizeClass                      default: 'audnavlg'
7792        Required. Class to indicate large display size.
7793    LargeTabHeight                      default: 100
7794        Required. Tab height (width when turned sideways) for large display.
7795    LargeSlideWidth                     default: 500
7796        Required. Slide width for large display.
7797    SlideTitleSelector                  default: '.desc_short'
7798        Required. Selector for item titles to be rotated on medium and large display.
7799    
7800    Methods:
7801    
7802    The widget is fully functional on its own, but can be remotely activated 
7802with the following 
7803    methods.
7804        
7805    Examples:
7806    
7807    Since the function is built as a jQuery Widget, simply call the functions on the 
7808    container element directly.
7809    
7810    $('#content_1337').SpotlistNavManager();
7811*/
7812(function ($)
7813{
7814    $.widget("ui.SpotlistNavManager",
7815        {
7816            options:
7817            {
7818                SliderNavClass: 'fb_audnav'
7819                , SlideListSelector: '.fbcms_spotlist_items'
7820                , SlideSelector: '.fbcms_spotlist_item'
7821                , PositionUnits: 'px'
7822                , ActiveSlideClass: 'audnav_active'
7823                , SmallTabHeight: 50
7824                , SmallSlideWidth: 300
7825                , SmallSizeCutoff: 767
7826                , MediumSizeClass: 'audnavmd'
7827                , MediumTabHeight: 75
7828                , MediumSlideWidth: 400
7829                , MediumSizeCutoff: 1023
7830                , LargeSizeClass: 'audnavlg'
7831                , LargeTabHeight: 100
7832                , LargeSlideWidth: 500
7833                , SlideTitleSelector: '.desc_short'
7834            },
7835
7836            _slideWidth: null,
7837            _slideHeight: null,
7838
7839            _slideTabWidth: null,
7840
7841
7842            _destroy: function ()
7843            {
7844            },
7845
7846            _init: function ()
7847            {
7848            },
7849
7850            _create: function ()
7851            {
7852                var self = this;
7853                var jElement = $(self.element);
7854                var lElementId = $(jElement).attr('id');
7855
7856                if (FBUtilities.IsStringEmptyOrWhitespace(self.options.SliderNavClass)
7857                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.SlideListSelector)
7858                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.SlideSelector)
7859                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.PositionUnits)
7860                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.ActiveSlideClass)
7861                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.MediumSizeClass)
7862                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.LargeSizeClass)
7863                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.SlideTitleSelector))
7864                {
7865                    FBUtilities.LogMessage('SpotlistNavManager - required settings null or empty.');
7866                    return;
7867                }
7868
7869                var lNumSpots = jElement.find(self.options.SlideSelector).length;
7870                if (lNumSpots < 3)
7871                {
7872                    FBUtilities.LogMessage('SpotlistNavManager - too few slides found.');
7873                    return;
7874                }
7875
7876                self._sizeSlideNav();
7877
7878                jElement.find(self.options.SlideListSelector).on(
7879                    "mouseover",
7880                    self.options.SlideSelector,
7881                    function (aEvent)
7882                    {
7883                        self._setSlideNavPositions($(aEvent.target).closest(self.options.SlideSelector).index());
7884                    }
7885                );
7886
7887                jElement.find(self.options.SlideListSelector).on(
7888                    "click",
7889                    self.options.SlideSelector,
7890                    function (aEvent)
7891                    {
7892                        var lSlide = $(aEvent.target).closest(self.options.SlideSelector);
7893                        if (lSlide.hasClass(self.options.ActiveSlideClass) === false)
7894                        {
7895                            aEvent.preventDefault();
7896                            self._setSlideNavPositions(lSlide.index());
7897                            return false;
7898                        }
7899                        else
7900                        {
7901                            return true;
7902                        }
7903                    }
7904                );
7905
7906                self._setSlideNavPositions(0);
7907                jElement.addClass(self.options.SliderNavClass);
7908
7909                $(window).resize(
7910                    function ()
7911                    {
7912                        waitForFinalEvent(
7913                            function ()
7914                            {
7915                                self._sizeSlideNav();
7916                            },
7917                            500,
7918                            "resizer"
7919                        );
7920                    }
7921                );
7922            },
7923
7924            _sizeSlideNav: function ()
7925            {
7926                var self = this;
7927                var jElement = $(self.element);
7928                var lElementId = $(jElement).attr('id');
7929
7930                var lNumSpots = jElement.find(self.options.SlideSelector).length;
7931                if (lNumSpots < 3)
7932                {
7933                    return;
7934                }
7935
7936                jElement.removeClass(self.options.MediumSizeClass);
7937                jElement.removeClass(self.options.LargeSizeClass);
7938                self._slideHeight = self.options.SmallSlideWidth;
7939                self._slideTabWidth = self.options.SmallTabHeight;
7940
7941                var lListWidth = jElement.find(self.options.SlideListSelector).width();
7942
7943                if ($(window).width() <= self.options.SmallSizeCutoff)
7944                {
7945                    jElement.find(self.options.SlideSelector).removeAttr("style");
7946                    jElement.find(self.options.SlideSelector).find('.desc_short').removeAttr("style");
7947                    jElement.find(self.options.SlideListSelector).removeAttr("style");
7948                    return;
7949                }
7950                else if ($(window).width() <= self.options.MediumSizeCutoff)
7951                {
7952                    self._slideHeight = self.options.MediumSlideWidth;
7953                    // self._slideTabWidth = self.options.MediumTabHeight;
7954                    self._slideTabWidth = lListWidth / (lNumSpots + 1);
7955                    jElement.addClass(self.options.MediumSizeClass);
7956                }
7957                else
7958                {
7959                    self._slideHeight = self.options.LargeSlideWidth;
7960                    // self._slideTabWidth = self.options.LargeTabHeight;
7961                    self._slideTabWidth = lListWidth / (lNumSpots + 1);
7962                    jElement.addClass(self.options.LargeSizeClass);
7963                }
7964
7965                self._slideWidth = jElement.find(self.options.SlideSelector).width();
7966
7967                $.each(
7968                    jElement.find(self.options.SlideSelector),
7969                    function (aIndex, aValue)
7970                    {
7971                        var lValue = $(aValue);
7972                        lValue.css('background-image', 'url("' + $(aValue).data('spotimgsrc') + '")');
7973                    }
7974                );
7975
7976                self._slideWidth = self._slideTabWidth * 2;
7977                // self._slideWidth = lListWidth - (lNumSpots - 1) * self._slideTabWidth;
7978                jElement.find(self.options.SlideSelector).css('width', self._slideWidth + self.options.PositionUnits);
7979                jElement.find(self.options.SlideSelector).find(self.options.SlideTitleSelector).css('width', self._slideHeight + self.options.PositionUnits);
7980                jElement.find(self.options.SlideSelector).find(self.options.SlideTitleSelector).css('transform-origin', 
7980(self._slideHeight / 2) + self.options.PositionUnits + ' ' + (self._slideHeight / 2) + self.options.PositionUnits);
7981                jElement.find(self.options.SlideListSelector).css('height', self._slideHeight + self.options.PositionUnits);
7982
7983                self._setSlideNavPositions(jElement.find(self.options.SlideSelector + '.' + self.options.ActiveSlideClass).index());
7984            },
7985
7986            _setSlideNavPositions: function (aActiveIndex)
7987            {
7988                var self = this;
7989                var jElement = $(self.element);
7990                var lElementId = $(jElement).attr('id');
7991
7992                $.each(
7993                    jElement.find(self.options.SlideSelector),
7994                    function (aIndex, aValue)
7995                    {
7996                        var lValue = $(aValue);
7997                        var lLeftPositionVal = (aIndex * self._slideTabWidth);
7998                        if (aActiveIndex === aIndex)
7999                        {
8000                            lValue.addClass(self.options.ActiveSlideClass);
8001                            lValue.find(self.options.SlideTitleSelector).css('width', 'auto');
8002                        }
8003                        else
8004                        {
8005                            lValue.removeClass(self.options.ActiveSlideClass);
8006                            lValue.find(self.options.SlideTitleSelector).css('width', self._slideHeight + self.options.PositionUnits);
8007                        }
8008                        if (aIndex > aActiveIndex)
8009                        {
8010                            lLeftPositionVal += (self._slideWidth - self._slideTabWidth);
8011                        }
8012                        lValue.css('left', (lLeftPositionVal + self.options.PositionUnits));
8013                    }
8014                );
8015            }
8016        });
8017})(jQuery);
8018
8019
8020///////////////////////////////////////////////////////////////////////////////
8021// IFrame Video Resizer
8022///////////////////////////////////////////////////////////////////////////////
8023
8024(function ($)
8025{
8026    VideoUtilities.SetupVideoResizer = function (aSelector)
8027    {
8028        if (FBUtilities.IsStringEmptyOrWhitespace(aSelector))
8029        {
8030            return;
8031        }
8032
8033        var lAllContainerElements = $(aSelector);
8034        if (lAllContainerElements.length === 0)
8035        {
8036            return;
8037        }
8038
8039        // Find all Videos (YouTube/Vimeo)
8040        var lAllVideos = lAllContainerElements.find("iframe[src^='//www.youtube.com'], iframe[src^='//player.vimeo.com']");
8041
8042        if (lAllVideos.length === 0)
8043        {
8044            return;
8045        }
8046
8047        // Figure out and save aspect ratio for each video
8048        lAllVideos.each(
8049            function ()
8050            {
8051                $(this)
8052                    .data('aspectRatio', this.height / this.width)
8053                    // and remove the hard coded width/height
8054                    .removeAttr('height')
8055                    .removeAttr('width');
8056            }
8057        );
8058
8059        // When the window is resized
8060        $(window).resize(
8061            function ()
8062            {
8063                // Resize all videos according to their own aspect ratio
8064                lAllVideos.each(
8065                    function ()
8066                    {
8067                        var $el = $(this);
8068                        var lNewWidth = $el.parent().width();
8069                        $el
8070                            .width(lNewWidth)
8071                            .height(lNewWidth * $el.data('aspectRatio'));
8072
8073                    }
8074                );
8075                // Kick off one resize to fix all videos on page load
8076            }
8077        ).resize();
8078    }; //end SetupVideoResizer
8079
8080}(jQuery));
8081
8082///////////////////////////////////////////////////////////////////////////////
8083// Email Icon Hover/Focus Event
8084///////////////////////////////////////////////////////////////////////////////
8085
8086(function ($)
8087{
8088
8089    $(
8090        function () 
8091        {
8092            var _EmailLinkClass = 'contact_button';
8093            var _EmailIconClass = 'fb_emailicon';
8094            //at rest: far fa-envelope
8095            //active:  fas fa-envelope
8096            var _EmailAtRestClass = 'far';
8097            var _EmailActiveClass = 'fas';
8098
8099            function _ResetEmailIcons()
8100            {
8101                $('.' + _EmailIconClass).removeClass(_EmailActiveClass);
8102                $('.' + _EmailIconClass).addClass(_EmailAtRestClass);
8103            }
8104
8105            function _ActivateEmailIcon(aElement)
8106            {
8107                if (FBUtilities.IsParameterUndefinedOrEmpty(aElement))
8108                {
8109                    return;
8110                }
8111                aElement.removeClass(_EmailAtRestClass);
8112                aElement.addClass(_EmailActiveClass);
8113            }
8114
8115            $('body').on(
8116                'focusin mouseover',
8117                '.' + _EmailLinkClass,
8118                function (aEvent)
8119                {
8120                    _ResetEmailIcons();
8121                    _ActivateEmailIcon($(aEvent.target).closest('.' + _EmailLinkClass).find('.' + _EmailIconClass));
8122                }
8123            );
8124
8125            $('body').on(
8126                'focusout mouseout',
8127                '.' + _EmailLinkClass,
8128                function (aEvent)
8129                {
8130                    if (!$(aEvent.target).hasClass(_EmailLinkClass))
8131                    {
8132                        //return false;
8133                    }
8134                    _ResetEmailIcons();
8135                }
8136            );
8137        }
8138    );
8139
8140}(jQuery));
8141
8142///////////////////////////////////////////////////////////////////////////////
8143// Foxbright Lightbox Manager
8144///////////////////////////////////////////////////////////////////////////////
8145/*
8146(function ($)
8147{
8148    FBLightbox._Initialized = false;
8149
8150    FBLightbox.Init = function ()
8151    {
8152        if (FBUtilities.IsParameterUndefinedOrEmpty(window.lightbox))
8153        {
8154            return;
8155        }
8156        if (FBLightbox._Initialized === false)
8157        {
8158            window.lightbox.init();
8159            FBLightbox._Initialized = true;
8160        }
8161    }; //end Init
8162
8163}(jQuery));
8164*/
8165
8166(function ($)
8167{
8168    var quickAjaxTemplate = "<div id=\"QuickAjaxDialog\" class=\"modal fade\" tabindex=\"-1\" role=\"dialog\" aria-labelledby=\"staticBackdrop\" aria-hidden=\"true\"> \
8169        <div class=\"modal-dialog modal-dialog-centered\" role=\"document\"> \
8170            <div class=\"modal-content\"> \
8171                <div class=\"modal-header\" \> \
8172            <h5 class=\"modal-title\" id=\"QuickAjaxDialogTitle\">Checking the building name.</h5> \
8173                </div> \
8174                <div class=\"modal-body\"> \
8175                    <div class=\"progress\" > \
8176                        <div class=\"progress-bar progress-bar-striped progress-bar-animated bg-danger\" role=\"progressbar\" style=\"width: 100%\" aria-valuenow=\"100\" aria-valuemin=\"0\" aria-valuemax=\"100\"></div> \
8177                    </div> \
8178                </div> \
8179            </div> \
8180        </div> \
8181    </div>";
8182
8183    /**
8184     * FBQuickAjax.Async
8185     * Creates a promise object that will post a simple ajax request to a remote ajax service. 
8186     * @param {
8187     *      url: [the posting url of the ajax service],
8188     *      timeout: [milliseconds to wait before giving up on the ajax request],
8189     *      title: [a short title for progress dialog],
8190     *      data: [some data to post to the posting url]
8191     * } options
8192     * @returns Promise object.
8193     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
8194     */
8195    FBQuickAjax.Async = function (options)
8196    {
8197        return new Promise(function (resolutionFunc, rejectionFunc)
8198        {
8199            $("#QuickAjaxDialog").remove();
8200
8201            var $dialog = $(quickAjaxTemplate);
8202
8203            $dialog.find(".modal-title").html(options.title);
8204            $("body").prepend($dialog);
8205
8206            $("#QuickAjaxDialog").off('shown.bs.modal').on('shown.bs.modal', function ()
8207            {
8208                $.ajax({
8209                    type: options.method || "post",
8210                    url: options.url,
8211                    timeout: options.timeout,
8212                    dataType: "json",
8213                    async: true,
8214                    data: options.data,
8215                    error: function (_jqXHR, textStatus, errorThrown) 
8216                    {
8217                        console.debug(`${textStatus}: ${errorThrown}`);
8218                        rejectionFunc(textStatus);
8219                    },
8220                    success: function (data, _textStatus, _jqXHR) 
8221                    {
8222                        resolutionFunc(data);
8223                    },
8224                    complete: function ()
8225                    {
8226                        $("#QuickAjaxDialog").modal("hide");
8227                    }
8228                });
8229            });
8230            $("#QuickAjaxDialog").modal('show');
8231        });
8232    }
8233
8234}(jQuery));
8235
8236
8237// Manages any in-page changes of the url in the address bar.
8238var FBUrlManager =
8239{
8240    CURRENTSTATEIDENTIFIER: 'CurrentUrlManagerState',
8241    _CallbackSets: {},
8242    _SpecifiedDefaults: {},
8243
8244    // initialize. Sets up a listener to run the callbacks upon a 'back' press in the browser.
8245    init: function ()
8246    {
8247        window.addEventListener(
8248            'popstate',
8249            (event) => 
8250                {
8251                    FBUrlManager._runCallbacks('OnAddressBarUpdate', document.location);
8252                }
8253        );
8254    },
8255    // sets a default for a parameter. Setting a value matching this default for the parameter will remove the parameter from the url.
8256    setParameterDefault: function (aParamName, aDefault)
8257    {
8258        if (FBUtilities.IsStringEmptyOrWhitespace(aParamName))
8259        {
8260            return;
8261        }
8262        if (FBUtilities.IsParameterUndefinedOrEmpty(FBUrlManager._SpecifiedDefaults))
8263        {
8264            return;
8265        }
8266
8267        let lParamName = FBUrlManager._getNormalizedParamName(aParamName);
8268        
8269        // create or overwrite.
8270        FBUrlManager._SpecifiedDefaults[lParamName] = aDefault;
8271    },
8272    // removes a parameter default.
8273    removeParameterDefault: function (aParamName)
8274    {
8275        if (FBUtilities.IsStringEmptyOrWhitespace(aParamName))
8276        {
8277            return;
8278        }
8279        if (FBUtilities.IsParameterUndefinedOrEmpty(FBUrlManager._SpecifiedDefaults))
8280        {
8281            return;
8282        }
8283
8284        let lParamName = FBUrlManager._getNormalizedParamName(aParamName);
8285        
8286        if (FBUtilities.IsParameterUndefinedOrNull(FBUrlManager._SpecifiedDefaults[lParamName]))
8287        {
8288            return;
8289        }
8290        delete FBUrlManager._SpecifiedDefaults[lParamName];
8291    },
8292    // update the address bar with the given parameter name and value.
8293    updateAddressBar: function (aParamName, aValue)
8294    {
8295        if (FBUtilities.IsStringEmptyOrWhitespace(aParamName))
8296        {
8297            return;
8298        }
8299        let lNewAddress = FBUrlManager._getUpdatedUrl(aParamName, aValue);
8300        
8301        window.history.replaceState(FBUrlManager.CURRENTSTATEIDENTIFIER, "", lNewAddress);
8302
8303        FBUrlManager._runCallbacks('OnAddressBarUpdate', lNewAddress);
8304    },
8305    // add a set of callbacks by an arbitrary name (for use if you wish to remove them at any point)
8306    //  available callbacks:
8307    //   'OnAddressBarUpdate' this will be run after an update and also if 'back' is used to navigate.
8308    //     it will have one parameter: the new url as set in the address bar.
8309    addCallbackSet: function (aName, aCallbackSet)
8310    {
8311        if (FBUtilities.IsStringEmptyOrWhitespace(aName))
8312        {
8313            return;
8314        }
8315        if (FBUtilities.IsParameterUndefinedOrEmpty(FBUrlManager._CallbackSets))
8316        {
8317            return;
8318        }
8319
8320        let lName = FBUrlManager._getNormalizedParamName(aName);
8321        
8322        // create or overwrite.
8323        FBUrlManager._CallbackSets[lName] = aCallbackSet;
8324    },
8325    // remove a set of callbacks by name.
8326    removeCallbackSet: function (aName)
8327    {
8328        if (FBUtilities.IsStringEmptyOrWhitespace(aName))
8329        {
8330            return;
8331        }
8332        if (FBUtilities.IsParameterUndefinedOrEmpty(FBUrlManager._CallbackSets))
8333        {
8334            return;
8335        }
8336
8337        let lName = FBUrlManager._getNormalizedParamName(aName);
8338        
8339        if (FBUtilities.IsParameterUndefinedOrNull(FBUrlManager._CallbackSets[lName]))
8340        {
8341            return;
8342        }
8343        delete FBUrlManager._CallbackSets[lName];
8344    },
8345    // worker method: runs the callback specified in each set of callbacks.
8346    _runCallbacks: function (aCallbackName, aNewAddress)
8347    {
8348        for(var lKey in FBUrlManager._CallbackSets)
8349        {
8350            FBUtilities.RunCallback(FBUrlManager._CallbackSets[lKey], aCallbackName, aNewAddress);
8351        }//endfor
8352    },
8353    // worker method: performs an desired normalization on the parameter name.
8354    _getNormalizedParamName: function(aParamName)
8355    {
8356        return aParamName;
8357    },
8358    // worker method: given a parameter name and value, returns the current address bar url updated with the new parameters.
8359    _getUpdatedUrl: function(aParamName, aValue)
8360    {
8361        let lProtocol = window.location.protocol;
8362        let lHost = window.location.host;
8363        let lPathName = window.location.pathname;
8364        let lSearch = FBUrlManager._getUpdatedSearch(window.location.search, aParamName, aValue);
8365        let lHash = window.location.hash;
8366        let lNewUrl = new URL(`${lProtocol}${lHost}${lPathName}${lSearch}${lHash}`);
8367        return lNewUrl.toString();
8368    },
8369    // worker method: given an address query component and parameter name and value, builds a new search query (with leading '?') for use.
8370    _getUpdatedSearch: function(aExistingQuery, aParamName, aValue)
8371    {
8372        let searchParams = new URLSearchParams(aExistingQuery);
8373
8374        if (searchParams.has(aParamName))
8375        {
8376            if (FBUtilities.IsParameterUndefinedOrEmpty(FBUrlManager._SpecifiedDefaults[aParamName])
8377                || FBUrlManager._SpecifiedDefaults[aParamName] !== aValue)
8378            {
8379                searchParams.set(aParamName, aValue);
8380            }
8381            else if (FBUrlManager._SpecifiedDefaults[aParamName] === aValue)
8382            {
8383                searchParams.delete(aParamName);
8384            }
8385        }
8386        else
8387        {
8388            if (FBUtilities.IsParameterUndefinedOrEmpty(FBUrlManager._SpecifiedDefaults[aParamName])
8389                || FBUrlManager._SpecifiedDefaults[aParamName] !== aValue)
8390            {
8391                searchParams.append(aParamName, aValue);
8392            }
8393        }
8394        let newSearch = searchParams.toString();
8395        if (FBUtilities.IsParameterUndefinedOrEmpty(newSearch) === false)
8396        {
8397            newSearch = '?' + newSearch;
8398        }
8399        return newSearch;
8400    }
8401
8402}//end FBUrlManager
8403// initialize it! It's OK if it's up before the page, needs to be ready for any default or callback submissions.
8404FBUrlManager.init();
8405
8406
8407///////////////////////////////////////////////////////////////////////////////
8408// Foxbright Translate Menu
8409(function ($) 
8410{
8411    $(
8412        function() 
8413        {
8414            // if a translate menu exists...
8415            if ($('.menu_translate').length > 0)
8416            {
8417                // add a named callback set...
8418                // 'OnAddressBarUpdate' - update any links in the menu that have a valid url with the google translate parameters with the new url.
8419                FBUrlManager.addCallbackSet(
8420                    'FoxbrightTranslateMenu',
8421                    {
8422                        'OnAddressBarUpdate': function (aNewUrl)
8423                        {
8424                            $.each($('.menu_translate a'), function (aIndex, aValue)
8425                            {
8426                                let lAnchor = $(aValue);
8427                                let lAnchorHref = lAnchor.attr('href');
8428                                let lAnchorUrl = null;
8429                                try
8430                                {
8431                                    lAnchorUrl = new URL(lAnchorHref);
8432                                }
8433                                catch (_)
8434                                {
8435                                    return;
8436                                }
8437                                if (lAnchorUrl.searchParams.has('u') === false
8438                                    || lAnchorUrl.searchParams.has('tl') === false)
8439                                {
8440                                    return;
8441                                }
8442                                lAnchorUrl.searchParams.set('u', aNewUrl);
8443                                lAnchor.attr('href', lAnchorUrl.toString());
8444                            });
8445                        }
8446                    }
8447                );
8448            }
8449        }
8450    );
8451}(jQuery));  // END JQUERY WRAPPER (Foxbright Translate Menu)
8452///////////////////////////////////////////////////////////////////////////////
8453
8454///////////////////////////////////////////////////////////////////////////////
8455// Foxbright Drop Widget
8456(function ($) 
8457{
8458    
8459    /*
8460        FBDropWidget - (Generic Drop derived from FBRListNav)
8461        - Foxbright Drop
8462        
8463        Javascript controller for the responsive simple drop that pops up upon command.  
8464        Derived from FBRListNav to avoid reinventing the drop.
8465        
8466        Options:
8467        
8468        DropClass                           default: 'fbcms_drop'
8469                Required.  A class to be added to the drop once the widget has successfully 
8470                completed. Change this at your peril.
8471        DropOpenClass                       default: 'dropopen'
8472                Required.  A class to indicate when the drop is open, to be added or removed 
8473                by the widget as appropriate.
8474        DropTitleClass                      default: null
8475                Required.  A class selector for finding the title element of the drop in the 
8476                drop element.  If the drop title is empty, the widget will do nothing.
8477        DropTitleAuxClass                      default: 'drop_title'
8478                Required.  A class to be added to the title once the widget has successfully
8479                complete. Change this at your peril.
8480        AddOpenButton                       default: false
8481                If true, add a button to the drop title with events to open and close the 
8482                drop. If true, OpenButtonClass, OpenDropContent, and CloseDropContent are 
8483                all required.
8484        OpenButtonClass                     default: 'drop_opener'
8485                Required if AddOpenButton is true.  A class used to identify the button added 
8486                to the drop title.
8487        OpenDropContent                     default: '<span class="fas fa-chevron-down"></span>'
8488                Required if AddOpenButton is true.  Content to appear in the drop button when 
8489                the drop is closed and may be opened.
8490                Whatever content is put into place, the following content is added afterward.
8491                <span class="screen-reader-text">Open {Drop Title} Drop</span>
8492        CloseDropContent                    default: '<span class="fas fa-chevron-up"></span>'
8493                Required if AddOpenButton is true.  Content to appear in the drop button when 
8494                the drop is open and may be closed.
8495                Whatever content is put into place, the following content is added afterward.
8496                <span class="screen-reader-text">Close {Drop Title} Drop</span>
8497        DropContainerClass                  default: null
8498                Required.  A class that should be found in the markup on the element wrapping 
8499                the drop.
8500        DropContainerClass                  default: 'drop_container'
8501                Required.  A class to be added to the container once the widget has successfully
8502                complete. Change this at your peril.
8503        InitOpen                            default: false
8504                If true, the drop will initialize open.
8505        DropClickActive                     default: true
8506                If true, the drop button will be activated on mouse click events.
8507        DropHoverActive                     default: false
8508                If true, the drop button will be activated on mouse hover events, closing on 
8509                hover out after a delay (DropHoverDelay) if not hovered over in that time.
8510        DropHoverDelay                      default: 300
8511                A hover delay for use if the DropHoverActive is true.
8512        
8513        Methods:
8514        
8515        The widget is fully functional on its own, but can be remotely activated with the following 
8516        methods.
8517        
8518        ToggleDrop
8519                Called to toggle the drop open or closed.
8520        OpenDrop
8521                Called to open the drop (or do nothing if the drop is already open).
8522        CloseDrop
8523                Called to close the drop (or do nothing if the drop is already closed).
8524        
8525        Examples:
8526        
8527        Since the function is built as a jQuery Widget, simply call the functions on the 
8528        container element directly.
8529        
8530        $('.fbcms_translate').FBDropWidget(
8531            {
8532                DropTitleClass: 'translatedrop_title'
8533                , DropContainerClass: 'translatedrop_container'
8534            }
8535        );
8536    */
8537    $.widget("ui.FBDropWidget",
8538        {
8539            options:
8540            {
8541                DropClass: 'fbcms_drop' 
8542                , DropOpenClass: 'dropopen'
8543                , DropTitleClass: null 
8544                , DropTitleAuxClass: 'drop_title' 
8545                , AddOpenButton: false
8546                , OpenButtonClass: 'drop_opener'
8547                , DropButtonOpenContent: '<span class="drop_icon fas fa-chevron-down"></span>'
8548                , DropButtonCloseContent: '<span class="drop_icon fas fa-chevron-up"></span>'
8549                , DropContainerClass: null 
8550                , DropContainerAuxClass: 'drop_container' 
8551                , InitOpen: false
8552                , DropClickActive: true
8553                , DropHoverActive: false
8554                , DropHoverDelay: 300
8555            },
8556
8557            _hoverTimer: null,
8558
8559            _destroy: function ()
8560            {
8561                return undefined;
8562            },
8563
8564            _init: function ()
8565            {
8566                return undefined;
8567            },
8568
8569            _create: function ()
8570            {
8571                var self = this;
8572                var jElement = $(self.element);
8573
8574                if (FBUtilities.IsStringEmptyOrWhitespace(self.options.DropClass)
8575                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.DropOpenClass)
8576                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.DropTitleClass)
8577                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.DropTitleAuxClass)
8578                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.DropContainerClass)
8579                    || FBUtilities.IsStringEmptyOrWhitespace(self.options.DropContainerAuxClass))
8580                {
8581                    FBUtilities.LogMessage('FBDropWidget - required settings null or empty.');
8582                    return;
8583                }
8584
8585                if (self.options.AddOpenButton === true
8586                    && (FBUtilities.IsStringEmptyOrWhitespace(self.options.OpenButtonClass)
8587                        || FBUtilities.IsStringEmptyOrWhitespace(self.options.DropButtonOpenContent)
8588                        || FBUtilities.IsStringEmptyOrWhitespace(self.options.DropButtonCloseContent)))
8589                {
8590                    FBUtilities.LogMessage('FBDropWidget - open button settings not set as requested.');
8591                    return;
8592                }
8593
8594                let lTitleElement = jElement.find(`.${self.options.DropTitleClass}`);
8595                let lContainerElement = jElement.find(`.${self.options.DropContainerClass}`);
8596
8597                if (lTitleElement.length === 0)
8598                {
8599                    FBUtilities.LogMessage('FBDropWidget - drop title not found.');
8600                    return;
8601                }
8602                if (lContainerElement.length === 0)
8603                {
8604                    FBUtilities.LogMessage('FBDropWidget - drop container not found.');
8605                    return;
8606                }
8607                lTitleElement.attr("aria-expanded", false);
8608
8609                self._DropTitle = lTitleElement.text();
8610                
8611                if (self.options.AddOpenButton === true)
8612                {
8613                    lTitleElement.append('<span class="' + self.options.OpenButtonClass + '"></span>');
8614                    self._setOpenButtonContent();
8615                }
8616
8617                let lButtonEventSelector = '.' + self.options.DropTitleClass;
8618                if (self.options.AddOpenButton === true)
8619                {
8620                    lButtonEventSelector = lButtonEventSelector + ',.' + self.options.OpenButtonClass;
8621                }
8622
8623                if (self.options.DropClickActive === true)
8624                {
8625                    jElement.on(
8626                        'click',
8627                        lButtonEventSelector,
8628                        function (aEvent) 
8629                        {
8630                            aEvent.preventDefault();
8631                            self.ToggleDrop();
8632                            return false;
8633                        }
8634                    );
8635                    jElement.on(
8636                        'keyup',
8637                        lButtonEventSelector,
8638                        function (aEvent) 
8639                        {
8640							if(aEvent.which == 13)
8641							{
8642								aEvent.preventDefault();
8643								self.ToggleDrop();
8644								return false;
8645							}
8646							return true;
8647                        }
8648                    );
8649				}
8650				
8651                if (self.options.DropHoverActive === true)
8652                {
8653                    jElement.hover(
8654                        function ()
8655                        {
8656                            self.OpenDrop();
8657
8658                            if (self._hoverTimer !== null)
8659                            {
8660                                $.clearTimer(self._hoverTimer);
8661                            }
8662                        }
8663                        , function ()
8664                        {
8665                            self._hoverTimer = $.timer(
8666                                self.options.DropHoverDelay,
8667                                function ()
8668                                {
8669                                    self.CloseDrop();
8670                                }
8671                            );
8672                        }
8673                    );
8674                }
8675
8676                if (self.options.InitOpen)
8677                {
8678                    self.OpenDrop();
8679                }
8680
8681                jElement.DropFocusManager(
8682                    {
8683                        HoverFocusClass: self.options.DropOpenClass
8684                        , OpenOnHoverFocus: false
8685                        , TitleOpenerSelector: '.' + self.options.DropTitleClass
8686                        , DropLinksSelector: '.' + self.options.DropContainerClass + ' :focusable'
8687                    }
8688                );
8689
8690                jElement.addClass(self.options.DropClass);
8691                lTitleElement.addClass(self.options.DropTitleAuxClass);
8692                lContainerElement.addClass(self.options.DropContainerAuxClass);
8693            },
8694
8695            _DropTitle: null,
8696
8697            ToggleDrop: function ()
8698            {
8699                var self = this,
8700                    jElement = $(self.element);
8701
8702                jElement.toggleClass(self.options.DropOpenClass);
8703                jElement.find(`.${self.options.DropTitleClass}`).attr("aria-expanded", jElement.hasClass(self.options.DropOpenClass));
8704
8705                self._setOpenButtonContent();
8706                self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: jElement, name: 'FBDropWidget:ToggleDrop' } }));
8707            },
8708
8709            OpenDrop: function ()
8710            {
8711                var self = this,
8712                    jElement = $(self.element);
8713
8714                jElement.addClass(self.options.DropOpenClass);
8715                jElement.find(`.${self.options.DropTitleClass}`).attr("aria-expanded", true);
8716                
8717                self._setOpenButtonContent();
8718                self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: jElement, name: 'FBDropWidget:OpenDrop' } }));
8719            },
8720
8721            CloseDrop: function ()
8722            {
8723                var self = this,
8724                    jElement = $(self.element);
8725
8726                jElement.removeClass(self.options.DropOpenClass);
8727                jElement.find(`.${self.options.DropTitleClass}`).attr("aria-expanded", false);
8728                
8729                self._setOpenButtonContent();
8730                self.element[0].dispatchEvent(new CustomEvent('fb:changevisibility', { bubbles: true, detail: { item: jElement, name: 'FBDropWidget:CloseDrop' } }));
8731            },
8732
8733            _setOpenButtonContent: function ()
8734            {
8735                var self = this,
8736                    jElement = $(self.element),
8737                    lOpenButton;
8738
8739                if (self.options.AddOpenButton === true)
8740                {
8741                    lOpenButton = jElement.find('.' + self.options.OpenButtonClass);
8742
8743                    if (jElement.is('.' + self.options.DropOpenClass))
8744                    {
8745                        lOpenButton.html(self.options.DropButtonCloseContent + '<span class="screen-reader-text">Close ' + self._DropTitle + ' Drop</span>');
8746                    }
8747                    else
8748                    {
8749                        lOpenButton.html(self.options.DropButtonOpenContent + '<span class="screen-reader-text">Open ' + self._DropTitle + ' Drop</span>');
8750                    }
8751                }
8752            }
8753
8754        }
8755    );
8756    
8757    
8758    FBUtilities.initTranslateWidget = function (aElement, aOnInitializedCallback)
8759    {
8760        if ($(aElement).FBDropWidget("instance") !== undefined)
8761        {
8762            $(aElement).FBDropWidget("destroy");
8763        }
8764
8765        $(aElement).FBDropWidget(
8766            {
8767                DropTitleClass: 'translatedrop_title'
8768                , DropContainerClass: 'translatedrop_container'
8769            }
8770        );
8771
8772        aOnInitializedCallback(aElement);
8773    };
8774}(jQuery));  // END JQUERY WRAPPER (Foxbright Drop Widget)
8775///////////////////////////////////////////////////////////////////////////////
8776
8777
8778(function ($)
8779{
8780    $(window).on('load', function ()
8781    {
8782        FBUtilities.initializeLoadables(document);
8783    });
8784
8785    FBUtilities.initializeLoadables = function (containerElement)
8786    {
8787        if (containerElement === null)
8788        {
8789            return;
8790        }
8791        containerElement.querySelectorAll("[data-fbonload]").forEach(
8792            function (element)
8793            {
8794                FBUtilities.initializeLoadable(element);
8795            }
8796        );
8797    }
8798
8799    FBUtilities.initializeLoadable = function (aLoadableElement)
8800    {
8801        if (!aLoadableElement.dataset.hasOwnProperty('fbonload'))
8802        {
8803            return;
8804        }
8805
8806        if (aLoadableElement.dataset.hasOwnProperty('fbInitState')
8807            && aLoadableElement.dataset.fbInitState !== "new")
8808        {
8809            return;
8810        }
8811                
8812        var func = aLoadableElement.dataset.fbonload;
8813        if (typeof FBUtilities[func] !== 'undefined'
8814            && typeof FBUtilities[func] === 'function')
8815        {
8816            FBUtilities[func](aLoadableElement, FBUtilities.onLoadableInitialized);
8817        }
8818        else
8819        {
8820            console.log(`Function '${func}' not found!`);
8821        }
8822    }
8823
8824    FBUtilities.onLoadableInitialized = function (aElement)
8825    {
8826        // must set to initialized once complete.
8827        aElement.dataset.fbInitState = "complete";
8828    }
8829}(jQuery));  // END JQUERY WRAPPER (Initialize Loadables)
8830
8831
8832///////////////////////////////////////////////////////////////////////////////
8833// Metronic Integrated "Fancy" Labels
8834(function ($) 
8835{
8836    function updateFancyInputField(aField, aHasFocus)
8837    {
8838        let parent = $(aField).parent();
8839        let hasWrapper = parent.hasClass('selectfolder') && parent.hasClass('input-group');
8840        let wrapper = hasWrapper ? $(aField).parent() : aField;
8841        if (aHasFocus === true || FBUtilities.IsParameterUndefinedOrEmpty(aField.val()) === false)
8842        {
8843            wrapper.addClass('notempty');
8844        }
8845        else
8846        {
8847            wrapper.removeClass('notempty');
8848        }
8849    }//end updateFancyInputField
8850
8851    FBUtilities.initFancyInputField = function (aElement, aOnInitializedCallback)
8852    {
8853        let element = $(aElement);
8854        let normalInput = element.find('.form-control,.vp-input');
8855        normalInput.on('focus blur change',
8856            function(e)
8857            {
8858                updateFancyInputField($(e.target), e.type === 'focus');
8859            }
8860        );
8861
8862        updateFancyInputField(normalInput, false);
8863
8864        aOnInitializedCallback(aElement);
8865    };
8866}(jQuery));  // END JQUERY WRAPPER (Metronic Integrated "Fancy" Labels)
8867///////////////////////////////////////////////////////////////////////////////
8868
8869
8870//
8871// BEGIN LATINIZE
8872// Thanks to https://stackoverflow.com/questions/286921/efficiently-replace-all-accented-characters-in-a-string
8873//
8874
8875var Latinise = {}; Latinise.latin_map = {
8876    "Á": "A",
8877    "Ă": "A",
8878    "Ắ": "A",
8879    "Ặ": "A",
8880    "Ằ": "A",
8881    "Ẳ": "A",
8882    "Ẵ": "A",
8883    "Ǎ": "A",
8884    "Â": "A",
8885    "Ấ": "A",
8886    "Ậ": "A",
8887    "Ầ": "A",
8888    "Ẩ": "A",
8889    "Ẫ": "A",
8890    "Ä": "A",
8891    "Ǟ": "A",
8892    "Ȧ": "A",
8893    "Ç ": "A",
8894    "Ạ": "A",
8895    "Ȁ": "A",
8896    "À": "A",
8897    "Ả": "A",
8898    "Ȃ": "A",
8899    "Ā": "A",
8900    "Ą": "A",
8901    "Å": "A",
8902    "Ǻ": "A",
8903    "Ḁ": "A",
8904    "Ⱥ": "A",
8905    "Ã": "A",
8906    "Ꜳ": "AA",
8907    "Æ": "AE",
8908    "Ǽ": "AE",
8909    "Ç¢": "AE",
8910    "Ꜵ": "AO",
8911    "Ꜷ": "AU",
8912    "Ꜹ": "AV",
8913    "Ꜻ": "AV",
8914    "Ꜽ": "AY",
8915    "Ḃ": "B",
8916    "Ḅ": "B",
8917    "Ɓ": "B",
8918    "Ḇ": "B",
8919    "Ƀ": "B",
8920    "Ƃ": "B",
8921    "Ć": "C",
8922    "Č": "C",
8923    "Ç": "C",
8924    "Ḉ": "C",
8925    "Ĉ": "C",
8926    "Ċ": "C",
8927    "Ƈ": "C",
8928    "È»": "C",
8929    "Ď": "D",
8930    "Ḑ": "D",
8931    "Ḓ": "D",
8932    "Ḋ": "D",
8933    "Ḍ": "D",
8934    "Ɗ": "D",
8935    "Ḏ": "D",
8936    "Dz": "D",
8937    "Dž": "D",
8938    "Đ": "D",
8939    "Ƌ": "D",
8940    "DZ": "DZ",
8941    "DŽ": "DZ",
8942    "É": "E",
8943    "Ĕ": "E",
8944    "Ě": "E",
8945    "Ȩ": "E",
8946    "Ḝ": "E",
8947    "Ê": "E",
8948    "Ế": "E",
8949    "Ệ": "E",
8950    "Ề": "E",
8951    "Ể": "E",
8952    "Ễ": "E",
8953    "Ḙ": "E",
8954    "Ë": "E",
8955    "Ė": "E",
8956    "Ẹ": "E",
8957    "Ȅ": "E",
8958    "È": "E",
8959    "Ẻ": "E",
8960    "Ȇ": "E",
8961    "Ē": "E",
8962    "Ḗ": "E",
8963    "Ḕ": "E",
8964    "Ę": "E",
8965    "Ɇ": "E",
8966    "Ẽ": "E",
8967    "Ḛ": "E",
8968    "Ꝫ": "ET",
8969    "Ḟ": "F",
8970    "Ƒ": "F",
8971    "Ç´": "G",
8972    "Ğ": "G",
8973    "Ǧ": "G",
8974    "Ä¢": "G",
8975    "Ĝ": "G",
8976    "Ä ": "G",
8977    "Ɠ": "G",
8978    "Ḡ": "G",
8979    "Ǥ": "G",
8980    "Ḫ": "H",
8981    "Ȟ": "H",
8982    "Ḩ": "H",
8983    "Ĥ": "H",
8984    "â±§": "H",
8985    "Ḧ": "H",
8986    "Ḣ": "H",
8987    "Ḥ": "H",
8988    "Ħ": "H",
8989    "Í": "I",
8990    "Ĭ": "I",
8991    "Ǐ": "I",
8992    "Î": "I",
8993    "Ï": "I",
8994    "Ḯ": "I",
8995    "İ": "I",
8996    "Ị": "I",
8997    "Ȉ": "I",
8998    "Ì": "I",
8999    "Ỉ": "I",
9000    "Ȋ": "I",
9001    "Ī": "I",
9002    "Ä®": "I",
9003    "Ɨ": "I",
9004    "Ĩ": "I",
9005    "Ḭ": "I",
9006    "Ꝺ": "D",
9007    "Ꝼ": "F",
9008    "Ᵹ": "G",
9009    "Ꞃ": "R",
9010    "Ꞅ": "S",
9011    "Ꞇ": "T",
9012    "Ꝭ": "IS",
9013    "Ä´": "J",
9014    "Ɉ": "J",
9015    "Ḱ": "K",
9016    "Ǩ": "K",
9017    "Ķ": "K",
9018    "Ⱪ": "K",
9019    "Ꝃ": "K",
9020    "Ḳ": "K",
9021    "Ƙ": "K",
9022    "Ḵ": "K",
9023    "Ꝁ": "K",
9024    "Ꝅ": "K",
9025    "Ĺ": "L",
9026    "Ƚ": "L",
9027    "Ľ": "L",
9028    "Ä»": "L",
9029    "Ḽ": "L",
9030    "Ḷ": "L",
9031    "Ḹ": "L",
9032    "â± ": "L",
9033    "Ꝉ": "L",
9034    "Ḻ": "L",
9035    "Ä¿": "L",
9036    "â±¢": "L",
9037    "Lj": "L",
9038    "Ł": "L",
9039    "LJ": "LJ",
9040    "Ḿ": "M",
9041    "Ṁ": "M",
9042    "Ṃ": "M",
9043    "â±®": "M",
9044    "Ń": "N",
9045    "Ň": "N",
9046    "Ņ": "N",
9047    "Ṋ": "N",
9048    "Ṅ": "N",
9049    "Ṇ": "N",
9050    "Ǹ": "N",
9051    "Ɲ": "N",
9052    "Ṉ": "N",
9053    "È ": "N",
9054    "Nj": "N",
9055    "Ñ": "N",
9056    "NJ": "NJ",
9057    "Ó": "O",
9058    "Ŏ": "O",
9059    "Ǒ": "O",
9060    "Ô": "O",
9061    "Ố": "O",
9062    "Ộ": "O",
9063    "Ồ": "O",
9064    "Ổ": "O",
9065    "Ỗ": "O",
9066    "Ö": "O",
9067    "Ȫ": "O",
9068    "È®": "O",
9069    "Ȱ": "O",
9070    "Ọ": "O",
9071    "Ő": "O",
9072    "Ȍ": "O",
9073    "Ò": "O",
9074    "Ỏ": "O",
9075    "Æ ": "O",
9076    "Ớ": "O",
9077    "Ợ": "O",
9078    "Ờ": "O",
9079    "Ở": "O",
9080    "á» ": "O",
9081    "Ȏ": "O",
9082    "Ꝋ": "O",
9083    "Ꝍ": "O",
9084    "Ō": "O",
9085    "Ṓ": "O",
9086    "Ṑ": "O",
9087    "Ɵ": "O",
9088    "Ǫ": "O",
9089    "Ǭ": "O",
9090    "Ø": "O",
9091    "Ǿ": "O",
9092    "Õ": "O",
9093    "Ṍ": "O",
9094    "Ṏ": "O",
9095    "Ȭ": "O",
9096    "Æ¢": "OI",
9097    "Ꝏ": "OO",
9098    "Ɛ": "E",
9099    "Ɔ": "O",
9100    "È¢": "OU",
9101    "Ṕ": "P",
9102    "Ṗ": "P",
9103    "Ꝓ": "P",
9104    "Ƥ": "P",
9105    "Ꝕ": "P",
9106    "â±£": "P",
9107    "Ꝑ": "P",
9108    "Ꝙ": "Q",
9109    "Ꝗ": "Q",
9110    "Ŕ": "R",
9111    "Ř": "R",
9112    "Ŗ": "R",
9113    "Ṙ": "R",
9114    "Ṛ": "R",
9115    "Ṝ": "R",
9116    "Ȑ": "R",
9117    "Ȓ": "R",
9118    "Ṟ": "R",
9119    "Ɍ": "R",
9120    "Ɽ": "R",
9121    "Ꜿ": "C",
9122    "Ǝ": "E",
9123    "Ś": "S",
9124    "Ṥ": "S",
9125    "Å ": "S",
9126    "Ṧ": "S",
9127    "Ş": "S",
9128    "Ŝ": "S",
9129    "Ș": "S",
9130    "á¹ ": "S",
9131    "á¹¢": "S",
9132    "Ṩ": "S",
9133    "Ť": "T",
9134    "Å¢": "T",
9135    "á¹°": "T",
9136    "Ț": "T",
9137    "Ⱦ": "T",
9138    "Ṫ": "T",
9139    "Ṭ": "T",
9140    "Ƭ": "T",
9141    "á¹®": "T",
9142    "Æ®": "T",
9143    "Ŧ": "T",
9144    "Ɐ": "A",
9145    "Ꞁ": "L",
9146    "Ɯ": "M",
9147    "Ʌ": "V",
9148    "Ꜩ": "TZ",
9149    "Ú": "U",
9150    "Ŭ": "U",
9151    "Ǔ": "U",
9152    "Û": "U",
9153    "á¹¶": "U",
9154    "Ü": "U",
9155    "Ǘ": "U",
9156    "Ǚ": "U",
9157    "Ǜ": "U",
9158    "Ǖ": "U",
9159    "á¹²": "U",
9160    "Ụ": "U",
9161    "Ű": "U",
9162    "Ȕ": "U",
9163    "Ù": "U",
9164    "Ủ": "U",
9165    "Ư": "U",
9166    "Ứ": "U",
9167    "á»°": "U",
9168    "Ừ": "U",
9169    "Ử": "U",
9170    "á»®": "U",
9171    "Ȗ": "U",
9172    "Ū": "U",
9173    "Ṻ": "U",
9174    "Ų": "U",
9175    "Å®": "U",
9176    "Ũ": "U",
9177    "Ṹ": "U",
9178    "á¹´": "U",
9179    "Ꝟ": "V",
9180    "á¹¾": "V",
9181    "Ʋ": "V",
9182    "á¹¼": "V",
9183    "Ꝡ": "VY",
9184    "Ẃ": "W",
9185    "Å´": "W",
9186    "Ẅ": "W",
9187    "Ẇ": "W",
9188    "Ẉ": "W",
9189    "Ẁ": "W",
9190    "â±²": "W",
9191    "Ẍ": "X",
9192    "Ẋ": "X",
9193    "Ý": "Y",
9194    "Ŷ": "Y",
9195    "Ÿ": "Y",
9196    "Ẏ": "Y",
9197    "á»´": "Y",
9198    "Ỳ": "Y",
9199    "Ƴ": "Y",
9200    "á»¶": "Y",
9201    "Ỿ": "Y",
9202    "Ȳ": "Y",
9203    "Ɏ": "Y",
9204    "Ỹ": "Y",
9205    "Ź": "Z",
9206    "Ž": "Z",
9207    "Ẑ": "Z",
9208    "Ⱬ": "Z",
9209    "Å»": "Z",
9210    "Ẓ": "Z",
9211    "Ȥ": "Z",
9212    "Ẕ": "Z",
9213    "Ƶ": "Z",
9214    "IJ": "IJ",
9215    "Œ": "OE",
9216    "ᴀ": "A",
9217    "ᴁ": "AE",
9218    "ʙ": "B",
9219    "ᴃ": "B",
9220    "ᴄ": "C",
9221    "ᴅ": "D",
9222    "ᴇ": "E",
9223    "ꜰ": "F",
9224    "É¢": "G",
9225    "ʛ": "G",
9226    "ʜ": "H",
9227    "ɪ": "I",
9228    "ʁ": "R",
9229    "ᴊ": "J",
9230    "ᴋ": "K",
9231    "ʟ": "L",
9232    "ᴌ": "L",
9233    "ᴍ": "M",
9234    "É´": "N",
9235    "ᴏ": "O",
9236    "ɶ": "OE",
9237    "ᴐ": "O",
9238    "ᴕ": "OU",
9239    "ᴘ": "P",
9240    "ʀ": "R",
9241    "ᴎ": "N",
9242    "ᴙ": "R",
9243    "ꜱ": "S",
9244    "ᴛ": "T",
9245    "â±»": "E",
9246    "ᴚ": "R",
9247    "ᴜ": "U",
9248    "á´ ": "V",
9249    "á´¡": "W",
9250    "ʏ": "Y",
9251    "á´¢": "Z",
9252    "á": "a",
9253    "ă": "a",
9254    "ắ": "a",
9255    "ặ": "a",
9256    "ằ": "a",
9257    "ẳ": "a",
9258    "ẵ": "a",
9259    "ǎ": "a",
9260    "â": "a",
9261    "ấ": "a",
9262    "ậ": "a",
9263    "ầ": "a",
9264    "ẩ": "a",
9265    "ẫ": "a",
9266    "ä": "a",
9267    "ǟ": "a",
9268    "ȧ": "a",
9269    "Ç¡": "a",
9270    "ạ": "a",
9271    "ȁ": "a",
9272    "à": "a",
9273    "ả": "a",
9274    "ȃ": "a",
9275    "ā": "a",
9276    "ą": "a",
9277    "ᶏ": "a",
9278    "ẚ": "a",
9279    "Ã¥": "a",
9280    "Ç»": "a",
9281    "ḁ": "a",
9282    "â±¥": "a",
9283    "ã": "a",
9284    "ꜳ": "aa",
9285    "æ": "ae",
9286    "ǽ": "ae",
9287    "Ç£": "ae",
9288    "ꜵ": "ao",
9289    "ꜷ": "au",
9290    "ꜹ": "av",
9291    "ꜻ": "av",
9292    "ꜽ": "ay",
9293    "ḃ": "b",
9294    "ḅ": "b",
9295    "ɓ": "b",
9296    "ḇ": "b",
9297    "ᵬ": "b",
9298    "ᶀ": "b",
9299    "ƀ": "b",
9300    "ƃ": "b",
9301    "ɵ": "o",
9302    "ć": "c",
9303    "č": "c",
9304    "ç": "c",
9305    "ḉ": "c",
9306    "ĉ": "c",
9307    "ɕ": "c",
9308    "ċ": "c",
9309    "ƈ": "c",
9310    "ȼ": "c",
9311    "ď": "d",
9312    "ḑ": "d",
9313    "ḓ": "d",
9314    "È¡": "d",
9315    "ḋ": "d",
9316    "ḍ": "d",
9317    "ɗ": "d",
9318    "ᶑ": "d",
9319    "ḏ": "d",
9320    "áµ­": "d",
9321    "ᶁ": "d",
9322    "đ": "d",
9323    "ɖ": "d",
9324    "ƌ": "d",
9325    "ı": "i",
9326    "È·": "j",
9327    "ɟ": "j",
9328    "ʄ": "j",
9329    "dz": "dz",
9330    "dž": "dz",
9331    "é": "e",
9332    "ĕ": "e",
9333    "ě": "e",
9334    "È©": "e",
9335    "ḝ": "e",
9336    "ê": "e",
9337    "ế": "e",
9338    "ệ": "e",
9339    "ề": "e",
9340    "ể": "e",
9341    "ễ": "e",
9342    "ḙ": "e",
9343    "ë": "e",
9344    "ė": "e",
9345    "ẹ": "e",
9346    "ȅ": "e",
9347    "è": "e",
9348    "ẻ": "e",
9349    "ȇ": "e",
9350    "ē": "e",
9351    "ḗ": "e",
9352    "ḕ": "e",
9353    "ⱸ": "e",
9354    "ę": "e",
9355    "ᶒ": "e",
9356    "ɇ": "e",
9357    "ẽ": "e",
9358    "ḛ": "e",
9359    "ꝫ": "et",
9360    "ḟ": "f",
9361    "ƒ": "f",
9362    "áµ®": "f",
9363    "ᶂ": "f",
9364    "ǵ": "g",
9365    "ğ": "g",
9366    "ǧ": "g",
9367    "Ä£": "g",
9368    "ĝ": "g",
9369    "Ä¡": "g",
9370    "É ": "g",
9371    "ḡ": "g",
9372    "ᶃ": "g",
9373    "Ç¥": "g",
9374    "ḫ": "h",
9375    "ȟ": "h",
9376    "ḩ": "h",
9377    "Ä¥": "h",
9378    "ⱨ": "h",
9379    "ḧ": "h",
9380    "ḣ": "h",
9381    "ḥ": "h",
9382    "ɦ": "h",
9383    "ẖ": "h",
9384    "ħ": "h",
9385    "ƕ": "hv",
9386    "í": "i",
9387    "Ä­": "i",
9388    "ǐ": "i",
9389    "î": "i",
9390    "ï": "i",
9391    "ḯ": "i",
9392    "ị": "i",
9393    "ȉ": "i",
9394    "ì": "i",
9395    "ỉ": "i",
9396    "ȋ": "i",
9397    "Ä«": "i",
9398    "į": "i",
9399    "ᶖ": "i",
9400    "ɨ": "i",
9401    "Ä©": "i",
9402    "ḭ": "i",
9403    "ꝺ": "d",
9404    "ꝼ": "f",
9405    "áµ¹": "g",
9406    "ꞃ": "r",
9407    "ꞅ": "s",
9408    "ꞇ": "t",
9409    "ꝭ": "is",
9410    "ǰ": "j",
9411    "ĵ": "j",
9412    "ʝ": "j",
9413    "ɉ": "j",
9414    "ḱ": "k",
9415    "Ç©": "k",
9416    "Ä·": "k",
9417    "ⱪ": "k",
9418    "ꝃ": "k",
9419    "ḳ": "k",
9420    "ƙ": "k",
9421    "ḵ": "k",
9422    "ᶄ": "k",
9423    "ꝁ": "k",
9424    "ꝅ": "k",
9425    "ĺ": "l",
9426    "ƚ": "l",
9427    "ɬ": "l",
9428    "ľ": "l",
9429    "ļ": "l",
9430    "ḽ": "l",
9431    "È´": "l",
9432    "ḷ": "l",
9433    "ḹ": "l",
9434    "ⱡ": "l",
9435    "ꝉ": "l",
9436    "ḻ": "l",
9437    "ŀ": "l",
9438    "É«": "l",
9439    "ᶅ": "l",
9440    "É­": "l",
9441    "ł": "l",
9442    "lj": "lj",
9443    "Å¿": "s",
9444    "ẜ": "s",
9445    "ẛ": "s",
9446    "ẝ": "s",
9447    "ḿ": "m",
9448    "ṁ": "m",
9449    "ṃ": "m",
9450    "ɱ": "m",
9451    "ᵯ": "m",
9452    "ᶆ": "m",
9453    "ń": "n",
9454    "ň": "n",
9455    "ņ": "n",
9456    "ṋ": "n",
9457    "ȵ": "n",
9458    "ṅ": "n",
9459    "ṇ": "n",
9460    "ǹ": "n",
9461    "ɲ": "n",
9462    "ṉ": "n",
9463    "ƞ": "n",
9464    "áµ°": "n",
9465    "ᶇ": "n",
9466    "ɳ": "n",
9467    "ñ": "n",
9468    "nj": "nj",
9469    "ó": "o",
9470    "ŏ": "o",
9471    "ǒ": "o",
9472    "ô": "o",
9473    "ố": "o",
9474    "ộ": "o",
9475    "ồ": "o",
9476    "ổ": "o",
9477    "ỗ": "o",
9478    "ö": "o",
9479    "È«": "o",
9480    "ȯ": "o",
9481    "ȱ": "o",
9482    "ọ": "o",
9483    "ő": "o",
9484    "ȍ": "o",
9485    "ò": "o",
9486    "ỏ": "o",
9487    "Æ¡": "o",
9488    "ớ": "o",
9489    "ợ": "o",
9490    "ờ": "o",
9491    "ở": "o",
9492    "ỡ": "o",
9493    "ȏ": "o",
9494    "ꝋ": "o",
9495    "ꝍ": "o",
9496    "ⱺ": "o",
9497    "ō": "o",
9498    "ṓ": "o",
9499    "ṑ": "o",
9500    "Ç«": "o",
9501    "Ç­": "o",
9502    "ø": "o",
9503    "Ç¿": "o",
9504    "õ": "o",
9505    "ṍ": "o",
9506    "ṏ": "o",
9507    "È­": "o",
9508    "Æ£": "oi",
9509    "ꝏ": "oo",
9510    "ɛ": "e",
9511    "ᶓ": "e",
9512    "ɔ": "o",
9513    "ᶗ": "o",
9514    "È£": "ou",
9515    "ṕ": "p",
9516    "ṗ": "p",
9517    "ꝓ": "p",
9518    "Æ¥": "p",
9519    "áµ±": "p",
9520    "ᶈ": "p",
9521    "ꝕ": "p",
9522    "áµ½": "p",
9523    "ꝑ": "p",
9524    "ꝙ": "q",
9525    "Ê ": "q",
9526    "ɋ": "q",
9527    "ꝗ": "q",
9528    "ŕ": "r",
9529    "ř": "r",
9530    "ŗ": "r",
9531    "ṙ": "r",
9532    "ṛ": "r",
9533    "ṝ": "r",
9534    "ȑ": "r",
9535    "ɾ": "r",
9536    "áµ³": "r",
9537    "ȓ": "r",
9538    "ṟ": "r",
9539    "ɼ": "r",
9540    "áµ²": "r",
9541    "ᶉ": "r",
9542    "ɍ": "r",
9543    "ɽ": "r",
9544    "ↄ": "c",
9545    "ꜿ": "c",
9546    "ɘ": "e",
9547    "É¿": "r",
9548    "ś": "s",
9549    "á¹¥": "s",
9550    "Å¡": "s",
9551    "á¹§": "s",
9552    "ş": "s",
9553    "ŝ": "s",
9554    "ș": "s",
9555    "ṡ": "s",
9556    "á¹£": "s",
9557    "ṩ": "s",
9558    "ʂ": "s",
9559    "áµ´": "s",
9560    "ᶊ": "s",
9561    "È¿": "s",
9562    "É¡": "g",
9563    "ᴑ": "o",
9564    "ᴓ": "o",
9565    "ᴝ": "u",
9566    "Å¥": "t",
9567    "Å£": "t",
9568    "á¹±": "t",
9569    "ț": "t",
9570    "ȶ": "t",
9571    "ẗ": "t",
9572    "ⱦ": "t",
9573    "ṫ": "t",
9574    "á¹­": "t",
9575    "Æ­": "t",
9576    "ṯ": "t",
9577    "áµµ": "t",
9578    "Æ«": "t",
9579    "ʈ": "t",
9580    "ŧ": "t",
9581    "ᵺ": "th",
9582    "ɐ": "a",
9583    "ᴂ": "ae",
9584    "ǝ": "e",
9585    "áµ·": "g",
9586    "É¥": "h",
9587    "Ê®": "h",
9588    "ʯ": "h",
9589    "ᴉ": "i",
9590    "ʞ": "k",
9591    "ꞁ": "l",
9592    "ɯ": "m",
9593    "ɰ": "m",
9594    "ᴔ": "oe",
9595    "ɹ": "r",
9596    "É»": "r",
9597    "ɺ": "r",
9598    "â±¹": "r",
9599    "ʇ": "t",
9600    "ʌ": "v",
9601    "ʍ": "w",
9602    "ʎ": "y",
9603    "ꜩ": "tz",
9604    "ú": "u",
9605    "Å­": "u",
9606    "ǔ": "u",
9607    "û": "u",
9608    "á¹·": "u",
9609    "ü": "u",
9610    "ǘ": "u",
9611    "ǚ": "u",
9612    "ǜ": "u",
9613    "ǖ": "u",
9614    "á¹³": "u",
9615    "ụ": "u",
9616    "ű": "u",
9617    "ȕ": "u",
9618    "ù": "u",
9619    "á»§": "u",
9620    "ư": "u",
9621    "ứ": "u",
9622    "á»±": "u",
9623    "ừ": "u",
9624    "á»­": "u",
9625    "ữ": "u",
9626    "ȗ": "u",
9627    "Å«": "u",
9628    "á¹»": "u",
9629    "ų": "u",
9630    "ᶙ": "u",
9631    "ů": "u",
9632    "Å©": "u",
9633    "á¹¹": "u",
9634    "á¹µ": "u",
9635    "ᵫ": "ue",
9636    "ꝸ": "um",
9637    "â±´": "v",
9638    "ꝟ": "v",
9639    "ṿ": "v",
9640    "ʋ": "v",
9641    "ᶌ": "v",
9642    "â±±": "v",
9643    "á¹½": "v",
9644    "ꝡ": "vy",
9645    "ẃ": "w",
9646    "ŵ": "w",
9647    "ẅ": "w",
9648    "ẇ": "w",
9649    "ẉ": "w",
9650    "ẁ": "w",
9651    "â±³": "w",
9652    "ẘ": "w",
9653    "ẍ": "x",
9654    "ẋ": "x",
9655    "ᶍ": "x",
9656    "ý": "y",
9657    "Å·": "y",
9658    "ÿ": "y",
9659    "ẏ": "y",
9660    "ỵ": "y",
9661    "ỳ": "y",
9662    "Æ´": "y",
9663    "á»·": "y",
9664    "ỿ": "y",
9665    "ȳ": "y",
9666    "ẙ": "y",
9667    "ɏ": "y",
9668    "ỹ": "y",
9669    "ź": "z",
9670    "ž": "z",
9671    "ẑ": "z",
9672    "ʑ": "z",
9673    "ⱬ": "z",
9674    "ż": "z",
9675    "ẓ": "z",
9676    "È¥": "z",
9677    "ẕ": "z",
9678    "áµ¶": "z",
9679    "ᶎ": "z",
9680    "ʐ": "z",
9681    "ƶ": "z",
9682    "ɀ": "z",
9683    "ff": "ff",
9684    "ffi": "ffi",
9685    "ffl": "ffl",
9686    "fi": "fi",
9687    "fl": "fl",
9688    "ij": "ij",
9689    "œ": "oe",
9690    "st": "st",
9691    "ₐ": "a",
9692    "ₑ": "e",
9693    "áµ¢": "i",
9694    "â±¼": "j",
9695    "ₒ": "o",
9696    "áµ£": "r",
9697    "ᵤ": "u",
9698    "áµ¥": "v",
9699    "ₓ": "x"
9700};
9701String.prototype.latinise = function () { return this.replace(/[^A-Za-z0-9\[\] ]/g, function (a) { return Latinise.latin_map[a] || a }) };
9702String.prototype.latinize = String.prototype.latinise;
9703String.prototype.isLatin = function () { return this == this.latinise() }
9704
9705// END LATINIZE
9706
9707// BEGIN PROPOSE URL SEGMENT
9708String.prototype.proposePathSegment = function ()
9709{
9710    return this
9711        .toLowerCase()
9712        .latinize()
9713        .replace(/[^a-zA-Z0-9\-_~]/g, "-")
9714        .replace(/[\-~_]{2,}/g, "-");
9715};
9716// END PROPOSE URL SEGMENT
9717
9718// BEGIN SIMPLE DEBOUNCE
9719const debounceFunc = (func, delay) => {
9720   let timer;
9721   return function(...args) {
9722       const context = this;
9723       window.clearTimeout(timer);
9724       timer = window.setTimeout(() => {
9725           func.apply(context, args);
9726       }, delay)
9727    }
9728}
9729// END SIMPLE DEBOUNCE

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.