PageSourceSearch

https://www.georgiapower.com/etc.clientlibs/global/clientlibs/clientlib-base.min.js

js georgiapower.com collected 2026-10-01 08:03:20 UTC 551,056 bytes, 15,306 lines download raw bytes

1/*******************************************************************************
2 * Copyright 2018 Adobe
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 ******************************************************************************/
16
17/**
18 * Element.matches()
19 * https://developer.mozilla.org/enUS/docs/Web/API/Element/matches#Polyfill
20 */
21if (!Element.prototype.matches) {
22    Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
23}
24
25// eslint-disable-next-line valid-jsdoc
26/**
27 * Element.closest()
28 * https://developer.mozilla.org/enUS/docs/Web/API/Element/closest#Polyfill
29 */
30if (!Element.prototype.closest) {
31    Element.prototype.closest = function(s) {
32        "use strict";
33        var el = this;
34        if (!document.documentElement.contains(el)) {
35            return null;
36        }
37        do {
38            if (el.matches(s)) {
39                return el;
40            }
41            el = el.parentElement || el.parentNode;
42        } while (el !== null && el.nodeType === 1);
43        return null;
44    };
45}
46
47/*******************************************************************************
48 * Copyright 2018 Adobe
49 *
50 * Licensed under the Apache License, Version 2.0 (the "License");
51 * you may not use this file except in compliance with the License.
52 * You may obtain a copy of the License at
53 *
54 *     http://www.apache.org/licenses/LICENSE-2.0
55 *
56 * Unless required by applicable law or agreed to in writing, software
57 * distributed under the License is distributed on an "AS IS" BASIS,
58 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
59 * See the License for the specific language governing permissions and
60 * limitations under the License.
61 ******************************************************************************/
62/* global
63    CQ
64 */
65(function() {
66    "use strict";
67
68    var containerUtils = window.CQ && window.CQ.CoreComponents && window.CQ.CoreComponents.container && window.CQ.CoreComponents.container.utils ? window.CQ.CoreComponents.container.utils : undefined;
69    if (!containerUtils) {
70        // eslint-disable-next-line no-console
71        console.warn("Tabs: container utilities at window.CQ.CoreComponents.container.utils are not available. This can lead to missing features. Ensure the core.wcm.components.commons.site.container client library is included on the page.");
72    }
73    var dataLayerEnabled;
74    var dataLayer;
75    var dataLayerName;
76
77    var NS = "cmp";
78    var IS = "tabs";
79
80    var keyCodes = {
81        END: 35,
82        HOME: 36,
83        ARROW_LEFT: 37,
84        ARROW_UP: 38,
85        ARROW_RIGHT: 39,
86        ARROW_DOWN: 40
87    };
88
89    var selectors = {
90        self: "[data-" + NS + '-is="' + IS + '"]',
91        active: {
92            tab: "cmp-tabs__tab--active",
93            tabpanel: "cmp-tabs__tabpanel--active"
94        }
95    };
96
97    /**
98     * Tabs Configuration
99     *
100     * @typedef {Object} TabsConfig Represents a Tabs configuration
101     * @property {HTMLElement} element The HTMLElement representing the Tabs
102     * @property {Object} options The Tabs options
103     */
104
105    /**
106     * Tabs
107     *
108     * @class Tabs
109     * @classdesc An interactive Tabs component for navigating a list of tabs
110     * @param {TabsConfig} config The Tabs configuration
111     */
112    function Tabs(config) {
113        var that = this;
114
115        if (config && config.element) {
116            init(config);
117        }
118
119        /**
120         * Initializes the Tabs
121         *
122         * @private
123         * @param {TabsConfig} config The Tabs configuration
124         */
125        function init(config) {
126            that._config = config;
127
128            // prevents multiple initialization
129            config.element.removeAttribute("data-" + NS + "-is");
130
131            cacheElements(config.element);
132            that._active = getActiveIndex(that._elements["tab"]);
133
134            if (that._elements.tabpanel) {
135                refreshActive();
136                bindEvents();
137                scrollToDeepLinkIdInTabs();
138            }
139
140            if (window.Granite && window.Granite.author && window.Granite.author.MessageChannel) {
141                /*
142                 * Editor message handling:
143                 * - subscribe to "cmp.panelcontainer" message requests sent by the editor frame
144                 * - check that the message data panel container type is correct and that the id (path) matches this specific Tabs component
145                 * - if so, route the "navigate" operation to enact a navigation of the Tabs based on index data
146                 */
147                CQ.CoreComponents.MESSAGE_CHANNEL = CQ.CoreComponents.MESSAGE_CHANNEL || new window.Granite.author.MessageChannel("cqauthor", window);
148                CQ.CoreComponents.MESSAGE_CHANNEL.subscribeRequestMessage("cmp.panelcontainer", function(message) {
149                    if (message.data && message.data.type === "cmp-tabs" && message.data.id === that._elements.self.dataset["cmpPanelcontainerId"]) {
150                        if (message.data.operation === "navigate") {
151                            navigate(message.data.index);
152                        }
153                    }
154                });
155            }
156        }
157
158        /**
159         * Displays the panel containing the element that corresponds to the deep link in the URI fragment
160         * and scrolls the browser to this element.
161         */
162        function scrollToDeepLinkIdInTabs() {
163            if (containerUtils) {
164                var deepLinkItemIdx = containerUtils.getDeepLinkItemIdx(that, "tab", "tabpanel");
165                if (deepLinkItemIdx > -1) {
166                    var deepLinkItem = that._elements["tab"][deepLinkItemIdx];
167                    if (deepLinkItem && that._elements["tab"][that._active].id !== deepLinkItem.id) {
168                        navigateAndFocusTab(deepLinkItemIdx, true);
169                    }
170                    var hashId = window.location.hash.substring(1);
171                    if (hashId) {
172                        var hashItem = document.querySelector("[id='" + hashId + "']");
173                        if (hashItem) {
174                            hashItem.scrollIntoView();
175                        }
176                    }
177                }
178            }
179        }
180
181        /**
182         * Returns the index of the active tab, if no tab is active returns 0
183         *
184         * @param {Array} tabs Tab elements
185         * @returns {Number} Index of the active tab, 0 if none is active
186         */
187        function getActiveIndex(tabs) {
188            if (tabs) {
189                for (var i = 0; i < tabs.length; i++) {
190                    if (tabs[i].classList.contains(selectors.active.tab)) {
191                        return i;
192                    }
193                }
194            }
195            return 0;
196        }
197
198        /**
199         * Caches the Tabs elements as defined via the {@code data-tabs-hook="ELEMENT_NAME"} markup API
200         *
201         * @private
202         * @param {HTMLElement} wrapper The Tabs wrapper element
203         */
204        function cacheElements(wrapper) {
205            that._elements = {};
206            that._elements.self = wrapper;
207            var hooks = that._elements.self.querySelectorAll("[data-" + NS + "-hook-" + IS + "]");
208
209            for (var i = 0; i < hooks.length; i++) {
210                var hook = hooks[i];
211                if (hook.closest("." + NS + "-" + IS) === that._elements.self) { // only process own tab elements
212                    var capitalized = IS;
213                    capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
214                    var key = hook.dataset[NS + "Hook" + capitalized];
215                    if (that._elements[key]) {
216                        if (!Array.isArray(that._elements[key])) {
217                            var tmp = that._elements[key];
218                            that._elements[key] = [tmp];
219                        }
220                        that._elements[key].push(hook);
221                    } else {
222                        that._elements[key] = hook;
223                    }
224                }
225            }
226        }
227
228        /**
229         * Binds Tabs event handling
230         *
231         * @private
232         */
233        function bindEvents() {
234            window.addEventListener("hashchange", scrollToDeepLinkIdInTabs, false);
235            var tabs = that._elements["tab"];
236            if (tabs) {
237                for (var i = 0; i < tabs.length; i++) {
238                    (function(index) {
239                        tabs[i].addEventListener("click", function(event) {
240                            navigateAndFocusTab(index);
241                        });
242                        tabs[i].addEventListener("keydown", function(event) {
243                            onKeyDown(event);
244                        });
245                    })(i);
246                }
247            }
248        }
249
250        /**
251         * Handles tab keydown events
252         *
253         * @private
254         * @param {Object} event The keydown event
255         */
256        function onKeyDown(event) {
257            var index = that._active;
258            var lastIndex = that._elements["tab"].length - 1;
259
260            switch (event.keyCode) {
261                case keyCodes.ARROW_LEFT:
262                case keyCodes.ARROW_UP:
263                    event.preventDefault();
264                    if (index > 0) {
265                        navigateAndFocusTab(index - 1);
266                    }
267                    break;
268                case keyCodes.ARROW_RIGHT:
269                case keyCodes.ARROW_DOWN:
270                    event.preventDefault();
271                    if (index < lastIndex) {
272                        navigateAndFocusTab(index + 1);
273                    }
274                    break;
275                case keyCodes.HOME:
276                    event.preventDefault();
277                    navigateAndFocusTab(0);
278                    break;
279                case keyCodes.END:
280                    event.preventDefault();
281                    navigateAndFocusTab(lastIndex);
282                    break;
283                default:
284                    return;
285            }
286        }
287
288        /**
289         * Refreshes the tab markup based on the current {@code Tabs#_active} index
290         *
291         * @private
292         */
293        function refreshActive() {
294            var tabpanels = that._elements["tabpanel"];
295            var tabs = that._elements["tab"];
296
297            if (tabpanels) {
298                if (Array.isArray(tabpanels)) {
299                    for (var i = 0; i < tabpanels.length; i++) {
300                        if (i === parseInt(that._active)) {
301                            tabpanels[i].classList.add(selectors.active.tabpanel);
302                            tabpanels[i].removeAttribute("aria-hidden");
303                            tabs[i].classList.add(selectors.active.tab);
304                            tabs[i].setAttribute("aria-selected", true);
305                            tabs[i].setAttribute("tabindex", "0");
306                        } else {
307                            tabpanels[i].classList.remove(selectors.active.tabpanel);
308                            tabpanels[i].setAttribute("aria-hidden", true);
309                            tabs[i].classList.remove(selectors.active.tab);
310                            tabs[i].setAttribute("aria-selected", false);
311                            tabs[i].setAttribute("tabindex", "-1");
312                        }
313                    }
314                } else {
315                    // only one tab
316                    tabpanels.classList.add(selectors.active.tabpanel);
317                    tabs.classList.add(selectors.active.tab);
318                }
319            }
320        }
321
322        /**
323         * Focuses the element and prevents scrolling the element into view
324         *
325         * @param {HTMLElement} element Element to focus
326         */
327        function focusWithoutScroll(element) {
328            var x = window.scrollX || window.pageXOffset;
329            var y = window.scrollY || window.pageYOffset;
330            element.focus();
331            window.scrollTo(x, y);
332        }
333
334        /**
335         * Navigates to the tab at the provided index
336         *
337         * @private
338         * @param {Number} index The index of the tab to navigate to
339         */
340        function navigate(index) {
341            that._active = index;
342            refreshActive();
343        }
344
345        /**
346         * Navigates to the item at the provided index and ensures the active tab gains focus
347         *
348         * @private
349         * @param {Number} index The index of the item to navigate to
350         * @param {Boolean} keepHash true to keep the hash in the URL, false to update it
351         */
352        function navigateAndFocusTab(index, keepHash) {
353            var exActive = that._active;
354            if (!keepHash && containerUtils) {
355                containerUtils.updateUrlHash(that, "tab", index);
356            }
357            navigate(index);
358            focusWithoutScroll(that._elements["tab"][index]);
359
360            if (dataLayerEnabled) {
361
362                var activeItem = getDataLayerId(that._elements.tabpanel[index]);
363                var exActiveItem = getDataLayerId(that._elements.tabpanel[exActive]);
364
365                dataLayer.push({
366                    event: "cmp:show",
367                    eventInfo: {
368                        path: "component." + activeItem
369                    }
370                });
371
372                dataLayer.push({
373                    event: "cmp:hide",
374                    eventInfo: {
375                        path: "component." + exActiveItem
376                    }
377                });
378
379                var tabsId = that._elements.self.id;
380                var uploadPayload = { component: {} };
381                uploadPayload.component[tabsId] = { shownItems: [activeItem] };
382
383                var removePayload = { component: {} };
384                removePayload.component[tabsId] = { shownItems: undefined };
385
386                dataLayer.push(removePayload);
387                dataLayer.push(uploadPayload);
388            }
389        }
390    }
391
392    /**
393     * Reads options data from the Tabs wrapper element, defined via {@code data-cmp-*} data attributes
394     *
395     * @private
396     * @param {HTMLElement} element The Tabs element to read options data from
397     * @returns {Object} The options read from the component data attributes
398     */
399    function readData(element) {
400        var data = element.dataset;
401        var options = [];
402        var capitalized = IS;
403        capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
404        var reserved = ["is", "hook" + capitalized];
405
406        for (var key in data) {
407            if (Object.prototype.hasOwnProperty.call(data, key)) {
408                var value = data[key];
409
410                if (key.indexOf(NS) === 0) {
411                    key = key.slice(NS.length);
412                    key = key.charAt(0).toLowerCase() + key.substring(1);
413
414                    if (reserved.indexOf(key) === -1) {
415                        options[key] = value;
416                    }
417                }
418            }
419        }
420
421        return options;
422    }
423
424    /**
425     * Parses the dataLayer string and returns the ID
426     *
427     * @private
428     * @param {HTMLElement} item the accordion item
429     * @returns {String} dataLayerId or undefined
430     */
431    function getDataLayerId(item) {
432        if (item) {
433            if (item.dataset.cmpDataLayer) {
434                return Object.keys(JSON.parse(item.dataset.cmpDataLayer))[0];
435            } else {
436                return item.id;
437            }
438        }
439        return null;
440    }
441
442    /**
443     * Document ready handler and DOM mutation observers. Initializes Tabs components as necessary.
444     *
445     * @private
446     */
447    function onDocumentReady() {
448        dataLayerEnabled = document.body.hasAttribute("data-cmp-data-layer-enabled");
449        if (dataLayerEnabled) {
450            dataLayerName = document.body.getAttribute("data-cmp-data-layer-name") || "adobeDataLayer";
451            dataLayer = window[dataLayerName] = window[dataLayerName] || [];
452        }
453
454        var elements = document.querySelectorAll(selectors.self);
455        for (var i = 0; i < elements.length; i++) {
456            new Tabs({ element: elements[i], options: readData(elements[i]) });
457        }
458
459        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
460        var body = document.querySelector("body");
461        var observer = new MutationObserver(function(mutations) {
462            mutations.forEach(function(mutation) {
463                // needed for IE
464                var nodesArray = [].slice.call(mutation.addedNodes);
465                if (nodesArray.length > 0) {
466                    nodesArray.forEach(function(addedNode) {
467                        if (addedNode.querySelectorAll) {
468                            var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self));
469                            elementsArray.forEach(function(element) {
470                                new Tabs({ element: element, options: readData(element) });
471                            });
472                        }
473                    });
474                }
475            });
476        });
477
478        observer.observe(body, {
479            subtree: true,
480            childList: true,
481            characterData: true
482        });
483    }
484
485    if (document.readyState !== "loading") {
486        onDocumentReady();
487    } else {
488        document.addEventListener("DOMContentLoaded", onDocumentReady);
489    }
490
491    if (containerUtils) {
492        window.addEventListener("load", containerUtils.scrollToAnchor, false);
493    }
494
495}());
496
497/*******************************************************************************
498 * Copyright 2022 Adobe
499 *
500 * Licensed under the Apache License, Version 2.0 (the "License");
501 * you may not use this file except in compliance with the License.
502 * You may obtain a copy of the License at
503 *
504 *     http://www.apache.org/licenses/LICENSE-2.0
505 *
506 * Unless required by applicable law or agreed to in writing, software
507 * distributed under the License is distributed on an "AS IS" BASIS,
508 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
509 * See the License for the specific language governing permissions and
510 * limitations under the License.
511 ******************************************************************************/
512(function(document) {
513    "use strict";
514
515    window.CMP = window.CMP || {};
516    window.CMP.utils = (function() {
517        var NS = "cmp";
518
519        /**
520         * Reads options data from the Component wrapper element, defined via {@code data-cmp-*} data attributes
521         *
522         * @param {HTMLElement} element The component element to read options data from
523         * @param {String} is The component identifier
524         * @returns {String[]} The options read from the component data attributes
525         */
526        var readData = function(element, is) {
527            var data = element.dataset;
528            var options = [];
529            var capitalized = is;
530            capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
531            var reserved = ["is", "hook" + capitalized];
532
533            for (var key in data) {
534                if (Object.prototype.hasOwnProperty.call(data, key)) {
535                    var value = data[key];
536
537                    if (key.indexOf(NS) === 0) {
538                        key = key.slice(NS.length);
539                        key = key.charAt(0).toLowerCase() + key.substring(1);
540
541                        if (reserved.indexOf(key) === -1) {
542                            options[key] = value;
543                        }
544                    }
545                }
546            }
547            return options;
548        };
549
550        /**
551         * Set up the final properties of a component by evaluating the transform function or fall back to the default value on demand
552         * @param {String[]} options the options to transform
553         * @param {Object} properties object of properties of property functions
554         * @returns {Object} transformed properties
555         */
556        var setupProperties = function(options, properties) {
557            var transformedProperties = {};
558
559            for (var key in properties) {
560                if (Object.prototype.hasOwnProperty.call(properties, key)) {
561                    var property = properties[key];
562                    if (options && options[key] != null) {
563                        if (property && typeof property.transform === "function") {
564                            transformedProperties[key] = property.transform(options[key]);
565                        } else {
566                            transformedProperties[key] = options[key];
567                        }
568                    } else {
569                        transformedProperties[key] = properties[key]["default"];
570                    }
571                }
572            }
573            return transformedProperties;
574        };
575
576
577        return {
578            readData: readData,
579            setupProperties: setupProperties
580        };
581    }());
582}(window.document));
583
584/*******************************************************************************
585 * Copyright 2018 Adobe
586 *
587 * Licensed under the Apache License, Version 2.0 (the "License");
588 * you may not use this file except in compliance with the License.
589 * You may obtain a copy of the License at
590 *
591 *     http://www.apache.org/licenses/LICENSE-2.0
592 *
593 * Unless required by applicable law or agreed to in writing, software
594 * distributed under the License is distributed on an "AS IS" BASIS,
595 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
596 * See the License for the specific language governing permissions and
597 * limitations under the License.
598 ******************************************************************************/
599(function() {
600    "use strict";
601
602    var containerUtils = window.CQ && window.CQ.CoreComponents && window.CQ.CoreComponents.container && window.CQ.CoreComponents.container.utils ? window.CQ.CoreComponents.container.utils : undefined;
603    if (!containerUtils) {
604        // eslint-disable-next-line no-console
605        console.warn("Tabs: container utilities at window.CQ.CoreComponents.container.utils are not available. This can lead to missing features. Ensure the core.wcm.components.commons.site.container client library is included on the page.");
606    }
607    var dataLayerEnabled;
608    var dataLayer;
609    var dataLayerName;
610
611    var NS = "cmp";
612    var IS = "carousel";
613
614    var keyCodes = {
615        SPACE: 32,
616        END: 35,
617        HOME: 36,
618        ARROW_LEFT: 37,
619        ARROW_UP: 38,
620        ARROW_RIGHT: 39,
621        ARROW_DOWN: 40
622    };
623
624    var selectors = {
625        self: "[data-" + NS + '-is="' + IS + '"]'
626    };
627
628    var properties = {
629        /**
630         * Determines whether the Carousel will automatically transition between slides
631         *
632         * @memberof Carousel
633         * @type {Boolean}
634         * @default false
635         */
636        "autoplay": {
637            "default": false,
638            "transform": function(value) {
639                return !(value === null || typeof value === "undefined");
640            }
641        },
642        /**
643         * Duration (in milliseconds) before automatically transitioning to the next slide
644         *
645         * @memberof Carousel
646         * @type {Number}
647         * @default 5000
648         */
649        "delay": {
650            "default": 5000,
651            "transform": function(value) {
652                value = parseFloat(value);
653                return !isNaN(value) ? value : null;
654            }
655        },
656        /**
657         * Determines whether automatic pause on hovering the carousel is disabled
658         *
659         * @memberof Carousel
660         * @type {Boolean}
661         * @default false
662         */
663        "autopauseDisabled": {
664            "default": false,
665            "transform": function(value) {
666                return !(value === null || typeof value === "undefined");
667            }
668        }
669    };
670
671    /**
672     * Carousel Configuration
673     *
674     * @typedef {Object} CarouselConfig Represents a Carousel configuration
675     * @property {HTMLElement} element The HTMLElement representing the Carousel
676     * @property {*[]} options The Carousel options
677     */
678
679    /**
680     * Carousel
681     *
682     * @class Carousel
683     * @classdesc An interactive Carousel component for navigating a list of generic items
684     * @param {CarouselConfig} config The Carousel configuration
685     */
686    function Carousel(config) {
687        var that = this;
688
689        if (config && config.element) {
690            init(config);
691        }
692
693        /**
694         * Initializes the Carousel
695         *
696         * @private
697         * @param {CarouselConfig} config The Carousel configuration
698         */
699        function init(config) {
700            that._config = config;
701
702            // prevents multiple initialization
703            config.element.removeAttribute("data-" + NS + "-is");
704
705            setupProperties(config.options);
706            cacheElements(config.element);
707
708            that._active = 0;
709            that._paused = false;
710
711            if (that._elements.item) {
712                initializeActive();
713                bindEvents();
714                resetAutoplayInterval();
715                refreshPlayPauseActions();
716                scrollToDeepLinkIdInCarousel();
717            }
718
719            // TODO: This section is only relevant in edit mode and should move to the editor clientLib
720            if (window.Granite && window.Granite.author && window.Granite.author.MessageChannel) {
721                /*
722                 * Editor message handling:
723                 * - subscribe to "cmp.panelcontainer" message requests sent by the editor frame
724                 * - check that the message data panel container type is correct and that the id (path) matches this specific Carousel component
725                 * - if so, route the "navigate" operation to enact a navigation of the Carousel based on index data
726                 */
727                window.CQ = window.CQ || {};
728                window.CQ.CoreComponents = window.CQ.CoreComponents || {};
729                window.CQ.CoreComponents.MESSAGE_CHANNEL = window.CQ.CoreComponents.MESSAGE_CHANNEL || new window.Granite.author.MessageChannel("cqauthor", window);
730                window.CQ.CoreComponents.MESSAGE_CHANNEL.subscribeRequestMessage("cmp.panelcontainer", function(message) {
731                    if (message.data && message.data.type === "cmp-carousel" && message.data.id === that._elements.self.dataset["cmpPanelcontainerId"]) {
732                        if (message.data.operation === "navigate") {
733                            navigate(message.data.index);
734                        }
735                    }
736                });
737            }
738        }
739
740        /**
741         * Displays the slide containing the element that corresponds to the deep link in the URI fragment
742         * and scrolls the browser to this element.
743         */
744        function scrollToDeepLinkIdInCarousel() {
745            if (containerUtils) {
746                var deepLinkItemIdx = containerUtils.getDeepLinkItemIdx(that, "item", "item");
747                if (deepLinkItemIdx > -1) {
748                    var deepLinkItem = that._elements["item"][deepLinkItemIdx];
749                    if (deepLinkItem && that._elements["item"][that._active].id !== deepLinkItem.id) {
750                        navigateAndFocusIndicator(deepLinkItemIdx, true);
751                        // pause the carousel auto-rotation
752                        pause();
753                    }
754                    var hashId = window.location.hash.substring(1);
755                    if (hashId) {
756                        var hashItem = document.querySelector("[id='" + hashId + "']");
757                        if (hashItem) {
758                            hashItem.scrollIntoView();
759                        }
760                    }
761                }
762            }
763        }
764
765        /**
766         * Caches the Carousel elements as defined via the {@code data-carousel-hook="ELEMENT_NAME"} markup API
767         *
768         * @private
769         * @param {HTMLElement} wrapper The Carousel wrapper element
770         */
771        function cacheElements(wrapper) {
772            that._elements = {};
773            that._elements.self = wrapper;
774            var hooks = that._elements.self.querySelectorAll("[data-" + NS + "-hook-" + IS + "]");
775
776            for (var i = 0; i < hooks.length; i++) {
777                var hook = hooks[i];
778                var capitalized = IS;
779                capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
780                var key = hook.dataset[NS + "Hook" + capitalized];
781                if (that._elements[key]) {
782                    if (!Array.isArray(that._elements[key])) {
783                        var tmp = that._elements[key];
784                        that._elements[key] = [tmp];
785                    }
786                    that._elements[key].push(hook);
787                } else {
788                    that._elements[key] = hook;
789                }
790            }
791        }
792
793        /**
794         * Sets up properties for the Carousel based on the passed options.
795         *
796         * @private
797         * @param {Object} options The Carousel options
798         */
799        function setupProperties(options) {
800            that._properties = {};
801
802            for (var key in properties) {
803                if (Object.prototype.hasOwnProperty.call(properties, key)) {
804                    var property = properties[key];
805                    var value = null;
806
807                    if (options && options[key] != null) {
808                        value = options[key];
809
810                        // transform the provided option
811                        if (property && typeof property.transform === "function") {
812                            value = property.transform(value);
813                        }
814                    }
815
816                    if (value === null) {
817                        // value still null, take the property default
818                        value = properties[key]["default"];
819                    }
820
821                    that._properties[key] = value;
822                }
823            }
824        }
825
826        /**
827         * Binds Carousel event handling
828         *
829         * @private
830         */
831        function bindEvents() {
832            window.addEventListener("hashchange", scrollToDeepLinkIdInCarousel, false);
833            if (that._elements["previous"]) {
834                that._elements["previous"].addEventListener("click", function() {
835                    var index = getPreviousIndex();
836                    navigate(index);
837                    if (dataLayerEnabled) {
838                        dataLayer.push({
839                            event: "cmp:show",
840                            eventInfo: {
841                                path: "component." + getDataLayerId(that._elements.item[index])
842                            }
843                        });
844                    }
845                });
846            }
847
848            if (that._elements["next"]) {
849                that._elements["next"].addEventListener("click", function() {
850                    var index = getNextIndex();
851                    navigate(index);
852                    if (dataLayerEnabled) {
853                        dataLayer.push({
854                            event: "cmp:show",
855                            eventInfo: {
856                                path: "component." + getDataLayerId(that._elements.item[index])
857                            }
858                        });
859                    }
860                });
861            }
862
863            var indicators = that._elements["indicator"];
864            if (indicators) {
865                for (var i = 0; i < indicators.length; i++) {
866                    (function(index) {
867                        indicators[i].addEventListener("click", function(event) {
868                            navigateAndFocusIndicator(index);
869                            // pause the carousel auto-rotation
870                            pause();
871                        });
872                    })(i);
873                }
874            }
875
876            if (that._elements["pause"]) {
877                if (that._properties.autoplay) {
878                    that._elements["pause"].addEventListener("click", onPauseClick);
879                }
880            }
881
882            if (that._elements["play"]) {
883                if (that._properties.autoplay) {
884                    that._elements["play"].addEventListener("click", onPlayClick);
885                }
886            }
887
888            that._elements.self.addEventListener("keydown", onKeyDown);
889
890            if (!that._properties.autopauseDisabled) {
891                that._elements.self.addEventListener("mouseenter", onMouseEnter);
892                that._elements.self.addEventListener("mouseleave", onMouseLeave);
893            }
894
895            // for accessibility we pause animation when a element get focused
896            var items = that._elements["item"];
897            if (items) {
898                for (var j = 0; j < items.length; j++) {
899                    items[j].addEventListener("focusin", onMouseEnter);
900                    items[j].addEventListener("focusout", onMouseLeave);
901                }
902            }
903        }
904
905        /**
906         * Handles carousel keydown events
907         *
908         * @private
909         * @param {Object} event The keydown event
910         */
911        function onKeyDown(event) {
912            var index = that._active;
913            var lastIndex = that._elements["indicator"].length - 1;
914
915            switch (event.keyCode) {
916                case keyCodes.ARROW_LEFT:
917                case keyCodes.ARROW_UP:
918                    event.preventDefault();
919                    if (index > 0) {
920                        navigateAndFocusIndicator(index - 1);
921                    }
922                    break;
923                case keyCodes.ARROW_RIGHT:
924                case keyCodes.ARROW_DOWN:
925                    event.preventDefault();
926                    if (index < lastIndex) {
927                        navigateAndFocusIndicator(index + 1);
928                    }
929                    break;
930                case keyCodes.HOME:
931                    event.preventDefault();
932                    navigateAndFocusIndicator(0);
933                    break;
934                case keyCodes.END:
935                    event.preventDefault();
936                    navigateAndFocusIndicator(lastIndex);
937                    break;
938                case keyCodes.SPACE:
939                    if (that._properties.autoplay && (event.target !== that._elements["previous"] && event.target !== that._elements["next"])) {
940                        event.preventDefault();
941                        if (!that._paused) {
942                            pause();
943                        } else {
944                            play();
945                        }
946                    }
947                    if (event.target === that._elements["pause"]) {
948                        that._elements["play"].focus();
949                    }
950                    if (event.target === that._elements["play"]) {
951                        that._elements["pause"].focus();
952                    }
953                    break;
954                default:
955                    return;
956            }
957        }
958
959        /**
960         * Handles carousel mouseenter events
961         *
962         * @private
963         * @param {Object} event The mouseenter event
964         */
965        function onMouseEnter(event) {
966            clearAutoplayInterval();
967        }
968
969        /**
970         * Handles carousel mouseleave events
971         *
972         * @private
973         * @param {Object} event The mouseleave event
974         */
975        function onMouseLeave(event) {
976            resetAutoplayInterval();
977        }
978
979        /**
980         * Handles pause element click events
981         *
982         * @private
983         * @param {Object} event The click event
984         */
985        function onPauseClick(event) {
986            pause();
987            that._elements["play"].focus();
988        }
989
990        /**
991         * Handles play element click events
992         *
993         * @private
994         * @param {Object} event The click event
995         */
996        function onPlayClick() {
997            play();
998            that._elements["pause"].focus();
999        }
1000
1001        /**
1002         * Pauses the playing of the Carousel. Sets {@code Carousel#_paused} marker.
1003         * Only relevant when autoplay is enabled
1004         *
1005         * @private
1006         */
1007        function pause() {
1008            that._paused = true;
1009            clearAutoplayInterval();
1010            refreshPlayPauseActions();
1011        }
1012
1013        /**
1014         * Enables the playing of the Carousel. Sets {@code Carousel#_paused} marker.
1015         * Only relevant when autoplay is enabled
1016         *
1017         * @private
1018         */
1019        function play() {
1020            that._paused = false;
1021
1022            // If the Carousel is hovered, don't begin auto transitioning until the next mouse leave event
1023            var hovered = false;
1024            if (that._elements.self.parentElement) {
1025                hovered = that._elements.self.parentElement.querySelector(":hover") === that._elements.self;
1026            }
1027            if (that._properties.autopauseDisabled || !hovered) {
1028                resetAutoplayInterval();
1029            }
1030
1031            refreshPlayPauseActions();
1032        }
1033
1034        /**
1035         * Refreshes the play/pause action markup based on the {@code Carousel#_paused} state
1036         *
1037         * @private
1038         */
1039        function refreshPlayPauseActions() {
1040            setActionDisabled(that._elements["pause"], that._paused);
1041            setActionDisabled(that._elements["play"], !that._paused);
1042        }
1043
1044        /**
1045         * Initialize {@code Carousel#_active} based on the active item of the carousel.
1046         */
1047        function initializeActive() {
1048            var items = that._elements["item"];
1049            if (items && Array.isArray(items)) {
1050                for (var i = 0; i < items.length; i++) {
1051                    if (items[i].classList.contains("cmp-carousel__item--active")) {
1052                        that._active = i;
1053                        break;
1054                    }
1055                }
1056            }
1057        }
1058
1059        /**
1060         * Refreshes the item markup based on the current {@code Carousel#_active} index
1061         *
1062         * @private
1063         */
1064        function refreshActive() {
1065            var items = that._elements["item"];
1066            var indicators = that._elements["indicator"];
1067
1068            if (items) {
1069                if (Array.isArray(items)) {
1070                    for (var i = 0; i < items.length; i++) {
1071                        if (i === parseInt(that._active)) {
1072                            items[i].classList.add("cmp-carousel__item--active");
1073                            items[i].removeAttribute("aria-hidden");
1074                            indicators[i].classList.add("cmp-carousel__indicator--active");
1075                            indicators[i].setAttribute("aria-selected", true);
1076                            indicators[i].setAttribute("tabindex", "0");
1077                        } else {
1078                            items[i].classList.remove("cmp-carousel__item--active");
1079                            items[i].setAttribute("aria-hidden", true);
1080                            indicators[i].classList.remove("cmp-carousel__indicator--active");
1081                            indicators[i].setAttribute("aria-selected", false);
1082                            indicators[i].setAttribute("tabindex", "-1");
1083                        }
1084                    }
1085                } else {
1086                    // only one item
1087                    items.classList.add("cmp-carousel__item--active");
1088                    indicators.classList.add("cmp-carousel__indicator--active");
1089                }
1090            }
1091        }
1092
1093        /**
1094         * Focuses the element and prevents scrolling the element into view
1095         *
1096         * @param {HTMLElement} element Element to focus
1097         */
1098        function focusWithoutScroll(element) {
1099            var x = window.scrollX || window.pageXOffset;
1100            var y = window.scrollY || window.pageYOffset;
1101            element.focus();
1102            window.scrollTo(x, y);
1103        }
1104
1105        /**
1106         * Retrieves the next active index, with looping
1107         *
1108         * @private
1109         * @returns {Number} Index of the next carousel item
1110         */
1111        function getNextIndex() {
1112            return that._active === (that._elements["item"].length - 1) ? 0 : that._active + 1;
1113        }
1114
1115        /**
1116         * Retrieves the previous active index, with looping
1117         *
1118         * @private
1119         * @returns {Number} Index of the previous carousel item
1120         */
1121        function getPreviousIndex() {
1122            return that._active === 0 ? (that._elements["item"].length - 1) : that._active - 1;
1123        }
1124
1125        /**
1126         * Navigates to the item at the provided index
1127         *
1128         * @private
1129         * @param {Number} index The index of the item to navigate to
1130         * @param {Boolean} keepHash true to keep the hash in the URL, false to update it
1131         */
1132        function navigate(index, keepHash) {
1133            if (index < 0 || index > (that._elements["item"].length - 1)) {
1134                return;
1135            }
1136
1137            that._active = index;
1138            refreshActive();
1139
1140            if (!keepHash && containerUtils) {
1141                containerUtils.updateUrlHash(that, "item", index);
1142            }
1143
1144            if (dataLayerEnabled) {
1145                var carouselId = that._elements.self.id;
1146                var activeItem = getDataLayerId(that._elements.item[index]);
1147                var updatePayload = { component: {} };
1148                updatePayload.component[carouselId] = { shownItems: [activeItem] };
1149
1150                var removePayload = { component: {} };
1151                removePayload.component[carouselId] = { shownItems: undefined };
1152
1153                dataLayer.push(removePayload);
1154                dataLayer.push(updatePayload);
1155            }
1156
1157            // reset the autoplay transition interval following navigation, if not already hovering the carousel
1158            if (that._elements.self.parentElement) {
1159                if (that._elements.self.parentElement.querySelector(":hover") !== that._elements.self) {
1160                    resetAutoplayInterval();
1161                }
1162            }
1163        }
1164
1165        /**
1166         * Navigates to the item at the provided index and ensures the active indicator gains focus
1167         *
1168         * @private
1169         * @param {Number} index The index of the item to navigate to
1170         * @param {Boolean} keepHash true to keep the hash in the URL, false to update it
1171         */
1172        function navigateAndFocusIndicator(index, keepHash) {
1173            navigate(index, keepHash);
1174            focusWithoutScroll(that._elements["indicator"][index]);
1175
1176            if (dataLayerEnabled) {
1177                dataLayer.push({
1178                    event: "cmp:show",
1179                    eventInfo: {
1180                        path: "component." + getDataLayerId(that._elements.item[index])
1181                    }
1182                });
1183            }
1184        }
1185
1186        /**
1187         * Starts/resets automatic slide transition interval
1188         *
1189         * @private
1190         */
1191        function resetAutoplayInterval() {
1192            if (that._paused || !that._properties.autoplay) {
1193                return;
1194            }
1195            clearAutoplayInterval();
1196            that._autoplayIntervalId = window.setInterval(function() {
1197                if (document.visibilityState && document.hidden) {
1198                    return;
1199                }
1200                var indicators = that._elements["indicators"];
1201                if (indicators !== document.activeElement && indicators.contains(document.activeElement)) {
1202                    // if an indicator has focus, ensure we switch focus following navigation
1203                    navigateAndFocusIndicator(getNextIndex(), true);
1204                } else {
1205                    navigate(getNextIndex(), true);
1206                }
1207            }, that._properties.delay);
1208        }
1209
1210        /**
1211         * Clears/pauses automatic slide transition interval
1212         *
1213         * @private
1214         */
1215        function clearAutoplayInterval() {
1216            window.clearInterval(that._autoplayIntervalId);
1217            that._autoplayIntervalId = null;
1218        }
1219
1220        /**
1221         * Sets the disabled state for an action and toggles the appropriate CSS classes
1222         *
1223         * @private
1224         * @param {HTMLElement} action Action to disable
1225         * @param {Boolean} [disable] {@code true} to disable, {@code false} to enable
1226         */
1227        function setActionDisabled(action, disable) {
1228            if (!action) {
1229                return;
1230            }
1231            if (disable !== false) {
1232                action.disabled = true;
1233                action.classList.add("cmp-carousel__action--disabled");
1234            } else {
1235                action.disabled = false;
1236                action.classList.remove("cmp-carousel__action--disabled");
1237            }
1238        }
1239    }
1240
1241    /**
1242     * Parses the dataLayer string and returns the ID
1243     *
1244     * @private
1245     * @param {HTMLElement} item the accordion item
1246     * @returns {String} dataLayerId or undefined
1247     */
1248    function getDataLayerId(item) {
1249        if (item) {
1250            if (item.dataset.cmpDataLayer) {
1251                return Object.keys(JSON.parse(item.dataset.cmpDataLayer))[0];
1252            } else {
1253                return item.id;
1254            }
1255        }
1256        return null;
1257    }
1258
1259    /**
1260     * Document ready handler and DOM mutation observers. Initializes Carousel components as necessary.
1261     *
1262     * @private
1263     */
1264    function onDocumentReady() {
1265        dataLayerEnabled = document.body.hasAttribute("data-cmp-data-layer-enabled");
1266        if (dataLayerEnabled) {
1267            dataLayerName = document.body.getAttribute("data-cmp-data-layer-name") || "adobeDataLayer";
1268            dataLayer = window[dataLayerName] = window[dataLayerName] || [];
1269        }
1270
1271        var elements = document.querySelectorAll(selectors.self);
1272        for (var i = 0; i < elements.length; i++) {
1273            new Carousel({ element: elements[i], options: CMP.utils.readData(elements[i], IS) });
1274        }
1275
1276        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
1277        var body = document.querySelector("body");
1278        var observer = new MutationObserver(function(mutations) {
1279            mutations.forEach(function(mutation) {
1280                // needed for IE
1281                var nodesArray = [].slice.call(mutation.addedNodes);
1282                if (nodesArray.length > 0) {
1283                    nodesArray.forEach(function(addedNode) {
1284                        if (addedNode.querySelectorAll) {
1285                            var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self));
1286                            elementsArray.forEach(function(element) {
1287                                new Carousel({ element: element, options: CMP.utils.readData(element, IS) });
1288                            });
1289                        }
1290                    });
1291                }
1292            });
1293        });
1294
1295        observer.observe(body, {
1296            subtree: true,
1297            childList: true,
1298            characterData: true
1299        });
1300    }
1301
1302    var documentReady = document.readyState !== "loading" ? Promise.resolve() : new Promise(function(resolve) {
1303        document.addEventListener("DOMContentLoaded", resolve);
1304    });
1305    Promise.all([documentReady]).then(onDocumentReady);
1306
1307    if (containerUtils) {
1308        window.addEventListener("load", containerUtils.scrollToAnchor, false);
1309    }
1310
1311}());
1312
1313/*******************************************************************************
1314 * Copyright 2017 Adobe
1315 *
1316 * Licensed under the Apache License, Version 2.0 (the "License");
1317 * you may not use this file except in compliance with the License.
1318 * You may obtain a copy of the License at
1319 *
1320 *     http://www.apache.org/licenses/LICENSE-2.0
1321 *
1322 * Unless required by applicable law or agreed to in writing, software
1323 * distributed under the License is distributed on an "AS IS" BASIS,
1324 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1325 * See the License for the specific language governing permissions and
1326 * limitations under the License.
1327 ******************************************************************************/
1328if (window.Element && !Element.prototype.closest) {
1329    // eslint valid-jsdoc: "off"
1330    Element.prototype.closest =
1331        function(s) {
1332            "use strict";
1333            var matches = (this.document || this.ownerDocument).querySelectorAll(s);
1334            var el      = this;
1335            var i;
1336            do {
1337                i = matches.length;
1338                while (--i >= 0 && matches.item(i) !== el) {
1339                    // continue
1340                }
1341            } while ((i < 0) && (el = el.parentElement));
1342            return el;
1343        };
1344}
1345
1346if (window.Element && !Element.prototype.matches) {
1347    Element.prototype.matches =
1348        Element.prototype.matchesSelector ||
1349        Element.prototype.mozMatchesSelector ||
1350        Element.prototype.msMatchesSelector ||
1351        Element.prototype.oMatchesSelector ||
1352        Element.prototype.webkitMatchesSelector ||
1353        function(s) {
1354            "use strict";
1355            var matches = (this.document || this.ownerDocument).querySelectorAll(s);
1356            var i       = matches.length;
1357            while (--i >= 0 && matches.item(i) !== this) {
1358                // continue
1359            }
1360            return i > -1;
1361        };
1362}
1363
1364if (!Object.assign) {
1365    Object.assign = function(target, varArgs) { // .length of function is 2
1366        "use strict";
1367        if (target === null) {
1368            throw new TypeError("Cannot convert undefined or null to object");
1369        }
1370
1371        var to = Object(target);
1372
1373        for (var index = 1; index < arguments.length; index++) {
1374            var nextSource = arguments[index];
1375
1376            if (nextSource !== null) {
1377                for (var nextKey in nextSource) {
1378                    if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
1379                        to[nextKey] = nextSource[nextKey];
1380                    }
1381                }
1382            }
1383        }
1384        return to;
1385    };
1386}
1387
1388(function(arr) {
1389    "use strict";
1390    arr.forEach(function(item) {
1391        if (Object.prototype.hasOwnProperty.call(item, "remove")) {
1392            return;
1393        }
1394        Object.defineProperty(item, "remove", {
1395            configurable: true,
1396            enumerable: true,
1397            writable: true,
1398            value: function remove() {
1399                this.parentNode.removeChild(this);
1400            }
1401        });
1402    });
1403})([Element.prototype, CharacterData.prototype, DocumentType.prototype]);
1404
1405/*******************************************************************************
1406 * Copyright 2022 Adobe
1407 *
1408 * Licensed under the Apache License, Version 2.0 (the "License");
1409 * you may not use this file except in compliance with the License.
1410 * You may obtain a copy of the License at
1411 *
1412 *     http://www.apache.org/licenses/LICENSE-2.0
1413 *
1414 * Unless required by applicable law or agreed to in writing, software
1415 * distributed under the License is distributed on an "AS IS" BASIS,
1416 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1417 * See the License for the specific language governing permissions and
1418 * limitations under the License.
1419 ******************************************************************************/
1420(function(document) {
1421    "use strict";
1422
1423    window.CMP = window.CMP || {};
1424    window.CMP.utils = (function() {
1425        var NS = "cmp";
1426
1427        /**
1428         * Reads options data from the Component wrapper element, defined via {@code data-cmp-*} data attributes
1429         *
1430         * @param {HTMLElement} element The component element to read options data from
1431         * @param {String} is The component identifier
1432         * @returns {String[]} The options read from the component data attributes
1433         */
1434        var readData = function(element, is) {
1435            var data = element.dataset;
1436            var options = [];
1437            var capitalized = is;
1438            capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
1439            var reserved = ["is", "hook" + capitalized];
1440
1441            for (var key in data) {
1442                if (Object.prototype.hasOwnProperty.call(data, key)) {
1443                    var value = data[key];
1444
1445                    if (key.indexOf(NS) === 0) {
1446                        key = key.slice(NS.length);
1447                        key = key.charAt(0).toLowerCase() + key.substring(1);
1448
1449                        if (reserved.indexOf(key) === -1) {
1450                            options[key] = value;
1451                        }
1452                    }
1453                }
1454            }
1455            return options;
1456        };
1457
1458        /**
1459         * Set up the final properties of a component by evaluating the transform function or fall back to the default value on demand
1460         * @param {String[]} options the options to transform
1461         * @param {Object} properties object of properties of property functions
1462         * @returns {Object} transformed properties
1463         */
1464        var setupProperties = function(options, properties) {
1465            var transformedProperties = {};
1466
1467            for (var key in properties) {
1468                if (Object.prototype.hasOwnProperty.call(properties, key)) {
1469                    var property = properties[key];
1470                    if (options && options[key] != null) {
1471                        if (property && typeof property.transform === "function") {
1472                            transformedProperties[key] = property.transform(options[key]);
1473                        } else {
1474                            transformedProperties[key] = options[key];
1475                        }
1476                    } else {
1477                        transformedProperties[key] = properties[key]["default"];
1478                    }
1479                }
1480            }
1481            return transformedProperties;
1482        };
1483
1484
1485        return {
1486            readData: readData,
1487            setupProperties: setupProperties
1488        };
1489    }());
1490}(window.document));
1491
1492/*******************************************************************************
1493 * Copyright 2022 Adobe
1494 *
1495 * Licensed under the Apache License, Version 2.0 (the "License");
1496 * you may not use this file except in compliance with the License.
1497 * You may obtain a copy of the License at
1498 *
1499 *     http://www.apache.org/licenses/LICENSE-2.0
1500 *
1501 * Unless required by applicable law or agreed to in writing, software
1502 * distributed under the License is distributed on an "AS IS" BASIS,
1503 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1504 * See the License for the specific language governing permissions and
1505 * limitations under the License.
1506 ******************************************************************************/
1507(function(document) {
1508    "use strict";
1509
1510    window.CMP = window.CMP || {};
1511    window.CMP.image = window.CMP.image || {};
1512    window.CMP.image.dynamicMedia = (function() {
1513        var autoSmartCrops = {};
1514        var SRC_URI_TEMPLATE_WIDTH_VAR = "{.width}";
1515        var SRC_URI_TEMPLATE_DPR_VAR = "{dpr}";
1516        var SRC_URI_DPR_OFF = "dpr=off";
1517        var SRC_URI_DPR_ON = "dpr=on,{dpr}";
1518        var dpr = window.devicePixelRatio || 1;
1519        var config = {
1520            minWidth: 20
1521        };
1522
1523        /**
1524         * get auto smart crops from dm
1525         * @param {String} src the src uri
1526         * @returns {{}} the smart crop json object
1527         */
1528        var getAutoSmartCrops = function(src) {
1529            var request = new XMLHttpRequest();
1530            var url = src.split(SRC_URI_TEMPLATE_WIDTH_VAR)[0] + "?req=set,json";
1531            request.open("GET", url, false);
1532            request.onload = function() {
1533                if (request.status >= 200 && request.status < 400) {
1534                    // success status
1535                    var responseText = request.responseText;
1536                    var rePayload = new RegExp(/^(?:\/\*jsonp\*\/)?\s*([^()]+)\(([\s\S]+),\s*"[0-9]*"\);?$/gmi);
1537                    var rePayloadJSON = new RegExp(/^{[\s\S]*}$/gmi);
1538                    var resPayload = rePayload.exec(responseText);
1539                    var payload;
1540                    if (resPayload) {
1541                        var payloadStr = resPayload[2];
1542                        if (rePayloadJSON.test(payloadStr)) {
1543                            payload = JSON.parse(payloadStr);
1544                        }
1545
1546                    }
1547                    // check "relation" - only in case of smartcrop preset
1548                    if (payload && payload.set.relation && payload.set.relation.length > 0) {
1549                        for (var i = 0; i < payload.set.relation.length; i++) {
1550                            autoSmartCrops[parseInt(payload.set.relation[i].userdata.SmartCropWidth)] =
1551                                ":" + payload.set.relation[i].userdata.SmartCropDef;
1552                        }
1553                    }
1554                } else {
1555                    // error status
1556                }
1557            };
1558            request.send();
1559            return autoSmartCrops;
1560        };
1561
1562        /**
1563         * Build and return the srcset value based on the available auto smart crops
1564         * @param {String} src the src uri
1565         * @param {Object} smartCrops the smart crops object
1566         * @returns {String} the srcset
1567         */
1568        var getSrcSet = function(src, smartCrops) {
1569            var srcset;
1570            var keys = Object.keys(smartCrops);
1571            if (keys.length > 0) {
1572                srcset = [];
1573                for (var key in autoSmartCrops) {
1574                    srcset.push(src.replace(SRC_URI_TEMPLATE_WIDTH_VAR, smartCrops[key]) + " " + key + "w");
1575                }
1576            }
1577            return  srcset.join(",");
1578        };
1579
1580        /**
1581         * Get the optimal width based on the available sizes
1582         * @param {[Number]} sizes the available sizes
1583         * @param {Number} width the element width
1584         * @returns {String} the optimal width
1585         */
1586        function getOptimalWidth(sizes, width) {
1587            var len = sizes.length;
1588            var key = 0;
1589
1590            while ((key < len - 1) && (sizes[key] < width)) {
1591                key++;
1592            }
1593
1594            return sizes[key] !== undefined ? sizes[key].toString() : width;
1595        }
1596
1597        /**
1598         * Get the width of an element or parent element if the width is smaller than the minimum width
1599         * @param {HTMLElement} component the image component
1600         * @param {HTMLElement | Node} parent the parent element
1601         * @returns {Number} the width of the element
1602         */
1603        var getWidth = function(component, parent) {
1604            var width = component.offsetWidth;
1605            while (width < config.minWidth && parent && !component._autoWidth) {
1606                width =  parent.offsetWidth;
1607                parent = parent.parentNode;
1608            }
1609            return width;
1610        };
1611
1612        /**
1613         * Set the src and srcset attribute for a Dynamic Media Image which auto smart crops enabled.
1614         * @param {HTMLElement} component the image component
1615         * @param {{}} properties the component properties
1616         */
1617        var setDMAttributes = function(component, properties) {
1618            // for v3 we first have to turn the dpr on
1619            var src = properties.src.replace(SRC_URI_DPR_OFF, SRC_URI_DPR_ON);
1620            src = src.replace(SRC_URI_TEMPLATE_DPR_VAR, dpr);
1621            var smartCrops = {};
1622            var width;
1623            if (properties["smartcroprendition"] === "SmartCrop:Auto") {
1624                smartCrops = getAutoSmartCrops(src);
1625            }
1626            var hasWidths = (properties.widths && properties.widths.length > 0) || Object.keys(smartCrops).length > 0;
1627            if (hasWidths) {
1628                var image = component.querySelector("img");
1629                var elemWidth = getWidth(component, component.parentNode);
1630                if (properties["smartcroprendition"] === "SmartCrop:Auto") {
1631                    image.setAttribute("srcset", CMP.image.dynamicMedia.getSrcSet(src, smartCrops));
1632                    width = getOptimalWidth(Object.keys(smartCrops, elemWidth));
1633                    image.setAttribute("src", CMP.image.dynamicMedia.getSrc(src, smartCrops[width]));
1634                } else {
1635                    width = getOptimalWidth(properties.widths, elemWidth);
1636                    image.setAttribute("src", CMP.image.dynamicMedia.getSrc(src, width));
1637                }
1638            }
1639        };
1640
1641        /**
1642         * Get the src attribute based on the optimal width
1643         * @param {String} src the src uri
1644         * @param {String} width the element width
1645         * @returns {String} the final src attribute
1646         */
1647        var getSrc = function(src, width) {
1648            if (src.indexOf(SRC_URI_TEMPLATE_WIDTH_VAR) > -1) {
1649                src = src.replace(SRC_URI_TEMPLATE_WIDTH_VAR, width);
1650            }
1651            return src;
1652        };
1653
1654
1655        return {
1656            getAutoSmartCrops: getAutoSmartCrops,
1657            getSrcSet: getSrcSet,
1658            getSrc: getSrc,
1659            setDMAttributes: setDMAttributes,
1660            getWidth: getWidth
1661        };
1662    }());
1663    document.dispatchEvent(new CustomEvent("core.wcm.components.commons.site.image.dynamic-media.loaded"));
1664}(window.document));
1665
1666/*******************************************************************************
1667 * Copyright 2016 Adobe
1668 *
1669 * Licensed under the Apache License, Version 2.0 (the "License");
1670 * you may not use this file except in compliance with the License.
1671 * You may obtain a copy of the License at
1672 *
1673 *     http://www.apache.org/licenses/LICENSE-2.0
1674 *
1675 * Unless required by applicable law or agreed to in writing, software
1676 * distributed under the License is distributed on an "AS IS" BASIS,
1677 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1678 * See the License for the specific language governing permissions and
1679 * limitations under the License.
1680 ******************************************************************************/
1681(function() {
1682    "use strict";
1683
1684    var NS = "cmp";
1685    var IS = "image";
1686
1687    var EMPTY_PIXEL = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
1688    var LAZY_THRESHOLD_DEFAULT = 0;
1689    var SRC_URI_TEMPLATE_WIDTH_VAR = "{.width}";
1690    var SRC_URI_TEMPLATE_WIDTH_VAR_ASSET_DELIVERY = "width={width}";
1691    var SRC_URI_TEMPLATE_DPR_VAR = "{dpr}";
1692
1693    var selectors = {
1694        self: "[data-" + NS + '-is="' + IS + '"]',
1695        image: '[data-cmp-hook-image="image"]',
1696        map: '[data-cmp-hook-image="map"]',
1697        area: '[data-cmp-hook-image="area"]'
1698    };
1699
1700    var lazyLoader = {
1701        "cssClass": "cmp-image__image--is-loading",
1702        "style": {
1703            "height": 0,
1704            "padding-bottom": "" // will be replaced with % ratio
1705        }
1706    };
1707
1708    var properties = {
1709        /**
1710         * An array of alternative image widths (in pixels).
1711         * Used to replace a {.width} variable in the src property with an optimal width if a URI template is provided.
1712         *
1713         * @memberof Image
1714         * @type {Number[]}
1715         * @default []
1716         */
1717        "widths": {
1718            "default": [],
1719            "transform": function(value) {
1720                var widths = [];
1721                value.split(",").forEach(function(item) {
1722                    item = parseFloat(item);
1723                    if (!isNaN(item)) {
1724                        widths.push(item);
1725                    }
1726                });
1727                return widths;
1728            }
1729        },
1730        /**
1731         * Indicates whether the image should be rendered lazily.
1732         *
1733         * @memberof Image
1734         * @type {Boolean}
1735         * @default false
1736         */
1737        "lazy": {
1738            "default": false,
1739            "transform": function(value) {
1740                return !(value === null || typeof value === "undefined");
1741            }
1742        },
1743        /**
1744         * Indicates image is DynamicMedia image.
1745         *
1746         * @memberof Image
1747         * @type {Boolean}
1748         * @default false
1749         */
1750        "dmimage": {
1751            "default": false,
1752            "transform": function(value) {
1753                return !(value === null || typeof value === "undefined");
1754            }
1755        },
1756        /**
1757         * The lazy threshold.
1758         * This is the number of pixels, in advance of becoming visible, when an lazy-loading image should begin
1759         * to load.
1760         *
1761         * @memberof Image
1762         * @type {Number}
1763         * @default 0
1764         */
1765        "lazythreshold": {
1766            "default": 0,
1767            "transform": function(value) {
1768                var val =  parseInt(value);
1769                if (isNaN(val)) {
1770                    return LAZY_THRESHOLD_DEFAULT;
1771                }
1772                return val;
1773            }
1774        },
1775        /**
1776         * The image source.
1777         *
1778         * Can be a simple image source, or a URI template representation that
1779         * can be variable expanded - useful for building an image configuration with an alternative width.
1780         * e.g. '/path/image.coreimg{.width}.jpeg/1506620954214.jpeg'
1781         *
1782         * @memberof Image
1783         * @type {String}
1784         */
1785        "src": {
1786            "transform": function(value) {
1787                return decodeURIComponent(value);
1788            }
1789        }
1790    };
1791
1792    var devicePixelRatio = window.devicePixelRatio || 1;
1793
1794    function Image(config) {
1795        var that = this;
1796
1797        var smartCrops = {};
1798
1799        var useAssetDelivery = false;
1800        var srcUriTemplateWidthVar = SRC_URI_TEMPLATE_WIDTH_VAR;
1801
1802        function init(config) {
1803            // prevents multiple initialization
1804            config.element.removeAttribute("data-" + NS + "-is");
1805
1806            // check if asset delivery is used
1807            if (config.options.src && config.options.src.indexOf(SRC_URI_TEMPLATE_WIDTH_VAR_ASSET_DELIVERY) >= 0) {
1808                useAssetDelivery = true;
1809                srcUriTemplateWidthVar = SRC_URI_TEMPLATE_WIDTH_VAR_ASSET_DELIVERY;
1810            }
1811
1812            that._properties = CMP.utils.setupProperties(config.options, properties);
1813            cacheElements(config.element);
1814            // check image is DM asset; if true try to make req=set
1815            if (config.options.src && Object.prototype.hasOwnProperty.call(config.options, "dmimage") && (config.options["smartcroprendition"] === "SmartCrop:Auto")) {
1816                smartCrops = CMP.image.dynamicMedia.getAutoSmartCrops(config.options.src);
1817            }
1818
1819            if (!that._elements.noscript) {
1820                return;
1821            }
1822
1823            that._elements.container = that._elements.link ? that._elements.link : that._elements.self;
1824
1825            unwrapNoScript();
1826
1827            if (that._properties.lazy) {
1828                addLazyLoader();
1829            }
1830
1831            if (that._elements.map) {
1832                that._elements.image.addEventListener("load", onLoad);
1833            }
1834
1835            window.addEventListener("resize", onWindowResize);
1836            ["focus", "click", "load", "transitionend", "animationend", "scroll"].forEach(function(name) {
1837                document.addEventListener(name, that.update);
1838            });
1839
1840            that._elements.image.addEventListener("cmp-image-redraw", that.update);
1841
1842            that._interSectionObserver = new IntersectionObserver(function(entries, interSectionObserver) {
1843                entries.forEach(function(entry) {
1844                    if (entry.intersectionRatio > 0) {
1845                        that.update();
1846                    }
1847                });
1848            });
1849            that._interSectionObserver.observe(that._elements.self);
1850
1851            that.update();
1852        }
1853
1854        function loadImage() {
1855            var hasWidths = (that._properties.widths && that._properties.widths.length > 0) || Object.keys(smartCrops).length > 0;
1856            var replacement;
1857            if (Object.keys(smartCrops).length > 0) {
1858                var optimalWidth = getOptimalWidth(Object.keys(smartCrops), false);
1859                replacement = smartCrops[optimalWidth];
1860            } else {
1861                replacement = hasWidths ? (that._properties.dmimage ? "" : ".") + getOptimalWidth(that._properties.widths, true) : "";
1862            }
1863            if (useAssetDelivery) {
1864                replacement = replacement !== "" ? ("width=" + replacement.substring(1)) : "";
1865            }
1866            var url = that._properties.src.replace(srcUriTemplateWidthVar, replacement);
1867            url = url.replace(SRC_URI_TEMPLATE_DPR_VAR, devicePixelRatio);
1868
1869            var imgSrcAttribute = that._elements.image.getAttribute("src");
1870
1871            if (url !== imgSrcAttribute) {
1872                if (imgSrcAttribute === null || imgSrcAttribute === EMPTY_PIXEL) {
1873                    that._elements.image.setAttribute("src", url);
1874                } else {
1875                    var urlTemplateParts = that._properties.src.split(srcUriTemplateWidthVar);
1876                    // check if image src was dynamically swapped meanwhile (e.g. by Target)
1877                    var isImageRefSame = imgSrcAttribute.startsWith(urlTemplateParts[0]);
1878                    if (isImageRefSame && urlTemplateParts.length > 1) {
1879                        isImageRefSame = imgSrcAttribute.endsWith(urlTemplateParts[urlTemplateParts.length - 1]);
1880                    }
1881                    if (isImageRefSame) {
1882                        that._elements.image.setAttribute("src", url);
1883                        if (!hasWidths) {
1884                            window.removeEventListener("scroll", that.update);
1885                        }
1886                    }
1887                }
1888            }
1889            if (that._lazyLoaderShowing) {
1890                that._elements.image.addEventListener("load", removeLazyLoader);
1891            }
1892            that._interSectionObserver.unobserve(that._elements.self);
1893        }
1894
1895        function getOptimalWidth(widths, useDevicePixelRatio) {
1896            var container = that._elements.self;
1897            var containerWidth = container.clientWidth;
1898            while (containerWidth === 0 && container.parentNode) {
1899                container = container.parentNode;
1900                containerWidth = container.clientWidth;
1901            }
1902
1903            var dpr = useDevicePixelRatio ? devicePixelRatio : 1;
1904            var optimalWidth = containerWidth * dpr;
1905            var len = widths.length;
1906            var key = 0;
1907
1908            while ((key < len - 1) && (widths[key] < optimalWidth)) {
1909                key++;
1910            }
1911
1912            return widths[key].toString();
1913        }
1914
1915        function addLazyLoader() {
1916            var width = that._elements.image.getAttribute("width");
1917            var height = that._elements.image.getAttribute("height");
1918
1919            if (width && height) {
1920                var ratio = (height / width) * 100;
1921                var styles = lazyLoader.style;
1922
1923                styles["padding-bottom"] = ratio + "%";
1924
1925                for (var s in styles) {
1926                    if (Object.prototype.hasOwnProperty.call(styles, s)) {
1927                        that._elements.image.style[s] = styles[s];
1928                    }
1929                }
1930            }
1931            that._elements.image.setAttribute("src", EMPTY_PIXEL);
1932            that._elements.image.classList.add(lazyLoader.cssClass);
1933            that._lazyLoaderShowing = true;
1934        }
1935
1936        function unwrapNoScript() {
1937            var markup = decodeNoscript(that._elements.noscript.textContent.trim());
1938            var parser = new DOMParser();
1939
1940            // temporary document avoids requesting the image before removing its src
1941            var temporaryDocument = parser.parseFromString(markup, "text/html");
1942            var imageElement = temporaryDocument.querySelector(selectors.image);
1943            imageElement.removeAttribute("src");
1944            that._elements.container.insertBefore(imageElement, that._elements.noscript);
1945
1946            var mapElement = temporaryDocument.querySelector(selectors.map);
1947            if (mapElement) {
1948                that._elements.container.insertBefore(mapElement, that._elements.noscript);
1949            }
1950
1951            that._elements.noscript.parentNode.removeChild(that._elements.noscript);
1952            if (that._elements.container.matches(selectors.image)) {
1953                that._elements.image = that._elements.container;
1954            } else {
1955                that._elements.image = that._elements.container.querySelector(selectors.image);
1956            }
1957
1958            that._elements.map = that._elements.container.querySelector(selectors.map);
1959            that._elements.areas = that._elements.container.querySelectorAll(selectors.area);
1960        }
1961
1962        function removeLazyLoader() {
1963            that._elements.image.classList.remove(lazyLoader.cssClass);
1964            for (var property in lazyLoader.style) {
1965                if (Object.prototype.hasOwnProperty.call(lazyLoader.style, property)) {
1966                    that._elements.image.style[property] = "";
1967                }
1968            }
1969            that._elements.image.removeEventListener("load", removeLazyLoader);
1970            that._lazyLoaderShowing = false;
1971        }
1972
1973        function isLazyVisible() {
1974            if (that._elements.container.offsetParent === null) {
1975                return false;
1976            }
1977
1978            var wt = window.pageYOffset;
1979            var wb = wt + document.documentElement.clientHeight;
1980            var et = that._elements.container.getBoundingClientRect().top + wt;
1981            var eb = et + that._elements.container.clientHeight;
1982
1983            return eb >= wt - that._properties.lazythreshold && et <= wb + that._properties.lazythreshold;
1984        }
1985
1986        function resizeAreas() {
1987            if (that._elements.areas && that._elements.areas.length > 0) {
1988                for (var i = 0; i < that._elements.areas.length; i++) {
1989                    var width = that._elements.image.width;
1990                    var height = that._elements.image.height;
1991
1992                    if (width && height) {
1993                        var relcoords = that._elements.areas[i].dataset.cmpRelcoords;
1994                        if (relcoords) {
1995                            var relativeCoordinates = relcoords.split(",");
1996                            var coordinates = new Array(relativeCoordinates.length);
1997
1998                            for (var j = 0; j < coordinates.length; j++) {
1999                                if (j % 2 === 0) {
2000                                    coordinates[j] = parseInt(relativeCoordinates[j] * width);
2001                                } else {
2002                                    coordinates[j] = parseInt(relativeCoordinates[j] * height);
2003                                }
2004                            }
2005
2006                            that._elements.areas[i].coords = coordinates;
2007                        }
2008                    }
2009                }
2010            }
2011        }
2012
2013        function cacheElements(wrapper) {
2014            that._elements = {};
2015            that._elements.self = wrapper;
2016            var hooks = that._elements.self.querySelectorAll("[data-" + NS + "-hook-" + IS + "]");
2017
2018            for (var i = 0; i < hooks.length; i++) {
2019                var hook = hooks[i];
2020                var capitalized = IS;
2021                capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
2022                var key = hook.dataset[NS + "Hook" + capitalized];
2023                that._elements[key] = hook;
2024            }
2025        }
2026
2027        function onWindowResize() {
2028            that.update();
2029            resizeAreas();
2030        }
2031
2032        function onLoad() {
2033            resizeAreas();
2034        }
2035
2036        that.update = function() {
2037            if (that._properties.lazy) {
2038                if (isLazyVisible()) {
2039                    loadImage();
2040                }
2041            } else {
2042                loadImage();
2043            }
2044        };
2045
2046        if (config && config.element) {
2047            init(config);
2048        }
2049    }
2050
2051    function onDocumentReady() {
2052        var elements = document.querySelectorAll(selectors.self);
2053        for (var i = 0; i < elements.length; i++) {
2054            new Image({ element: elements[i], options: CMP.utils.readData(elements[i], IS) });
2055        }
2056
2057        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
2058        var body             = document.querySelector("body");
2059        var observer         = new MutationObserver(function(mutations) {
2060            mutations.forEach(function(mutation) {
2061                // needed for IE
2062                var nodesArray = [].slice.call(mutation.addedNodes);
2063                if (nodesArray.length > 0) {
2064                    nodesArray.forEach(function(addedNode) {
2065                        if (addedNode.querySelectorAll) {
2066                            var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self));
2067                            elementsArray.forEach(function(element) {
2068                                new Image({ element: element, options: CMP.utils.readData(element, IS) });
2069                            });
2070                        }
2071                    });
2072                }
2073            });
2074        });
2075
2076        observer.observe(body, {
2077            subtree: true,
2078            childList: true,
2079            characterData: true
2080        });
2081    }
2082
2083    var documentReady = document.readyState !== "loading" ? Promise.resolve() : new Promise(function(resolve) {
2084        document.addEventListener("DOMContentLoaded", resolve);
2085    });
2086
2087    Promise.all([documentReady]).then(onDocumentReady);
2088    /*
2089        on drag & drop of the component into a parsys, noscript's content will be escaped multiple times by the editor which creates
2090        the DOM for editing; the HTML parser cannot be used here due to the multiple escaping
2091     */
2092    function decodeNoscript(text) {
2093        text = text.replace(/&(amp;)*lt;/g, "<");
2094        text = text.replace(/&(amp;)*gt;/g, ">");
2095        return text;
2096    }
2097
2098})();
2099
2100/*******************************************************************************
2101 * Copyright 2017 Adobe
2102 *
2103 * Licensed under the Apache License, Version 2.0 (the "License");
2104 * you may not use this file except in compliance with the License.
2105 * You may obtain a copy of the License at
2106 *
2107 *     http://www.apache.org/licenses/LICENSE-2.0
2108 *
2109 * Unless required by applicable law or agreed to in writing, software
2110 * distributed under the License is distributed on an "AS IS" BASIS,
2111 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2112 * See the License for the specific language governing permissions and
2113 * limitations under the License.
2114 ******************************************************************************/
2115(function() {
2116    "use strict";
2117
2118    var NS = "cmp";
2119    var IS = "search";
2120
2121    var DELAY = 300; // time before fetching new results when the user is typing a search string
2122    var LOADING_DISPLAY_DELAY = 300; // minimum time during which the loading indicator is displayed
2123    var PARAM_RESULTS_OFFSET = "resultsOffset";
2124
2125    var keyCodes = {
2126        TAB: 9,
2127        ENTER: 13,
2128        ESCAPE: 27,
2129        ARROW_UP: 38,
2130        ARROW_DOWN: 40
2131    };
2132
2133    var selectors = {
2134        self: "[data-" + NS + '-is="' + IS + '"]',
2135        item: {
2136            self: "[data-" + NS + "-hook-" + IS + '="item"]',
2137            title: "[data-" + NS + "-hook-" + IS + '="itemTitle"]',
2138            focused: "." + NS + "-search__item--is-focused"
2139        }
2140    };
2141
2142    var properties = {
2143        /**
2144         * The minimum required length of the search term before results are fetched.
2145         *
2146         * @memberof Search
2147         * @type {Number}
2148         * @default 3
2149         */
2150        minLength: {
2151            "default": 3,
2152            transform: function(value) {
2153                value = parseFloat(value);
2154                return isNaN(value) ? null : value;
2155            }
2156        },
2157        /**
2158         * The maximal number of results fetched by a search request.
2159         *
2160         * @memberof Search
2161         * @type {Number}
2162         * @default 10
2163         */
2164        resultsSize: {
2165            "default": 10,
2166            transform: function(value) {
2167                value = parseFloat(value);
2168                return isNaN(value) ? null : value;
2169            }
2170        }
2171    };
2172
2173    var idCount = 0;
2174
2175    function readData(element) {
2176        var data = element.dataset;
2177        var options = [];
2178        var capitalized = IS;
2179        capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
2180        var reserved = ["is", "hook" + capitalized];
2181
2182        for (var key in data) {
2183            if (Object.prototype.hasOwnProperty.call(data, key)) {
2184                var value = data[key];
2185
2186                if (key.indexOf(NS) === 0) {
2187                    key = key.slice(NS.length);
2188                    key = key.charAt(0).toLowerCase() + key.substring(1);
2189
2190                    if (reserved.indexOf(key) === -1) {
2191                        options[key] = value;
2192                    }
2193                }
2194            }
2195        }
2196
2197        return options;
2198    }
2199
2200    function toggleShow(element, show) {
2201        if (element) {
2202            if (show !== false) {
2203                element.style.display = "block";
2204                element.setAttribute("aria-hidden", false);
2205            } else {
2206                element.style.display = "none";
2207                element.setAttribute("aria-hidden", true);
2208            }
2209        }
2210    }
2211
2212    function serialize(form) {
2213        var query = [];
2214        if (form && form.elements) {
2215            for (var i = 0; i < form.elements.length; i++) {
2216                var node = form.elements[i];
2217                if (!node.disabled && node.name) {
2218                    var param = [node.name, encodeURIComponent(node.value)];
2219                    query.push(param.join("="));
2220                }
2221            }
2222        }
2223        return query.join("&");
2224    }
2225
2226    function mark(node, regex) {
2227        if (!node || !regex) {
2228            return;
2229        }
2230
2231        // text nodes
2232        if (node.nodeType === 3) {
2233            var nodeValue = node.nodeValue;
2234            var match = regex.exec(nodeValue);
2235
2236            if (nodeValue && match) {
2237                var element = document.createElement("mark");
2238                element.className = NS + "-search__item-mark";
2239                element.appendChild(document.createTextNode(match[0]));
2240
2241                var after = node.splitText(match.index);
2242                after.nodeValue = after.nodeValue.substring(match[0].length);
2243                node.parentNode.insertBefore(element, after);
2244            }
2245        } else if (node.hasChildNodes()) {
2246            for (var i = 0; i < node.childNodes.length; i++) {
2247                // recurse
2248                mark(node.childNodes[i], regex);
2249            }
2250        }
2251    }
2252
2253    function Search(config) {
2254        if (config.element) {
2255            // prevents multiple initialization
2256            config.element.removeAttribute("data-" + NS + "-is");
2257        }
2258
2259        this._cacheElements(config.element);
2260        this._setupProperties(config.options);
2261
2262        this._action = this._elements.form.getAttribute("action");
2263        this._resultsOffset = 0;
2264        this._hasMoreResults = true;
2265
2266        this._elements.input.addEventListener("input", this._onInput.bind(this));
2267        this._elements.input.addEventListener("focus", this._onInput.bind(this));
2268        this._elements.input.addEventListener("keydown", this._onKeydown.bind(this));
2269        this._elements.clear.addEventListener("click", this._onClearClick.bind(this));
2270        document.addEventListener("click", this._onDocumentClick.bind(this));
2271        this._elements.results.addEventListener("scroll", this._onScroll.bind(this));
2272
2273        this._makeAccessible();
2274    }
2275
2276    Search.prototype._displayResults = function() {
2277        if (this._elements.input.value.length === 0) {
2278            toggleShow(this._elements.clear, false);
2279            this._cancelResults();
2280        } else if (this._elements.input.value.length < this._properties.minLength) {
2281            toggleShow(this._elements.clear, true);
2282        } else {
2283            this._updateResults();
2284            toggleShow(this._elements.clear, true);
2285        }
2286    };
2287
2288    Search.prototype._onScroll = function(event) {
2289        // fetch new results when the results to be scrolled down are less than the visible results
2290        if (this._elements.results.scrollTop + 2 * this._elements.results.clientHeight >= this._elements.results.scrollHeight) {
2291            this._resultsOffset += this._properties.resultsSize;
2292            this._displayResults();
2293        }
2294    };
2295
2296    Search.prototype._onInput = function(event) {
2297        var self = this;
2298        self._cancelResults();
2299        // start searching when the search term reaches the minimum length
2300        this._timeout = setTimeout(function() {
2301            self._displayResults();
2302        }, DELAY);
2303    };
2304
2305    Search.prototype._onKeydown = function(event) {
2306        var self = this;
2307
2308        switch (event.keyCode) {
2309            case keyCodes.TAB:
2310                if (self._resultsOpen()) {
2311                    toggleShow(self._elements.results, false);
2312                    self._elements.input.setAttribute("aria-expanded", "false");
2313                }
2314                break;
2315            case keyCodes.ENTER:
2316                event.preventDefault();
2317                if (self._resultsOpen()) {
2318                    var focused = self._elements.results.querySelector(selectors.item.focused);
2319                    if (focused) {
2320                        focused.click();
2321                    }
2322                }
2323                break;
2324            case keyCodes.ESCAPE:
2325                self._cancelResults();
2326                break;
2327            case keyCodes.ARROW_UP:
2328                if (self._resultsOpen()) {
2329                    event.preventDefault();
2330                    self._stepResultFocus(true);
2331                }
2332                break;
2333            case keyCodes.ARROW_DOWN:
2334                if (self._resultsOpen()) {
2335                    event.preventDefault();
2336                    self._stepResultFocus();
2337                } else {
2338                    // test the input and if necessary fetch and display the results
2339                    self._onInput();
2340                }
2341                break;
2342            default:
2343                return;
2344        }
2345    };
2346
2347    Search.prototype._onClearClick = function(event) {
2348        event.preventDefault();
2349        this._elements.input.value = "";
2350        toggleShow(this._elements.clear, false);
2351        toggleShow(this._elements.results, false);
2352        this._elements.input.setAttribute("aria-expanded", "false");
2353    };
2354
2355    Search.prototype._onDocumentClick = function(event) {
2356        var inputContainsTarget =  this._elements.input.contains(event.target);
2357        var resultsContainTarget = this._elements.results.contains(event.target);
2358
2359        if (!(inputContainsTarget || resultsContainTarget)) {
2360            toggleShow(this._elements.results, false);
2361            this._elements.input.setAttribute("aria-expanded", "false");
2362        }
2363    };
2364
2365    Search.prototype._resultsOpen = function() {
2366        return this._elements.results.style.display !== "none";
2367    };
2368
2369    Search.prototype._makeAccessible = function() {
2370        var id = NS + "-search-results-" + idCount;
2371        this._elements.input.setAttribute("aria-owns", id);
2372        this._elements.results.id = id;
2373        idCount++;
2374    };
2375
2376    Search.prototype._generateItems = function(data, results) {
2377        var self = this;
2378
2379        data.forEach(function(item) {
2380            var el = document.createElement("span");
2381            el.innerHTML = self._elements.itemTemplate.innerHTML;
2382            el.querySelectorAll(selectors.item.title)[0].appendChild(document.createTextNode(item.title));
2383            el.querySelectorAll(selectors.item.self)[0].setAttribute("href", self._safeHref(item.url));
2384            results.innerHTML += el.innerHTML;
2385        });
2386    };
2387
2388    Search.prototype._safeHref = function(href) {
2389        var a = document.createElement("a");
2390        a.href = href;
2391        return a.pathname;
2392    };
2393
2394    Search.prototype._markResults = function() {
2395        var nodeList = this._elements.results.querySelectorAll(selectors.item.self);
2396        var escapedTerm = this._elements.input.value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
2397        var regex = new RegExp("(" + escapedTerm + ")", "gi");
2398
2399        for (var i = this._resultsOffset - 1; i < nodeList.length; ++i) {
2400            var result = nodeList[i];
2401            mark(result, regex);
2402        }
2403    };
2404
2405    Search.prototype._stepResultFocus = function(reverse) {
2406        var results = this._elements.results.querySelectorAll(selectors.item.self);
2407        var focused = this._elements.results.querySelector(selectors.item.focused);
2408        var newFocused;
2409        var index = Array.prototype.indexOf.call(results, focused);
2410        var focusedCssClass = NS + "-search__item--is-focused";
2411
2412        if (results.length > 0) {
2413
2414            if (!reverse) {
2415                // highlight the next result
2416                if (index < 0) {
2417                    results[0].classList.add(focusedCssClass);
2418                    results[0].setAttribute("aria-selected", "true");
2419                } else if (index + 1 < results.length) {
2420                    results[index].classList.remove(focusedCssClass);
2421                    results[index].setAttribute("aria-selected", "false");
2422                    results[index + 1].classList.add(focusedCssClass);
2423                    results[index + 1].setAttribute("aria-selected", "true");
2424                }
2425
2426                // if the last visible result is partially hidden, scroll up until it's completely visible
2427                newFocused = this._elements.results.querySelector(selectors.item.focused);
2428                if (newFocused) {
2429                    var bottomHiddenHeight = newFocused.offsetTop + newFocused.offsetHeight - this._elements.results.scrollTop - this._elements.results.clientHeight;
2430                    if (bottomHiddenHeight > 0) {
2431                        this._elements.results.scrollTop += bottomHiddenHeight;
2432                    } else {
2433                        this._onScroll();
2434                    }
2435                }
2436
2437            } else {
2438                // highlight the previous result
2439                if (index >= 1) {
2440                    results[index].classList.remove(focusedCssClass);
2441                    results[index].setAttribute("aria-selected", "false");
2442                    results[index - 1].classList.add(focusedCssClass);
2443                    results[index - 1].setAttribute("aria-selected", "true");
2444                }
2445
2446                // if the first visible result is partially hidden, scroll down until it's completely visible
2447                newFocused = this._elements.results.querySelector(selectors.item.focused);
2448                if (newFocused) {
2449                    var topHiddenHeight = this._elements.results.scrollTop - newFocused.offsetTop;
2450                    if (topHiddenHeight > 0) {
2451                        this._elements.results.scrollTop -= topHiddenHeight;
2452                    }
2453                }
2454            }
2455        }
2456    };
2457
2458    Search.prototype._updateResults = function() {
2459        var self = this;
2460        if (self._hasMoreResults) {
2461            var request = new XMLHttpRequest();
2462            var url = self._action + "?" + serialize(self._elements.form) + "&" + PARAM_RESULTS_OFFSET + "=" + self._resultsOffset;
2463
2464            request.open("GET", url, true);
2465            request.onload = function() {
2466                // when the results are loaded: hide the loading indicator and display the search icon after a minimum period
2467                setTimeout(function() {
2468                    toggleShow(self._elements.loadingIndicator, false);
2469                    toggleShow(self._elements.icon, true);
2470                }, LOADING_DISPLAY_DELAY);
2471                if (request.status >= 200 && request.status < 400) {
2472                    // success status
2473                    var data = JSON.parse(request.responseText);
2474                    if (data.length > 0) {
2475                        self._generateItems(data, self._elements.results);
2476                        self._markResults();
2477                        toggleShow(self._elements.results, true);
2478                        self._elements.input.setAttribute("aria-expanded", "true");
2479                    } else {
2480                        self._hasMoreResults = false;
2481                    }
2482                    // the total number of results is not a multiple of the fetched results:
2483                    // -> we reached the end of the query
2484                    if (self._elements.results.querySelectorAll(selectors.item.self).length % self._properties.resultsSize > 0) {
2485                        self._hasMoreResults = false;
2486                    }
2487                } else {
2488                    // error status
2489                }
2490            };
2491            // when the results are loading: display the loading indicator and hide the search icon
2492            toggleShow(self._elements.loadingIndicator, true);
2493            toggleShow(self._elements.icon, false);
2494            request.send();
2495        }
2496    };
2497
2498    Search.prototype._cancelResults = function() {
2499        clearTimeout(this._timeout);
2500        this._elements.results.scrollTop = 0;
2501        this._resultsOffset = 0;
2502        this._hasMoreResults = true;
2503        this._elements.results.innerHTML = "";
2504        this._elements.input.setAttribute("aria-expanded", "false");
2505    };
2506
2507    Search.prototype._cacheElements = function(wrapper) {
2508        this._elements = {};
2509        this._elements.self = wrapper;
2510        var hooks = this._elements.self.querySelectorAll("[data-" + NS + "-hook-" + IS + "]");
2511
2512        for (var i = 0; i < hooks.length; i++) {
2513            var hook = hooks[i];
2514            var capitalized = IS;
2515            capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
2516            var key = hook.dataset[NS + "Hook" + capitalized];
2517            this._elements[key] = hook;
2518        }
2519    };
2520
2521    Search.prototype._setupProperties = function(options) {
2522        this._properties = {};
2523
2524        for (var key in properties) {
2525            if (Object.prototype.hasOwnProperty.call(properties, key)) {
2526                var property = properties[key];
2527                if (options && options[key] != null) {
2528                    if (property && typeof property.transform === "function") {
2529                        this._properties[key] = property.transform(options[key]);
2530                    } else {
2531                        this._properties[key] = options[key];
2532                    }
2533                } else {
2534                    this._properties[key] = properties[key]["default"];
2535                }
2536            }
2537        }
2538    };
2539
2540    function onDocumentReady() {
2541        var elements = document.querySelectorAll(selectors.self);
2542        for (var i = 0; i < elements.length; i++) {
2543            new Search({ element: elements[i], options: readData(elements[i]) });
2544        }
2545
2546        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
2547        var body = document.querySelector("body");
2548        var observer = new MutationObserver(function(mutations) {
2549            mutations.forEach(function(mutation) {
2550                // needed for IE
2551                var nodesArray = [].slice.call(mutation.addedNodes);
2552                if (nodesArray.length > 0) {
2553                    nodesArray.forEach(function(addedNode) {
2554                        if (addedNode.querySelectorAll) {
2555                            var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self));
2556                            elementsArray.forEach(function(element) {
2557                                new Search({ element: element, options: readData(element) });
2558                            });
2559                        }
2560                    });
2561                }
2562            });
2563        });
2564
2565        observer.observe(body, {
2566            subtree: true,
2567            childList: true,
2568            characterData: true
2569        });
2570    }
2571
2572    if (document.readyState !== "loading") {
2573        onDocumentReady();
2574    } else {
2575        document.addEventListener("DOMContentLoaded", onDocumentReady);
2576    }
2577
2578})();
2579
2580/*******************************************************************************
2581 * Copyright 2016 Adobe
2582 *
2583 * Licensed under the Apache License, Version 2.0 (the "License");
2584 * you may not use this file except in compliance with the License.
2585 * You may obtain a copy of the License at
2586 *
2587 *     http://www.apache.org/licenses/LICENSE-2.0
2588 *
2589 * Unless required by applicable law or agreed to in writing, softw
2589are
2590 * distributed under the License is distributed on an "AS IS" BASIS,
2591 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2592 * See the License for the specific language governing permissions and
2593 * limitations under the License.
2594 ******************************************************************************/
2595(function() {
2596    "use strict";
2597
2598    var NS = "cmp";
2599    var IS = "formText";
2600    var IS_DASH = "form-text";
2601    var SELF_SELECTOR = "[data-" + NS + '-is="' + IS + '"]';
2602
2603    var selectors = {
2604        self: SELF_SELECTOR,
2605        validationMessage: SELF_SELECTOR + " .cmp-form-text__validation-message"
2606    };
2607
2608    var displayValidationMessage = false;
2609
2610    var properties = {
2611        /**
2612         * A validation message to display if there is a type mismatch between the user input and expected input.
2613         *
2614         * @type {String}
2615         */
2616        constraintMessage: "",
2617        /**
2618         * A validation message to display if no input is supplied, but input is expected for the field.
2619         *
2620         * @type {String}
2621         */
2622        requiredMessage: ""
2623    };
2624
2625    function readData(element) {
2626        var data = element.dataset;
2627        var options = [];
2628        var capitalized = IS;
2629        capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
2630        var reserved = ["is", "hook" + capitalized];
2631
2632        for (var key in data) {
2633            if (Object.prototype.hasOwnProperty.call(data, key)) {
2634                var value = data[key];
2635
2636                if (key.indexOf(NS) === 0) {
2637                    key = key.slice(NS.length);
2638                    key = key.charAt(0).toLowerCase() + key.substring(1);
2639
2640                    if (reserved.indexOf(key) === -1) {
2641                        options[key] = value;
2642                    }
2643                }
2644            }
2645        }
2646
2647        return options;
2648    }
2649
2650    function FormText(config) {
2651        if (config.element) {
2652            // prevents multiple initialization
2653            config.element.removeAttribute("data-" + NS + "-is");
2654        }
2655
2656        this._cacheElements(config.element);
2657        this._setupProperties(config.options);
2658
2659        this._elements.input.addEventListener("invalid", this._onInvalid.bind(this));
2660        this._elements.input.addEventListener("input", this._onInput.bind(this));
2661        if (displayValidationMessage) {
2662            this._elements.input.checkValidity();
2663        }
2664    }
2665
2666    FormText.prototype._onInvalid = function(event) {
2667        event.target.setCustomValidity("");
2668        if (event.target.validity.typeMismatch) {
2669            if (this._properties.constraintMessage) {
2670                event.target.setCustomValidity(this._properties.constraintMessage);
2671            }
2672        } else if (event.target.validity.valueMissing) {
2673            if (this._properties.requiredMessage) {
2674                event.target.setCustomValidity(this._properties.requiredMessage);
2675            }
2676        }
2677        if (displayValidationMessage) {
2678            var validationMessage = event.target.parentElement.querySelector(".cmp-form-text__validation-message");
2679            if (validationMessage) {
2680                validationMessage.innerText = event.target.validationMessage;
2681            }
2682        }
2683    };
2684
2685    FormText.prototype._onInput = function(event) {
2686        event.target.setCustomValidity("");
2687        if (displayValidationMessage) {
2688            event.target.checkValidity();
2689        }
2690    };
2691
2692    FormText.prototype._cacheElements = function(wrapper) {
2693        this._elements = {};
2694        this._elements.self = wrapper;
2695        var hooks = this._elements.self.querySelectorAll("[data-" + NS + "-hook-" + IS_DASH + "]");
2696        for (var i = 0; i < hooks.length; i++) {
2697            var hook = hooks[i];
2698            var capitalized = IS;
2699            capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
2700            var key = hook.dataset[NS + "Hook" + capitalized];
2701            this._elements[key] = hook;
2702        }
2703    };
2704
2705    FormText.prototype._setupProperties = function(options) {
2706        this._properties = {};
2707
2708        for (var key in properties) {
2709            if (Object.prototype.hasOwnProperty.call(properties, key)) {
2710                var property = properties[key];
2711                if (options && options[key] != null) {
2712                    if (property && typeof property.transform === "function") {
2713                        this._properties[key] = property.transform(options[key]);
2714                    } else {
2715                        this._properties[key] = options[key];
2716                    }
2717                } else {
2718                    this._properties[key] = properties[key]["default"];
2719                }
2720            }
2721        }
2722    };
2723
2724    function onDocumentReady() {
2725        var validationMessages = document.querySelectorAll(selectors.validationMessage);
2726        if (validationMessages && validationMessages.length > 0) {
2727            displayValidationMessage = true;
2728        }
2729
2730        var elements = document.querySelectorAll(selectors.self);
2731        for (var i = 0; i < elements.length; i++) {
2732            new FormText({ element: elements[i], options: readData(elements[i]) });
2733        }
2734
2735        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
2736        var body = document.querySelector("body");
2737        var observer = new MutationObserver(function(mutations) {
2738            mutations.forEach(function(mutation) {
2739                // needed for IE
2740                var nodesArray = [].slice.call(mutation.addedNodes);
2741                if (nodesArray.length > 0) {
2742                    nodesArray.forEach(function(addedNode) {
2743                        if (addedNode.querySelectorAll) {
2744                            var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self));
2745                            elementsArray.forEach(function(element) {
2746                                new FormText({ element: element, options: readData(element) });
2747                            });
2748                        }
2749                    });
2750                }
2751            });
2752        });
2753
2754        observer.observe(body, {
2755            subtree: true,
2756            childList: true,
2757            characterData: true
2758        });
2759    }
2760
2761    if (document.readyState !== "loading") {
2762        onDocumentReady();
2763    } else {
2764        document.addEventListener("DOMContentLoaded", onDocumentReady);
2765    }
2766
2767})();
2768
2769$(function() {
2770    var ua = window.navigator.userAgent;
2771    var trident = ua.indexOf('Trident/');
2772    var msie = ua.indexOf('MSIE ');
2773    if(trident > 0 || msie > 0)
2774    {
2775        $('.cmp-form-text__text[type=date]').datepicker();
2776    }
2777});
2778
2779$(document).ready(function() {
2780   var recaptcha = $(".recaptcha-target");
2781   if (recaptcha.length != 0) {
2782      recaptcha.each(function () {
2783         sitekey = $(this).data('site');
2784         $(this)[0].innerHTML = ' <div id="forms--recaptcha" class="g-recaptcha" data-sitekey='+sitekey+'></div>';
2785      })
2786   }
2787
2788  
2789   $(".formContainerComp  form").submit(function(event) {
2790      var recaptcha = $("#g-recaptcha-response").val();
2791      if (recaptcha === "" && $("#g-recaptcha-response").length > 0) {
2792         event.preventDefault();
2793         if($('.captcha-error').length < 1)
2794             $('.g-recaptcha').prepend("<p class='captcha-error' style='color:red'>This is a required field</p>");
2795      }
2796   });
2797});
2798$(function () {
2799  var $formButton = $('.cmp-form-button');
2800
2801  var performValidation = function () {
2802    $formButton.on('click', function (e) {
2803      $('.input-message--error').remove();
2804      $form = $formButton.closest('form');
2805      var $inputs = $form.find('input');
2806      for (var i = 0; i < $inputs.length; i++) {
2807        $($inputs[i]).removeClass('input--error');
2808        if (
2809          $($inputs[i]).val() === '' &&
2810          ($($inputs[i]).prop('required') || $($inputs[i]).hasClass('required'))
2811        ) {
2812          $($inputs[i]).addClass('input--error');
2813          $($inputs[i]).after(
2814            '<p class="input-message--error">This field is required</p>'
2815          );
2816        }
2817      }
2818    });
2819  };
2820
2821  var isAuthorMode = $('body').hasClass('aem-AuthorLayer-Edit') ||
2822                     $('body').hasClass('aem-AuthorLayer-Design') ||
2823                     $('body').hasClass('aem-AuthorLayer-Layouting') ||
2824                     $('html').hasClass('aem-AuthorLayer-Edit');
2825
2826  var initConditionalVisibility = function () {
2827    if (isAuthorMode) {
2828      return;
2829    }
2830
2831    var $hiddenElements = $('[data-hidden="true"]');
2832
2833    $hiddenElements.each(function () {
2834      var $element = $(this);
2835      var conditionalFieldName = $element.attr('data-conditional-field');
2836      var conditionalValue = $element.attr('data-conditional-value');
2837
2838      if (!conditionalFieldName || !conditionalValue) {
2839        $element.hide();
2840        return;
2841      }
2842
2843      var $form = $element.closest('form');
2844      if (!$form.length) {
2845        $element.hide();
2846        return;
2847      }
2848
2849      var $conditionalInput = $form.find('[name="' + conditionalFieldName + '"]');
2850
2851      if (!$conditionalInput.length) {
2852        $element.hide();
2853        return;
2854      }
2855
2856      var handleVisibilityCheck = function () {
2857        var currentValue = '';
2858        var inputType = $conditionalInput.first().attr('type');
2859
2860        if (inputType === 'radio') {
2861          currentValue = $form.find('[name="' + conditionalFieldName + '"]:checked').val() || '';
2862        } else if (inputType === 'checkbox') {
2863          var checkedValues = [];
2864          $form.find('[name="' + conditionalFieldName + '"]:checked').each(function () {
2865            checkedValues.push($(this).val());
2866          });
2867          currentValue = checkedValues.join(', ');
2868        } else {
2869          currentValue = $conditionalInput.first().val() || '';
2870        }
2871
2872        if (currentValue === conditionalValue) {
2873          $element.show();
2874        } else {
2875          $element.hide();
2876        }
2877      };
2878
2879      $conditionalInput.on('change input', handleVisibilityCheck);
2880      handleVisibilityCheck();
2881    });
2882  };
2883
2884  var logSalesforceErrors = function () {
2885    var params = new URLSearchParams(window.location.search);
2886    var sfStep = params.get('sfStep');
2887    if (!sfStep) {
2888      return;
2889    }
2890
2891    var sfStatus = params.get('sfStatus') || 'N/A';
2892    var sfError = params.get('sfError') || 'No response body';
2893
2894    console.error(
2895      '[Salesforce Error] Step: ' + sfStep +
2896      ' | Status: ' + sfStatus +
2897      ' | Response: ' + sfError
2898    );
2899
2900    if (window.history && window.history.replaceState) {
2901      var cleanUrl = window.location.pathname + window.location.hash;
2902      window.history.replaceState(null, '', cleanUrl);
2903    }
2904  };
2905
2906  performValidation();
2907  initConditionalVisibility();
2908  logSalesforceErrors();
2909});
2910
2911$(function() {
2912    var unavailableDates = [];
2913    var hideWeekend = false;
2914    function unavailable(date) {
2915
2916
2917        var noWeekend = $.datepicker.noWeekends(date);
2918        var year = "" + date.getYear();
2919        var month = "" + (date.getMonth() + 1);
2920        month = month.length == 1 ? "0"+month : month;
2921        var date = "" + date.getDate();
2922        date = date.length == 1 ? "0"+date : date;
2923        year = year.substring(1);
2924
2925        dmy = month + "-" +date + "-" + year;
2926
2927        if(hideWeekend && !noWeekend[0])
2928        {
2929            return [false, "", "Unavailable"];
2930        }
2931        else if ($.inArray(dmy, unavailableDates) == -1) {
2932                    return [true, ""];
2933        }
2934        else {
2935            return [false, "", "Unavailable"];
2936        }
2937    }
2938
2939    $('.form--calendar').each(function(){
2940        var date = $(this).data('date');
2941        unavailableDates = date ? date.split(',') : '';
2942        hideWeekend = $(this).data('weekends');
2943
2944        $(this).datepicker({
2945            dateFormat: 'mm-dd-yy',
2946            beforeShowDay: unavailable
2947        });
2948
2949        if($(this).data('required') === 'required')
2950        {
2951            $(this).addClass('required');
2952        }
2953    });
2954
2955});
2956
2957(function() {
2958	$(".video-collection__carousel").each(function(index, element){
2959		var $this = $(this);
2960		var swiper = new Swiper($this, {
2961			speed: 400,
2962			spaceBetween: 20,
2963			slidesPerView: 4,		
2964			grabCursor: true,
2965			navigation: {
2966				nextEl: $this.siblings('.swiper-navigation').children(".swiper-button-next"),
2967				prevEl: $this.siblings('.swiper-navigation').children(".swiper-button-prev"),
2968			},		
2969			pagination: {
2970				el: $this.siblings('.swiper-navigation').children(".swiper-pagination"),
2971				type: 'fraction'
2972			},		
2973			breakpoints: {
2974				// when window width is <= 991px
2975				991: {
2976					slidesPerView: 3, // or 'auto'
2977				},
2978	
2979				767: {
2980					slidesPerView: 1,
2981					slidesPerColumn: 4,
2982				},
2983			},
2984			on: {
2985				init: function () {
2986					/*
2987						In order to maintain a 4 ratio per column on mobile
2988						we manually add empty slides until the total amount of slides
2989						is divisible by 4. This will ensure that each column will keep 4
2990						slides and the last column will have those fake slides but they are empty
2991					*/
2992					if ($(window).width() < 768) {
2993						var slidesCount = $this.find('.video__item').length;
2994						while (slidesCount % 4 !== 0) {
2995							this.appendSlide(['<div class="swiper-slide"></div>'])
2996							slidesCount++;
2997							if(slidesCount % 4 === 0) {
2998								this.updateSlides();
2999								break;
3000							}
3001						}					
3002					}//end if
3003				},
3004			},			
3005		});
3006	});
3007})();
3008$(function () {
3009
3010  var categoryOption = '',
3011    subcategoryOption = '',
3012    currentScreenSize = 'mobile';
3013
3014
3015  // initial check of screensize
3016  if (window.innerWidth >= 992) {
3017    currentScreenSize = 'desktop';
3018  }
3019
3020  var mq = window.matchMedia('(min-width: 992px)');
3021  mq.addEventListener("change", WidthChange);
3022  WidthChange(mq);
3023
3024  // media query change
3025  function WidthChange(mq) {
3026    $('.tips__filter.category select').each(function (ev, obj) {
3027      var $el = $(this),
3028        option = $(this)
3029        .find('option:selected')
3030        .data('option'),
3031        subCategory= $(this)
3032                .closest('.tips__filters')
3033                .find('.subcategory.active li[data-active]')
3034                .data('option');
3035      if(option && option != 'All Categories')
3036      {
3037       option = option.replace(/:|\//gi, function () {
3038                        return '-';
3039                      })
3040                      .toLowerCase();
3041      }
3042      if(subCategory)
3043     {
3044        subCategory = subCategory.replace(/:|\//gi, function () {
3045                        return '-';
3046                    })
3047                    .toLowerCase();
3048     }
3049
3050      if (mq.matches) {
3051        // window width is at least 992px
3052        currentScreenSize = 'desktop';
3053        filterRebateResults($el, option, subCategory);
3054      } else {
3055        // window width is less than 992px
3056        currentScreenSize = 'mobile';
3057        filterRebateResults($el, option, subCategory);
3058      }
3059    });
3060  }
3061
3062  $('.tips__filter.category select').bind({
3063    changed: function (ev, obj) {
3064
3065      var $el = $(this),
3066        option = obj.dropdownOption.data('option');
3067      filterRebateResults($el, option);
3068      showSubcategorySelect($el, option);
3069    }
3070  });
3071  $('.tips__filter.subcategory select').bind({
3072    changed: function (ev, obj) {
3073      var $el = $(this),
3074        option = obj.dropdownOption.data('option');
3075      filterRebateResults($el, categoryOption, option);
3076    }
3077  });
3078  $('.tips__filter select').selectBoxIt();
3079
3080  function filterRebateResults($el, category, subcategory) {
3081    // track category, subcategory
3082    categoryOption = category;
3083    subcategoryOption = subcategory;
3084
3085    var tipsComponent = $el.closest('.tipsgrid');
3086
3087    // filter grid by category, subcategory
3088    var $gridItems = tipsComponent.find('.tips__col');
3089    var itemsToRemove = [];
3090
3091    $gridItems.each(function (i) {
3092      $(this).show();
3093      var tags = $(this).attr('data-tags');
3094      if (tags) {
3095        tags = tags.toLowerCase().split(';');
3096      }
3097
3098      if (categoryOption != 'All Categories') {
3099        if (categoryOption != '' && categoryOption != undefined) {
3100          categoryOption = category
3101              .replace(/:|\//gi, function () {
3102                return '-';
3103              })
3104              .toLowerCase();
3105
3106          var subcategoryOptionMatches = tags.filter(function(s) { return s.includes(categoryOption) });
3107          if ((tags && tags.indexOf(categoryOption) < 0) && subcategoryOptionMatches.length <= 0) {
3108            $(this).hide();
3109            itemsToRemove.push($(this));
3110          } else {
3111            $(this).show();
3112          }
3113        }
3114
3115        if (subcategoryOption != '' && subcategoryOption != undefined  && subcategoryOption != 'All Categories') {
3116          subcategoryOption = subcategory
3117              .replace(/:|\//gi, function (x) {
3118                return '-';
3119              })
3120              .toLowerCase();
3121          if (tags && tags.indexOf(subcategoryOption) < 0) {
3122            $(this).hide();
3123            if (itemsToRemove.indexOf(i) < 0) {
3124              itemsToRemove.push($(this));
3125            }
3126          } else {
3127             if (tags && tags.indexOf(categoryOption) > -1) {
3128                $(this).show();
3129             }
3130          }
3131        }
3132      }
3133
3134    });
3135
3136    // if slider, don't pagninate
3137    $slider = tipsComponent.find('.pagination');
3138    if ($slider) {
3139      // remove items from array before initializing pagination
3140      for (var i = 0; i < itemsToRemove.length; i++) {
3141        for (var j = 0; j < $gridItems.length; j++) {
3142          if ($($gridItems[j]).is(itemsToRemove[i])) {
3143            $gridItems.splice(j, 1);
3144            j--;
3145          }
3146        }
3147      }
3148
3149      // handle pagination
3150      var $pagination = tipsComponent.find('.tips__pagination');
3151      var $visibleItems = tipsComponent.find('.tips__pages');
3152      var rowsDesktop = tipsComponent
3153        .find('[data-rows-desktop]')
3154        .attr('data-rows-desktop');
3155      var rowsMobile = tipsComponent
3156        .find('[data-rows-mobile]')
3157        .attr('data-rows-mobile');
3158      if (currentScreenSize == 'desktop') {
3159        initPagination($pagination, $gridItems, 3 * rowsDesktop, $visibleItems);
3160      } else {
3161        initPagination($pagination, $gridItems, 2 * rowsMobile, $visibleItems);
3162      }
3163    }
3164
3165  }
3166
3167
3168  function showSubcategorySelect($el, category) {
3169    var tipsComponent = $el.closest('.tipsgrid ');
3170
3171    tipsComponent
3172      .find('.tips__filter.subcategory.active')
3173      .removeClass('active');
3174    if (category != '') {
3175      var subcategoryFilter = tipsComponent.find(
3176        '.tips__filter.subcategory[data-category="' + category + '"]'
3177      );
3178      subcategoryFilter.addClass('active');
3179      var categorySelect = tipsComponent
3180        .find('.tips__filter.category')
3181        .find('select')
3182        .data('selectBox-selectBoxIt');
3183      var subcategorySelect = subcategoryFilter
3184        .find('select')
3185        .data('selectBox-selectBoxIt');
3186      if (subcategorySelect) {
3187        var firstOption = subcategorySelect.selectOption(0);
3188      }
3189    }
3190  }
3191});
3192$(function(){
3193    var alphabet = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"];
3194
3195    setup();
3196    function setup()
3197    {
3198        resize();
3199
3200        $('.image-tab__container').each(function(){
3201                 var startingIndex = $(this).data('intro') ? 0 : 1;
3202                 findAndSetContentTab($(this), startingIndex);
3203        });
3204
3205    }
3206
3207    function resize() {
3208        $('.image-tab').each(function(){
3209             var $pointsContainer = $('.image-tab__points')
3210            $(this).find('.image-tab__points').width( $(this).find('.image-tab--image').width()).height( $(this).find('.image-tab--image').height())
3211
3212        });
3213        $('.image-tab__point--container').each(function(){
3214            $(this).css('top',$(this).data('top')+'%');
3215            $(this).css('left',$(this).data('left')+'%');
3216            $(this).css('height',$(this).data('height') +'%');
3217            $(this).css('width',$(this).data('width') +'%');
3218            if($(this).data('pointname') === 'alphabet')
3219            {
3220                $(this).find('.image-tab__point').text(alphabet[$(this).data('index') - 1]);
3221            }
3222            //$(this).closest('.image-tab').find('.image-tab--content[data-index=1]').show();
3223        });
3224    }
3225
3226    var resizeTimeout;
3227    $(window).on('resize', function(){
3228        clearTimeout(resizeTimeout)
3229        resizeTimeout = setTimeout(function(){
3230            resize();
3231        },500)
3232    });
3233
3234
3235    $('.image-tab__point--container').click(function(){
3236
3237        findAndSetContentTab($(this), $(this).data('index'));
3238        $(window).trigger('resize')
3239
3240    })
3241
3242    function findAndSetContentTab($object, index)
3243    {
3244        var $imageTab = $object.closest('.image-tab');
3245        $imageTab.find('.image-tab__point--container').removeClass('active');
3246        $imageTab.find('.image-tab__point--container[data-index='+index+']').addClass('active');
3247        if(!$imageTab.find('.image-tab__point--container[data-index='+index+']').hasClass('link'))
3248        {
3249            $imageTab.find('.image-tab--content').hide();
3250            $imageTab.find('.image-tab--content').removeClass('active');
3251            $imageTab.find('.image-tab--content[data-index='+index+']').show();
3252            $imageTab.find('.image-tab--content[data-index='+index+']')
3253        }
3254    }
3255
3256
3257});
3258$(function () {
3259	$('document').ready(function(){
3260
3261		var fbUrl = 'https://www.facebook.com/sharer.php?u=' + window.location.href;
3262        $('.social--icon--facebook').attr("href",fbUrl );
3263
3264        var twitterUrl = 'https://twitter.com/intent/tweet?url=' + window.location.href;
3265        $('.social--icon--twitter').attr("href",twitterUrl );
3266
3267        var linkedInUrl = 'https://www.linkedin.com/sharing/share-offsite/?url=' + window.location.href;
3268        $('.social--icon--linked-in').attr("href",linkedInUrl );
3269
3270 		var mailToUrl = $('.social--icon--email').prop('href');
3271		var finalMailToUrl = mailToUrl + window.location.href;
3272
3273		 $('.social--icon--email').attr("href",finalMailToUrl );
3274
3275
3276    })
3277});
3278$(function () {
3279  $('#inputPassword').keyup(function (event) {
3280    if (event.keyCode == 13) {
3281      $('#loginButton').click();
3282    }
3283  });
3284
3285  $('#loginButton').click(function (e) {
3286    e.preventDefault();
3287
3288    if ($('#rememberMe').is(':checked')) {
3289      user = $('#inputPassword').val();
3290      if (user != '' && user != null) {
3291        setCookie('usr_c', user, 30);
3292      }
3293    }
3294
3295    var valid = validateForm();
3296    if (valid) {
3297      $.ajax({
3298        type: 'POST',
3299        url: $('#url').val(),
3300        data: {
3301          j_username: $('#inputPassword').val(),
3302          j_password: $('#inputPassword').val(),
3303          j_validate: 'true',
3304        },
3305        success: function (data, textStatus, jqXHR) {
3306          window.location.href = getRedirectPath() + '?t=' + Date.now();
3307        },
3308        error: function (XMLHttpRequest, textStatus, errorThrown) {
3309          $('#errordiv').val('Invalid User Name or Password');
3310        },
3311      });
3312    } else {
3313      $('#errordiv').val('Invalid User Name or Password');
3314    }
3315  });
3316});
3317
3318//Function to redirect to homepage after login
3319var getRedirectPath = function () {
3320  return $('#redirectPath').attr('href');
3321};
3322
3323// Function for setting cookies at login page
3324var setCookie = function (cname, cvalue, exdays) {
3325  var d = new Date();
3326  d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000);
3327  var expires = 'expires=' + d.toGMTString();
3328  document.cookie = cname + '=' + cvalue + ';' + expires + ';path=/';
3329};
3330
3331// Function for getting cookies at login page
3332var getCookie = function (cname) {
3333  var name = cname + '=';
3334  var decodedCookie = decodeURIComponent(document.cookie);
3335  var ca = decodedCookie.split(';');
3336  for (var i = 0; i < ca.length; i++) {
3337    var c = ca[i];
3338    while (c.charAt(0) == ' ') {
3339      c = c.substring(1);
3340    }
3341    if (c.indexOf(name) == 0) {
3342      return c.substring(name.length, c.length);
3343    }
3344  }
3345  return '';
3346};
3347
3348// Function for validating Login form
3349var validateForm = function () {
3350  return $('#inputPassword').val().length > 0;
3351};
3352
3353$(function(){
3354
3355    function setupPopper(targetID, popoverID)
3356    {
3357                var popcorn = document.querySelector('#'+targetID);
3358                var tooltip = document.querySelector('#'+popoverID);
3359                Popper.createPopper(popcorn, tooltip, {
3360                      placement: 'bottom',
3361                      modifiers: [
3362                        {
3363                          name: 'offset',
3364                          options: {
3365
3366                            offset: [0, 10],
3367                          },
3368                        },
3369                        { name: 'flip', enabled: false }
3370                      ],
3371                    });
3372    }
3373
3374    function setupInputFields()
3375    {
3376
3377        $(".ROICalculator__input--average-usage").on('focusout', function(evt){
3378
3379           if (evt.which != 110 && $(this).val() ){//not a fullstop
3380                var unit = $('.ROICalculator__unit').val()
3381                var min = 10000;
3382                var max = 500000;
3383                if(unit == 'cubic ft')
3384                {
3385                    min = Math.round(min / 7.48);
3386                    max = Math.round(max / 7.48);
3387                }
3388                else if(unit == 'cubic meters')
3389                {
3390                    min = Math.round(min / 264.172);
3391                    max = Math.round(max / 264.172);
3392                }
3393                var n = parseFloat($(this).val().replace(',', ''));
3394                $(this).removeClass('input--error')
3395                if($(this).next().hasClass("input-message--error"))
3396                {
3397                    $(this).next().remove();
3398                }
3399                if(!isNaN(n) && n <= max && n >= min)
3400                {
3401                    $(this).val(n.toLocaleString());
3402                }
3403                else {
3404                     var errorMessage = isNaN(n) ? 'A number is required.' :'Range: '+min.toLocaleString()+'-'+max.toLocaleString()+' '+unit+' allowed';
3405                    $(this).addClass('input--error')
3406                    $(this).after('<p class="input-message--error">'+errorMessage+'</p>');
3407                }
3408           }
3409        });
3410
3411        //jQuery
3412        $(".ROICalculator__number:not(.ROICalculator__input--average-usage)").on('focusout', function(evt){
3413
3414           if (evt.which != 110 && $(this).val() ){//not a fullstop
3415                var min = $(this).data("min")
3416                var max = $(this).data("max")
3417                var n = parseFloat($(this).val().replace(',', ''));
3418                $(this).removeClass('input--error')
3419                if($(this).next().hasClass("input-message--error"))
3420                {
3421                    $(this).next().remove();
3422                }
3423                if(!isNaN(n) && n <= max && n >= min)
3424                {
3425                    $(this).val(n.toLocaleString());
3426                }
3427                else {
3428                     var errorMessage = isNaN(n) ? 'A number is required.' :'Range: '+min.toLocaleString()+'-'+max.toLocaleString()+' allowed';
3429                    $(this).addClass('input--error')
3430                    $(this).after('<p class="input-message--error">'+errorMessage+'</p>');
3431                }
3432           }
3433        });
3434
3435         $(".ROICalculator__percent").on('focusout', function(evt){
3436                var min = $(this).data("min")
3437                var max = $(this).data("max")
3438                if($(this).val())
3439                {
3440                    var n = parseFloat($(this).val().replace(',', '').replace('%', ''));
3441                    $(this).removeClass('input--error')
3442                    if($(this).next().hasClass("input-message--error"))
3443                    {
3444                        $(this).next().remove();
3445                    }
3446                    if(!isNaN(n) &&  n <= max && n >= min)
3447                    {
3448                        $(this).val(n.toLocaleString() + '%');
3449                    }
3450                    else {
3451                         var errorMessage = isNaN(n) ? 'A number is required.' : 'Range: '+min.toLocaleString()+'-'+max.toLocaleString()+'% allowed'
3452                        $(this).addClass('input--error')
3453                        $(this).after('<p class="input-message--error">'+errorMessage+'</p>');
3454                     }
3455                }
3456         });
3457    }
3458
3459    function formatTableData(dataItem)
3460    {
3461        return "<td>"  + dataItem + "</td>"
3462    }
3463
3464    function formatTableDataInfo(dataItemName, dataItemValue, message)
3465    {
3466            var html = "<div>";
3467            html += message ? '<span data-popover="'+message+'">' : '';
3468            html += "<p class='ROICalculator__table-info--title'>" + dataItemName + "</p>";
3469            html += message ? '</span>' : ''
3470            html += "<p class='ROICalculator__table-info--content'>" + dataItemValue + "</p>";
3471            html += "</div>"
3472            return html;
3473    }
3474
3475    function formatSavingsDataInfo(dataItemName, dataItemValue, message)
3476    {
3477            var html = "<div>";
3478
3479            html += "<p class='ROICalculator__savings--title'>" + dataItemName
3480            html += '<span class="popover--center" data-popover="'+message+'">'
3481            html += '<a class="ROICalculator__popover"></a>'
3482            html += '</span>'
3483            html +=  "</p>";
3484            html += "<p class='ROICalculator__savings--content'>" + dataItemValue + "</p>";
3485            html += "</div>"
3486            return html;
3487
3488    }
3489
3490    function numberWithCommas(x, noDecimals) {
3491        if(noDecimals)
3492        {
3493            return parseInt(x).toLocaleString();
3494        }
3495        return x.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
3496    }
3497
3498    function formatTableMobile(data)
3499    {
3500            $( ".ROICalculator__table tbody" ).empty();
3501            data.forEach(function(dataItem){
3502                var html = "<tr class='ROICalculator__table-info--header'>";
3503                html += formatTableData(dataItem["year"])
3504                html += formatTableData('$'+numberWithCommas(dataItem["totalActualCost"],true))
3505                html += formatTableData('$'+numberWithCommas(dataItem["totalBillableCost"],true))
3506                html += formatTableData('$'+numberWithCommas(dataItem["additionalRevenue"],true))
3507                html += "<td class='ROICalculator__table-arrow--container'><div class='ROICalculator__table-arrow'></div></td>";
3508                html += "</tr>"
3509                html += "<tr class='ROICalculator__table-info'><td><div class='ROICalculator__table-info--empty-column'></div></td><td colspan='4'>"
3510                html += "<div style='display:none;'  class='ROICalculator__table-info--container'>"
3511                html += formatTableDataInfo("Usage", dataItem["usage"].toLocaleString(),"Based on Average Usage per year (gal/yr)")
3512                html += formatTableDataInfo("100% <br> Charge", '$'+numberWithCommas(dataItem["hundredPercentCharge"]), "Based on measurement of 100% of water usage and the total cost of water")
3513                html += formatTableDataInfo("Actual <br> Charge", '$'+numberWithCommas(dataItem["actualCharge"]),"Based on measurement of reduced of water measurement with PD meter and the total cost of water")
3514                html += "</div>"
3515                html += "<div style='display:none;'  class='ROICalculator__table-info--container'>"
3516                if(dataItem["accuracyLost"] == 0)
3517                {
3518                    html += formatTableDataInfo("Accuracy <br> Loss", "N/A", "PD meter water usage (reduced due to mechanical wear of PD meter)")
3519                }
3520                else {
3521                    html += formatTableDataInfo("Accuracy <br> Loss", numberWithCommas(dataItem["accuracyLost"], true), "PD meter water usage (reduced due to mechanical wear of PD meter)")
3522                }
3523
3524                html += formatTableDataInfo("Cost <br> Sewer", '$'+numberWithCommas(dataItem["costSewer"]),"Sewer Rate (based on 1000 gallons)")
3525                html += formatTableDataInfo("Cost <br> Water", '$'+numberWithCommas(dataItem["costWater"]), "Water Rate (based on 1000 gals)")
3526                html += "</div>"
3527                html += "<div style='display:none;'  class='ROICalculator__table-info--container'>"
3528                html += formatTableDataInfo("Total <br>  Cost", '$'+numberWithCommas(dataItem["totalCost"]),"Water rate plus sewer rate (based on 1000 gallons)")
3529                html += formatTableDataInfo("Difference", '$'+numberWithCommas(dataItem["difference"]), "Difference between 100% charge and Actual charge")
3530                html += formatTableDataInfo("Low Flow <br> Accuracy", numberWithCommas(dataItem["lowFlowAccuracy"], true), "Additional gallons measured due to iPERL's low flow measurement capabilities")
3531                html += "</div>"
3532                html += "<div style='display:none;'  class='ROICalculator__table-info--container'>"
3533                html += formatTableDataInfo("Added <br> Revenue", '$'+numberWithCommas(dataItem["addedRevenue"]), "Additional per meter revenue as a result of the increased low flow measurement with iPERL multiplied by the total cost of water")
3534                html += formatTableDataInfo("Total Added <br> Revenue", '$'+numberWithCommas(dataItem["totalAddedRevenue"]),"Total added per meter revenue due to iPERL overall accuracy plus added low flow revenue")
3535                html += formatTableDataInfo("", '')
3536                html += "</div>"
3537                html += "</td></tr>"
3538                html += "</tr>"
3539                $('.ROICalculator__table-body').append(html);
3540            })
3541
3542            $('.ROICalculator__savings-container-info').append(formatSavingsDataInfo("Difference", '$'+numberWithCommas(data[19]["totalDifference"]),"Difference between 100% charge and Actual charge"));
3543            $('.ROICalculator__savings-container-info').append(formatSavingsDataInfo("Added Revenue",'$'+ numberWithCommas(data[19]["sumAddedRevenue"]),"Additional per meter revenue as a result of the increased low flow measurement with iPERL multiplied by the total cost of water"));
3544            $('.ROICalculator__savings-container-info').append(formatSavingsDataInfo("Total Added Revenue",'$'+ numberWithCommas(data[19]["sumTotalAddedRevenue"]), "Total added per meter revenue due to iPERL overall accuracy plus added low flow revenue"));
3545            $('.ROICalculator__savings-container-info').append(formatSavingsDataInfo("Total Actual $", '$'+numberWithCommas(data[19]["sumTotalActualAmount"], true),"100% Charge field plus Added Revenue field due to iPERL's low flow capabilities multiplied by the total number of meters (Cost Sewer)"));
3546            $('.ROICalculator__savings-container-info').append(formatSavingsDataInfo("Total Billable $", '$'+numberWithCommas(data[19]["sumTotalBillableAmou
3546nt"], true),"Actual billed amount: Actual Charge multiplied by the total number of meters (Cost Sewer)"));
3547            $('.ROICalculator__savings-container-info').append(formatSavingsDataInfo("Total Additional Revenue", '$'+numberWithCommas(data[19]["totalAdditionalRevenue"], true),"Additional revenue per year as a result of iPERL's perpetual accuracy and low flow measurement capabilities"));
3548
3549    }
3550
3551
3552    function formatTableDesktop(data)
3553    {
3554        $( ".ROICalculator__table tbody" ).empty();
3555        data.forEach(function(dataItem){
3556            var html = "<tr class='ROICalculator__table-info--header'>";
3557            html += formatTableData(dataItem["year"])
3558            html += formatTableData(dataItem["usage"].toLocaleString())
3559            html += formatTableData('$'+numberWithCommas(dataItem["hundredPercentCharge"]))
3560            html += formatTableData('$'+numberWithCommas(dataItem["actualCharge"]))
3561            html += formatTableData('$'+numberWithCommas(dataItem["totalActualCost"], true))
3562            html += formatTableData('$'+numberWithCommas(dataItem["totalBillableCost"], true))
3563            html += formatTableData('$'+numberWithCommas(dataItem["additionalRevenue"], true))
3564            html += "<td class='ROICalculator__table-arrow--container'><div class='ROICalculator__table-arrow'></div></td>";
3565            html += "<tr class='ROICalculator__table-info'><td class='ROICalculator--fold'><div class='ROICalculator__table-info--empty-column'></div></td><td class='ROICalculator--fold' colspan='8'><div style='display:none;'  class='ROICalculator__table-info--container'>"
3566            if(dataItem["accuracyLost"] == 0)
3567            {
3568                html += formatTableDataInfo("Accuracy <br> Loss", "N/A", "PD meter water usage (reduced due to mechanical wear of PD meter)")
3569            }
3570            else {
3571                html += formatTableDataInfo("Accuracy <br> Loss", numberWithCommas(dataItem["accuracyLost"], true), "PD meter water usage (reduced due to mechanical wear of PD meter)")
3572            }
3573            html += formatTableDataInfo("Cost Sewer", '$'+numberWithCommas(dataItem["costSewer"]),"Sewer Rate (based on 1000 gallons)")
3574            html += formatTableDataInfo("Cost Water", '$'+numberWithCommas(dataItem["costWater"]),"Water Rate (based on 1000 gals)")
3575            html += formatTableDataInfo("Total Cost", '$'+numberWithCommas(dataItem["totalCost"]),"Water rate plus sewer rate (based on 1000 gallons)")
3576            html += formatTableDataInfo("Difference", '$'+numberWithCommas(dataItem["difference"]), "Difference between 100% charge and Actual charge")
3577            html += formatTableDataInfo("Low Flow Accuracy", numberWithCommas(dataItem["lowFlowAccuracy"], true), "Additional gallons measured due to iPERL's low flow measurement capabilities")
3578            html += formatTableDataInfo("Added Revenue", '$'+numberWithCommas(dataItem["addedRevenue"]), "Additional per meter revenue as a result of the increased low flow measurement with iPERL multiplied by the total cost of water")
3579            html += formatTableDataInfo("Total Added Revenue", '$'+numberWithCommas(dataItem["totalAddedRevenue"], false), "Total added per meter revenue due to iPERL overall accuracy plus added low flow revenue")
3580            html += "</div></td></tr>"
3581            html += "</tr>"
3582            $('.ROICalculator__table-body').append(html);
3583        });
3584
3585        $('.ROICalculator__savings-container-info').append(formatSavingsDataInfo("Difference", '$'+numberWithCommas(data[19]["totalDifference"]),"Difference between 100% charge and Actual charge"));
3586        $('.ROICalculator__savings-container-info').append(formatSavingsDataInfo("Added Revenue",'$'+ numberWithCommas(data[19]["sumAddedRevenue"]),"Additional per meter revenue as a result of the increased low flow measurement with iPERL multiplied by the total cost of water"));
3587        $('.ROICalculator__savings-container-info').append(formatSavingsDataInfo("Total Added Revenue",'$'+ numberWithCommas(data[19]["sumTotalAddedRevenue"]), "Total added per meter revenue due to iPERL overall accuracy plus added low flow revenue"));
3588        $('.ROICalculator__savings-container-info').append(formatSavingsDataInfo("Total Actual $", '$'+numberWithCommas(data[19]["sumTotalActualAmount"], true),"100% Charge field plus Added Revenue field due to iPERL's low flow capabilities multiplied by the total number of meters (Cost Sewer)"));
3589        $('.ROICalculator__savings-container-info').append(formatSavingsDataInfo("Total Billable $", '$'+numberWithCommas(data[19]["sumTotalBillableAmou
3589nt"], true),"Actual billed amount: Actual Charge multiplied by the total number of meters (Cost Sewer)"));
3590        $('.ROICalculator__savings-container-info').append(formatSavingsDataInfo("Total Additional Revenue", '$'+numberWithCommas(data[19]["totalAdditionalRevenue"], true),"Additional revenue per year as a result of iPERL's perpetual accuracy and low flow measurement capabilities"));
3591
3592    };
3593
3594    function reset()
3595    {
3596        $('.input-message--error').remove();
3597        $('.ROICalculator__input:not(:disabled)').each(function(){
3598            $(this).val("")
3599            $(this).removeClass('input--error')
3600        })
3601        $('.ROICalculator__results').hide();
3602        $('.ROICalculator__container').removeClass('ROICalculator__container--results')
3603        if (window.innerWidth <= 992) {
3604            $('.ROICalculator__container form').show();
3605            $('.ROICalculator__description').show()
3606        }
3607
3608    }
3609
3610   $(document).ready(function(){
3611          setupInputFields()
3612          //Setup
3613          if($('.ROICalculator__container').length > 0)
3614          {
3615              setupPopper('quantityResidentialMeters__button', 'quantityResidentialMeters__message');
3616              setupPopper('averageUsage__button', 'averageUsage__message');
3617              setupPopper('twentyYearAccuracyLost__button', 'twentyYearAccuracyLost__message');
3618              setupPopper('lowFlowAccuracyIncrease__button', 'lowFlowAccuracyIncrease__message');
3619              setupPopper('totalAnticipatedLowFlowMeasurement__button', 'totalAnticipatedLowFlowMeasurement__message');
3620              setupPopper('increasePerYear__button', 'increasePerYear__message');
3621              setupPopper('waterRate__button', 'waterRate__message');
3622              setupPopper('sewerRate__button', 'sewerRate__message');
3623          }
3624
3625          $('.btn__reset').click(function(){
3626               reset();
3627          });
3628
3629          $('.ROICalculator__back-arrow').click(function(){
3630               $('.roicalculator').removeClass('roicalculator--fixed');
3631                $('.ROICalculator__results').hide();
3632                $('.ROICalculator__container form').show();
3633                $('.ROICalculator__description').show()
3634                location.hash = ''
3635                location.hash = 'ROICalculator'
3636
3637          });
3638          $('.ROICalculator__submit').click(function(e){
3639                e.preventDefault()
3640
3641                $(this).removeClass('input--error')
3642                $('.ROICalculator__results').hide();
3643                var map = {};
3644                var failed = false;
3645                $(".ROICalculator__container input").each(function() {
3646                    if($(this).next().hasClass('.input-message--error'))
3647                    {
3648                        $(this).next().remove();
3649                    }
3650                    $(this).removeClass('.input-message--error');
3651                    var val = $(this).val().replace(',', '');
3652                    if(val && !$(this).hasClass('input--error'))
3653                    {
3654                         $(this).removeClass('input--error')
3655                        if($(this).hasClass('ROICalculator__percent'))
3656                        {
3657                            val = val.replace("%", '');
3658                            val = parseFloat(val)/100;
3659                        }
3660                        else {
3661                            val = parseFloat(val);
3662                        }
3663                    }
3664                    else {
3665                        failed = true
3666                            if(!$(this).hasClass('input--error'))
3667                            {
3668                                $(this).addClass('input--error')
3669                                $(this).after('<p class="input-message--error">This field is required.</p>');
3670                            }
3671                    }
3672                    map[$(this).attr("id")] = val;
3673                });
3674                map['unit'] = $(".ROICalculator__container select").val();
3675
3676                $('.ROICalculator__table-body').empty();
3677                $('.ROICalculator__savings-container-info').empty();
3678                if(!failed)
3679                {
3680                    $.ajax({
3681                    type: 'GET',
3682                    url: "/bin/southern/global/roicalculator",
3683                    data: map,
3684                    success: function (data, textStatus, jqXHR) {
3685
3686                      if (window.innerWidth >= 992) {
3687                        formatTableDesktop(data)
3688                        $('ROICalculator__table').data('index', 1);
3689                        updateTable();
3690                         $('.ROICalculator__results').show();
3691                      }
3692                      else {
3693                        formatTableMobile(data)
3694                        $('.ROICalculator__container form').hide();
3695                        $('.ROICalculator__description').hide();
3696                        $('.ROICalculator__results').show();
3697                        $('.roicalculator').addClass('roicalculator--fixed');
3698                        $('.ROICalculator__container').addClass('ROICalculator__container--results')
3699                      }
3700                      //ROICalculator__table-info--header
3701                      //ROICalculator__table-arrow
3702                      $('.ROICalculator__table-info--header').click(function(){
3703                            $(this).next().find('td .ROICalculator__table-info--container').slideToggle();
3704                            $(this).find('.ROICalculator__table-arrow').toggleClass('ROICalculator__table-arrow--flip');
3705                             $('.ROICalculator__results').show();
3706                      });
3707
3708                      $('span[data-popover]').hover(function(){
3709                        var popoverMessage = $(this).data('popover');
3710                        $('.ROICalculator__popover-text').text(popoverMessage);
3711                         $('#table__message').addClass('ROICalculator__popover-message--show');
3712
3713                        Popper.createPopper($(this)[0], $('#table__message')[0], {
3714                          placement: 'bottom',
3715                          modifiers: [
3716                            {
3717                              name: 'offset',
3718                              options: {
3719
3720                                offset: [0, 15],
3721                              },
3722                            },
3723                            { name: 'flip', enabled: false }
3724                          ],
3725                        });
3726
3727                        $(this).hasClass('popover--center')  ? $('.js-popover-triangle').show() : $('.js-popover-triangle').hide();
3728                        event.stopPropagation();
3729                      }, function(){
3730                        $('#table__message').removeClass('ROICalculator__popover-message--show');
3731                      });
3732
3733
3734
3735                    },
3736                    error: function (XMLHttpRequest, textStatus, errorThrown) {
3737                      $('#errordiv').val('Invalid User Name or Password');
3738                    },
3739                });
3740                }
3741          });
3742
3743
3744
3745   });
3746
3747   function updateTable()
3748   {
3749         var tableIndex = $('.ROICalculator__table').data('index');
3750         var $tableRows = $('.ROICalculator__table-body tr');
3751         $tableRows.hide();
3752         $tableRows.each(function(index, row){
3753            if(tableIndex == 1 && index < 20)
3754            {
3755                $(row).show();
3756            }
3757            else if(tableIndex == 2 && index >= 20)
3758            {
3759                $(row).show();
3760            }
3761
3762         })
3763   }
3764
3765   $('.ROICalculator__table-pagination--arrow').click(function(){
3766        var index = $('.ROICalculator__table').data('index');
3767        var $next = $('.ROICalculator__table-pagination--next');
3768        var $prev = $('.ROICalculator__table-pagination--previous')
3769
3770        if(index == 1)
3771        {
3772            $next.attr('disabled', true);
3773            $prev.attr('disabled', false);
3774            index = 2;
3775        }
3776        else {
3777            $next.attr('disabled', false);
3778            $prev.attr('disabled', true);
3779            index = 1;
3780        }
3781
3782        $('.ROICalculator__table').data('index' , index);
3783
3784        var text = index + " of 2";
3785        $('.ROICalculator__table-pagination--text').text(text);
3786        updateTable();
3787
3788   })
3789
3790
3791});
3792/**
3793 * Responsive Nav Bar - Hide on Scroll
3794 *
3795 * When enabled via the component dialog, the nav bar hides when the user
3796 * scrolls down and only reappears once the user scrolls all the way back
3797 * to the top of the page (scrollTop === 0).
3798 *
3799 * Requires GSAP + ScrollTrigger to be loaded globally.
3800 */
3801
3802document.addEventListener('DOMContentLoaded', function() {
3803  var navBar = document.querySelector('.responsive-nav-bar[data-enable-hide-on-scroll="true"]');
3804
3805  if (!navBar) {
3806    return;
3807  }
3808
3809  if (typeof gsap === 'undefined') {
3810    console.warn('Responsive Nav Bar: GSAP is not available');
3811    return;
3812  }
3813
3814  if (typeof ScrollTrigger === 'undefined') {
3815    console.warn('Responsive Nav Bar: GSAP ScrollTrigger plugin is not available');
3816    return;
3817  }
3818
3819  gsap.registerPlugin(ScrollTrigger);
3820
3821  var scrollContainerSelector = navBar.dataset.scrollContainerSelector || '';
3822  var scrollContainer = null;
3823
3824  if (scrollContainerSelector.trim()) {
3825    scrollContainer = document.querySelector(scrollContainerSelector);
3826    if (!scrollContainer) {
3827      console.warn('Responsive Nav Bar: Scroll container not found for selector:', scrollContainerSelector);
3828    }
3829  }
3830
3831  var navBarHeight = navBar.offsetHeight;
3832  var isHidden = false;
3833  var refreshSuppressed = false;
3834
3835  function suppressRefresh() {
3836    if (refreshSuppressed) return;
3837    refreshSuppressed = true;
3838    ScrollTrigger.config({ autoRefreshEvents: 'none' });
3839  }
3840
3841  function restoreRefresh() {
3842    if (!refreshSuppressed) return;
3843    refreshSuppressed = false;
3844    ScrollTrigger.config({ autoRefreshEvents: 'visibilitychange,DOMContentLoaded,load' });
3845  }
3846
3847  function buildShowAnim(height) {
3848    var anim = gsap.timeline({ paused: true })
3849      .from(navBar, {
3850        clipPath: 'inset(0% 0% 100% 0%)',
3851        marginBottom: -height,
3852        duration: 0.2,
3853        force3D: true,
3854        onStart: suppressRefresh,
3855        onComplete: restoreRefresh,
3856        onReverseComplete: restoreRefresh
3857      }, 0);
3858    anim.progress(1);
3859    return anim;
3860  }
3861
3862  var showAnim = buildShowAnim(navBarHeight);
3863
3864  var scrollTriggerOpts = {
3865    start: 'top top',
3866    end: 'max',
3867    onUpdate: function(self) {
3868      var scrollPosition = scrollContainer
3869        ? scrollContainer.scrollTop
3870        : window.scrollY;
3871
3872      if (!isHidden && self.direction === 1 && scrollPosition > 0) {
3873        isHidden = true;
3874        showAnim.reverse();
3875      } else if (isHidden && scrollPosition <= 0) {
3876        isHidden = false;
3877        showAnim.play();
3878      }
3879    }
3880  };
3881
3882  if (scrollContainer) {
3883    scrollTriggerOpts.scroller = scrollContainer;
3884  }
3885
3886  ScrollTrigger.create(scrollTriggerOpts);
3887
3888  var resizeTimeout;
3889  window.addEventListener('resize', function() {
3890    clearTimeout(resizeTimeout);
3891    resizeTimeout = setTimeout(function() {
3892      var newNavBarHeight = navBar.offsetHeight;
3893      if (newNavBarHeight !== navBarHeight) {
3894        navBarHeight = newNavBarHeight;
3895        showAnim.kill();
3896        showAnim = buildShowAnim(navBarHeight);
3897      }
3898      isHidden = false;
3899      showAnim.progress(1);
3900      ScrollTrigger.refresh();
3901    }, 250);
3902  });
3903});
3904
3905$(function () {
3906  $(".related--article-rail[data-type='latest'], .related--article-rail[data-type='latestPublished']").each(function () {
3907    var $this = $(this);
3908    var data = $this.find('.related-article-data');
3909    var cols = data.attr('data-columns') || '';
3910    var root = $this;
3911    var count = parseInt(data.attr('data-count'));
3912    var hideTag = data.attr('data-hide-tag') ? data.attr('data-hide-tag') : "";
3913    var hideImages = data.attr('data-hide-images') === 'true';
3914    var showAuthor = data.attr('data-show-author') === 'true';
3915    var showDescription = data.attr('data-show-description') === 'true';
3916    if (isNaN(count)) {
3917      count = 2;
3918    }
3919
3920    if (hideImages) {
3921      root.addClass('related--article-rail--text-only');
3922    }
3923
3924    var filter = $this.attr('data-type') === 'latestPublished' ? 'most-recent-published' : 'most-recent';
3925
3926    $.get('/bin/southern/global/latestarticles', {
3927      tags: data.attr('data-tags'),
3928      path: data.attr('data-path'),
3929      limit: count + 1,
3930      filter: filter,
3931      start: data.attr('data-start'),
3932    }).done(function (data) {
3933      var pages = data.pages;
3934      pages = pages
3935        .filter(function (s) {
3936          return s.path != window.location.href;
3937        })
3938        .slice(0, count);
3939
3940      for (var i = 0; i < pages.length; i++) {
3941        var tagClasses = pages[i].tagClasses ? pages[i].tagClasses : "";
3942        var article = $(
3943          '<div class="related--article-list col-md-12 ' + cols + ' ' + tagClasses + '">'
3944        );
3945
3946        var redirect = pages[i].customRedirect ? pages[i].customRedirect : pages[i].path;
3947
3948        if (!hideImages) {
3949          var imageAlt = escapeHtmlAttr(pages[i].articlePromoImageAlt);
3950          article.append(
3951            '<div class="related--article-image-section" ><span class="related--article-tag '+hideTag+'">' +
3952              pages[i].primaryTag +
3953              '</span> <a href="' +
3954              redirect +
3955              '"> <img class="related--article-image sr-only" src="' +
3956              pages[i].articlePromoImage +
3957              '" alt="' +
3958              imageAlt +
3959              '"/><figure class="fixedratio" style="background-image: url(' +
3960              pages[i].articlePromoImage +
3961              ');"></figure></a></div>'
3962          );
3963        } else {
3964          article.append(
3965            '<span class="related--article-tag ' + hideTag + ' related--article-tag--standalone">' +
3966              pages[i].primaryTag +
3967              '</span>'
3968          );
3969        }
3970
3971        article.append(
3972          $('<a class="related--article-desc"></a>').attr('href', redirect).text(pages[i].title)
3973        );
3974
3975        if (showDescription && pages[i].description) {
3976          article.append(
3977            $('<p class="related--article-description"></p>').text(pages[i].description)
3978          );
3979        }
3980
3981        if (showAuthor && pages[i].author) {
3982          var $author = $('<div class="related--article-author"></div>');
3983          if (pages[i].authorLink) {
3984            $author.append(
3985              $('<a class="related--article-author-link"></a>')
3986                .attr('href', pages[i].authorLink)
3987                .attr('aria-label', pages[i].author)
3988                .text(pages[i].author)
3989            );
3990          } else {
3991            $author.text(pages[i].author);
3992          }
3993          article.append($author);
3994        }
3995
3996        root.append(article);
3997      }
3998    });
3999  });
4000
4001  function escapeHtmlAttr(value) {
4002    if (value == null || value === '') {
4003      return '';
4004    }
4005    return String(value)
4006      .replace(/&/g, '&amp;')
4007      .replace(/"/g, '&quot;')
4008      .replace(/'/g, '&#39;')
4009      .replace(/</g, '&lt;')
4010      .replace(/>/g, '&gt;');
4011  }
4012});
4013
4014$(function () {
4015  var categoryOption = '',
4016    subcategoryOption = '',
4017    currentScreenSize = 'mobile';
4018
4019  // initial check of screensize
4020  if (window.innerWidth >= 992) {
4021    currentScreenSize = 'desktop';
4022  }
4023
4024  var mq = window.matchMedia('(min-width: 992px)');
4025  mq.addEventListener('change', WidthChange);
4026  WidthChange(mq);
4027
4028  // media query change
4029  function WidthChange(mq) {
4030    $('.rebates__filter.category select').each(function (ev, obj) {
4031      var $el = $(this),
4032        option = $(this).find('option:selected').data('option'),
4033        subCategory = $(this)
4034          .closest('.rebates__filter')
4035          .find('.subcategory.active li[data-active]')
4036          .data('option');
4037      if (option && option != 'All Categories') {
4038        option = option
4039          .replace(/:|\//gi, function () {
4040            return '-';
4041          })
4042          .toLowerCase();
4043      }
4044      if (subCategory) {
4045        subCategory = subCategory
4046          .replace(/:|\//gi, function () {
4047            return '-';
4048          })
4049          .toLowerCase();
4050      }
4051
4052      if (mq.matches) {
4053        // window width is at least 992px
4054        currentScreenSize = 'desktop';
4055        filterRebateResults($el, option, subCategory);
4056      } else {
4057        // window width is less than 992px
4058        currentScreenSize = 'mobile';
4059        filterRebateResults($el, option, subCategory);
4060      }
4061    });
4062  }
4063
4064  $('.rebates__filter.category select').bind({
4065    changed: function (ev, obj) {
4066      var $el = $(this),
4067        option = obj.dropdownOption.data('option');
4068      filterRebateResults($el, option);
4069      showSubcategorySelect($el, option);
4070    },
4071  });
4072  $('.rebates__filter.subcategory select').bind({
4073    changed: function (ev, obj) {
4074      var $el = $(this),
4075        option = obj.dropdownOption.data('option');
4076      filterRebateResults($el, categoryOption, option);
4077    },
4078  });
4079  $('.rebates__filter select').selectBoxIt();
4080
4081  function filterRebateResults($el, category, subcategory) {
4082    // track category, subcategory
4083    categoryOption = category;
4084    subcategoryOption = subcategory;
4085
4086    var rebatesComponent = $el.closest('.rebatesgrid');
4087
4088    // filter grid by category, subcategory
4089    var $gridItems = rebatesComponent.find('.rebates__col');
4090    var itemsToRemove = [];
4091
4092    $gridItems.each(function (i) {
4093      $(this).show();
4094      var tags = $(this).attr('data-tags');
4095      if (tags) {
4096        tags = tags.toLowerCase().split(';');
4097      }
4098      if (categoryOption != 'All Categories') {
4099        if (categoryOption != '' && categoryOption != undefined) {
4100          categoryOption = category
4101            .replace(/:|\//gi, function () {
4102              return '-';
4103            })
4104            .toLowerCase();
4105
4106          var subcategoryOptionMatches = tags.filter(function (s) {
4107            return s.includes(categoryOption);
4108          });
4109          if (
4110            tags &&
4111            tags.indexOf(categoryOption) < 0 &&
4112            subcategoryOptionMatches.length <= 0
4113          ) {
4114            $(this).hide();
4115            itemsToRemove.push($(this));
4116          } else {
4117            $(this).show();
4118          }
4119        }
4120        if (
4121          subcategoryOption != '' &&
4122          subcategoryOption != undefined &&
4123          subcategoryOption != 'All Categories'
4124        ) {
4125          subcategoryOption = subcategory
4126            .replace(/:|\//gi, function (x) {
4127              return '-';
4128            })
4129            .toLowerCase();
4130          if (tags && tags.indexOf(subcategoryOption) < 0) {
4131            $(this).hide();
4132            if (itemsToRemove.indexOf(i) < 0) {
4133              itemsToRemove.push($(this));
4134            }
4135          } else {
4136            if (tags && tags.indexOf(categoryOption) > -1) {
4137              $(this).show();
4138            }
4139          }
4140        }
4141      }
4142    });
4143
4144    // if slider, don't pagninate
4145    $slider = rebatesComponent.find('.pagination');
4146    if ($slider) {
4147      // remove items from array before initializing pagination
4148      for (var i = 0; i < itemsToRemove.length; i++) {
4149        for (var j = 0; j < $gridItems.length; j++) {
4150          if ($($gridItems[j]).is(itemsToRemove[i])) {
4151            $gridItems.splice(j, 1);
4152            j--;
4153          }
4154        }
4155      }
4156
4157      // handle pagination
4158      var $pagination = rebatesComponent.find('.rebates__pagination');
4159      var $visibleItems = rebatesComponent.find('.rebates__pages');
4160      var rowsDesktop = rebatesComponent
4161        .find('[data-rows-desktop]')
4162        .attr('data-rows-desktop');
4163      var rowsMobile = rebatesComponent
4164        .find('[data-rows-mobile]')
4165        .attr('data-rows-mobile');
4166      if (currentScreenSize == 'desktop') {
4167        initPagination($pagination, $gridItems, 3 * rowsDesktop, $visibleItems);
4168      } else {
4169        initPagination($pagination, $gridItems, 2 * rowsMobile, $visibleItems);
4170      }
4171    }
4172  }
4173
4174  function showSubcategorySelect($el, category) {
4175    var rebatesComponent = $el.closest('.rebatesgrid');
4176
4177    rebatesComponent
4178      .find('.rebates__filter.subcategory.active')
4179      .removeClass('active');
4180    if (category != '') {
4181      var subcategoryFilter = rebatesComponent.find(
4182        '.rebates__filter.subcategory[data-category="' + category + '"]'
4183      );
4184      subcategoryFilter.addClass('active');
4185      var categorySelect = rebatesComponent
4186        .find('.rebates__filter.category')
4187        .find('select')
4188        .data('selectBox-selectBoxIt');
4189      var subcategorySelect = subcategoryFilter
4190        .find('select')
4191        .data('selectBox-selectBoxIt');
4192      if (subcategorySelect) {
4193        var firstOption = subcategorySelect.selectOption(0);
4194      }
4195    }
4196  }
4197});
4198
4199$(function() {
4200  // initiate polyfill
4201  $(document).ready(function() {
4202    if (document.querySelector('.rebates__img img')) {
4203      var objectFit = new ObjectFitPolyfill('.rebates__img img');
4204    }
4205  });
4206});
4207
4208(function() {
4209    'use strict';
4210
4211    document.addEventListener('DOMContentLoaded', function() {
4212        initializePricingRows();
4213    });
4214
4215    function resolveTemplateSimple(templateStr, data) {
4216        return templateStr.replace(/\{\{([^}]+)\}\}/g, function(match, expression) {
4217            var parts = expression.trim().split('.');
4218            var value = data;
4219            for (var i = 0; i < parts.length; i++) {
4220                if (value == null) return '';
4221                var key = parts[i].replace(/^\[|\]$/g, '');
4222                value = value[key];
4223            }
4224            return (value != null) ? value : '';
4225        });
4226    }
4227
4228    function compileAndRender(templateStr, data) {
4229        if (typeof Handlebars !== 'undefined') {
4230            try {
4231                var template = Handlebars.compile(templateStr);
4232                return template(data);
4233            } catch (e) {
4234                console.warn('[RatePlans] Handlebars.compile failed (possible CSP restriction):', e.message);
4235            }
4236        } else {
4237            console.warn('[RatePlans] Handlebars not available, using fallback resolver');
4238        }
4239        return resolveTemplateSimple(templateStr, data);
4240    }
4241
4242    function initializePricingRows() {
4243        var pricingRows = document.querySelectorAll('.pricing-row .cf-row[data-cfpath]');
4244
4245        pricingRows.forEach(function(row) {
4246            var cfPath = row.getAttribute('data-cfpath');
4247            var variation = row.getAttribute('data-variation');
4248
4249            if (cfPath) {
4250                row.classList.add('loading');
4251                var jsonUrl = buildJsonUrl(cfPath, variation);
4252                fetchAndRenderRow(row, jsonUrl);
4253            }
4254        });
4255    }
4256
4257    function buildJsonUrl(cfPath, variation) {
4258        cfPath = cfPath.replace(/\/+$/, '');
4259        var jsonUrl = cfPath + '.model.json';
4260        if (!jsonUrl.startsWith('/')) {
4261            jsonUrl = '/' + jsonUrl;
4262        }
4263        return buildAbsoluteUrl(jsonUrl);
4264    }
4265
4266    function buildAbsoluteUrl(path) {
4267        if (path.startsWith('http://') || path.startsWith('https://')) {
4268            return path;
4269        }
4270        return window.location.origin + path;
4271    }
4272
4273    function fetchAndRenderRow(rowElement, jsonUrl) {
4274        fetch(jsonUrl, {
4275            credentials: 'same-origin',
4276            headers: {
4277                'Accept': 'application/json'
4278            }
4279        })
4280            .then(function(response) {
4281                if (!response.ok) {
4282                    throw new Error('HTTP ' + response.status + ': ' + jsonUrl);
4283                }
4284                return response.text();
4285            })
4286            .then(function(text) {
4287                if (!text || text.trim().length === 0) {
4288                    throw new Error('Empty response from: ' + jsonUrl);
4289                }
4290                try {
4291                    return JSON.parse(text);
4292                } catch (e) {
4293                    throw new Error('Invalid JSON response from: ' + jsonUrl);
4294                }
4295            })
4296            .then(function(data) {
4297                if (data.error) {
4298                    throw new Error('Server error: ' + data.error);
4299                }
4300                renderRowWithData(rowElement, data);
4301                rowElement.classList.remove('loading');
4302                rowElement.classList.add('loaded');
4303            })
4304            .catch(function(error) {
4305                console.error('[RatePlans] Error fetching content fragment data:', error);
4306                rowElement.classList.remove('loading');
4307                rowElement.classList.add('error');
4308            });
4309    }
4310
4311    function renderRowWithData(rowElement, data) {
4312        var cells = rowElement.querySelectorAll('div');
4313
4314        cells.forEach(function(cell) {
4315            var fieldName = cell.getAttribute('data-template-field');
4316            var templateSource = fieldName ? (data[fieldName] || '') : cell.innerHTML;
4317
4318            if (templateSource && templateSource.includes('{{')) {
4319                var renderedHtml = compileAndRender(templateSource, data);
4320                cell.innerHTML = renderedHtml;
4321            }
4322        });
4323    }
4324
4325})();
4326
4327document.addEventListener('DOMContentLoaded', () => {
4328    const stickyParents = document.querySelectorAll('.sticky-row');
4329    
4330    stickyParents.forEach((stickyParent) => {
4331        // Create a "sentinel" or observer target above each sticky element
4332        const sentinel = document.createElement('div');
4333        sentinel.style.position = 'absolute';
4334        sentinel.style.top = '-1px'; // Place it just above
4335        sentinel.style.height = '1px';
4336        sentinel.style.width = '100%';
4337        stickyParent.parentNode.insertBefore(sentinel, stickyParent);
4338
4339        const observer = new IntersectionObserver(([entry]) => {
4340            // If the sentinel is intersecting, the parent is NOT stuck at the top
4341            if (entry.isIntersecting) {
4342                stickyParent.classList.remove('is-stuck');
4343            } else {
4344                // The sentinel is out of view (above the viewport top), so it is stuck
4345                stickyParent.classList.add('is-stuck');
4346            }
4347        }, {
4348            root: null, // observe against the viewport
4349            rootMargin: '0px',
4350            threshold: 0 // Fire as soon as 1 pixel crosses the boundary
4351        });
4352
4353        observer.observe(sentinel);
4354    });
4355});
4356$(function() {
4357  var SCREEN_MD = 992,
4358    prevMd,
4359    imgSelector = '.quote-container .quote__img img';
4360
4361  function removePolyFillStyles() {
4362    var quoteImgs = document.querySelectorAll(imgSelector);
4363    for (var i = 0; i < quoteImgs.length; i++) {
4364      quoteImgs[i].removeAttribute('style');
4365      quoteImgs[i].classList.remove('object-fit');
4366    }
4367  }
4368
4369  function quoteImagePolyfill() {
4370    var md = window.matchMedia('(min-width: ' + SCREEN_MD + 'px)').matches;
4371    // if the window width changed between sm and md || the document initialized (since prevMd wasn't set on init)
4372    if (md != prevMd || !prevMd) {
4373      if (md) {
4374        if (document.querySelectorAll(imgSelector)) {
4375          var objectFit = new ObjectFitPolyfill(imgSelector);
4376        }
4377      } else {
4378        removePolyFillStyles();
4379      }
4380
4381      prevMd = md;
4382    }
4383  }
4384
4385  // initiate polyfill
4386  $(window).on('load', function() {
4387    quoteImagePolyfill();
4388  });
4389
4390  // add or remove object-fit polyfill when screen is resized above or below 992px
4391  window.addEventListener('resize', quoteImagePolyfill);
4392});
4393
4394document.addEventListener('DOMContentLoaded', () => {
4395  const allQuestionnaires = document.querySelectorAll('.questionnaire-wrapper');
4396  allQuestionnaires.forEach((questionnaireWrapper) => {
4397    const componentId = questionnaireWrapper.id;
4398    const container = questionnaireWrapper.closest('.questionnaire-container');
4399    if (!container) return;
4400    var simulatedSelections = "";
4401    const panels = container.querySelectorAll('.panel');
4402    const prevBtn = container.querySelector('.prev-button button');
4403    const nextBtn = container.querySelector('.next-button button');
4404    const resultsContainer = container.querySelector('.questionnaire-results');
4405    const ctaWrapper = container.querySelector('.cta-wrapper');
4406    const resetBtn = ctaWrapper?.querySelector('button.reset-cta');
4407    const progressSteps = container.querySelectorAll('.progress-section li');
4408    const progressBar = container.querySelector('.progress-section');
4409    const cardsContainer = container.querySelector('.cards-container');
4410    const cardWrappers = Array.from(
4411      cardsContainer?.querySelectorAll('.card-wrapper') || []
4412    );
4413    const mobilePrevBtn = cardsContainer?.querySelector(
4414      '.mobile-prev-button button'
4415    );
4416    const mobileNextBtn = cardsContainer?.querySelector(
4417      '.mobile-next-button button'
4418    );
4419    const emailButton = container.querySelectorAll('.email-button');
4420
4421    if (emailButton) {
4422      $(emailButton).click(function () {
4423        $('.email-submit--background').show();
4424        $('.email-submit').show();
4425      });
4426
4427      $('.email__close').click(function () {
4428        $('.email-submit--background').hide();
4429        $('.email-submit').hide();
4430      });
4431
4432      function resetMessage() {
4433        $('.input-message--error').remove();
4434        $('.input--error').removeClass('input--error');
4435      }
4436
4437      function isValidEmail(email) {
4438        return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
4439      }
4440
4441      $('.send-email-js-questionnaire-trigger').click(function () {
4442        var email = $(container.querySelectorAll('.questionnaire-email')).val();
4443        console.log(email);
4444        console.log(convertUserSelections(getUserSelections()));
4445        var path = $(this).data('path');
4446        resetMessage();
4447        if (!email || !isValidEmail(email)) {
4448          $(this).addClass('input--error');
4449          $(this)
4450            .prev()
4451            .before(
4452              `<p class='input-message--error'>Please enter a valid email address.</p>`
4453            );
4454        } else {
4455          $.ajax({
4456            type: 'POST',
4457            url: '/bin/southern/global/questionnaireemail',
4458            data: {
4459              email: email,
4460              path: path,
4461              url: window.location.href,
4462              share: simulatedSelections != "" ? simulatedSelections : convertUserSelections(getUserSelections()),
4463            },
4464            success: function (msg) {
4465              $('.email-submit--background').hide();
4466              $('.email-submit').hide();
4467            },
4468            error: function () {
4469              $(this).addClass('input--error');
4470              $(this)
4471                .prev()
4472                .before(
4473                  "<p class='input-message--error'>There was an issue sending the email. Please try again later.</p>"
4474                );
4475            },
4476          });
4477        }
4478      });
4479    }
4480
4481    let currentPanelIndex = 0;
4482    let currentCardIndex = 0;
4483    let visibleCards = [];
4484
4485    const customResultsParam = getQueryParam('custom-results');
4486    console.log(customResultsParam);
4487    if (customResultsParam) {
4488      simulatedSelections = parseCustomResultsString(customResultsParam);
4489
4490      // Skip to results view
4491      questionnaireWrapper.classList.add('hidden');
4492      progressBar.classList.add('hidden');
4493      resultsContainer.classList.remove('hidden');
4494      ctaWrapper?.classList.remove('hidden');
4495      evaluateResultsVisibility(simulatedSelections);
4496    }
4497    if (!customResultsParam) {
4498      console.log('test');
4499      questionnaireWrapper.classList.remove('hidden');
4500      updatePanelVisibility();
4501      updateQuestionDependencies();
4502    }
4503
4504    function evaluateRangeCondition(userValue, condition) {
4505      if (typeof userValue !== 'number' || isNaN(userValue)) return false;
4506      if (condition.includes('-')) {
4507        const [min, max] = condition.split('-').map(Number);
4508        return userValue >= min && userValue <= max;
4509      }
4510      if (condition.startsWith('<='))
4511        return userValue <= parseFloat(condition.substring(2));
4512      if (condition.startsWith('>='))
4513        return userValue >= parseFloat(condition.substring(2));
4514      if (condition.startsWith('<'))
4515        return userValue < parseFloat(condition.substring(1));
4516      if (condition.startsWith('>'))
4517        return userValue > parseFloat(condition.substring(1));
4518      if (condition.startsWith('='))
4519        return userValue === parseFloat(condition.substring(1));
4520      return false;
4521    }
4522
4523    function convertUserSelections(selections) {
4524      const parts = [];
4525
4526      Object.entries(selections).forEach(([key, value]) => {
4527        if (Array.isArray(value)) {
4528          value.forEach((val) => {
4529            parts.push(`${encodeURIComponent(key)}${encodeURIComponent(val)}`);
4530          });
4531        } else {
4532          parts.push(`${encodeURIComponent(key)}${encodeURIComponent(value)}`);
4533        }
4534      });
4535
4536      return parts.join('-');
4537    }
4538
4539    function getUserSelections() {
4540      const selections = {};
4541      container.querySelectorAll('.question').forEach((question, index) => {
4542        const selectedRadio = question.querySelector(
4543          'input[type="radio"]:checked'
4544        );
4545        const selectedCheckboxes = question.querySelectorAll(
4546          'input[type="checkbox"]:checked'
4547        );
4548        const selectedDropdown = question.querySelector('select');
4549        const selectedInput = question.querySelector(
4550          'input[type="text"][data-options], input[type="number"][data-options]'
4551        );
4552        const simpleInput = question.querySelector(
4553          'input[type="text"]:not([data-options]), input[type="number"]:not([data-options])'
4554        );
4555
4556        if (selectedRadio) {
4557          selections[index + 1] = selectedRadio.value;
4558        } else if (selectedCheckboxes.length > 0) {
4559          selections[index + 1] = Array.from(selectedCheckboxes).map(
4560            (cb) => cb.value
4561          );
4562        } else if (selectedDropdown && selectedDropdown.value !== '') {
4563          selections[index + 1] = selectedDropdown.value;
4564        } else if (selectedInput && selectedInput.value.trim() !== '') {
4565          const userValue = parseFloat(selectedInput.value.trim());
4566          const optionsData = selectedInput.dataset.options;
4567          let matchedOptions = [];
4568          if (optionsData) {
4569            try {
4570              const decodedOptions = optionsData.replace(/&quot;/g, '"');
4571              const options = JSON.parse(decodedOptions);
4572              options.forEach((option) => {
4573                if (evaluateRangeCondition(userValue, option.optionContent)) {
4574                  matchedOptions.push(option.optionCode);
4575                }
4576              });
4577            } catch (e) {
4578              console.error('Invalid range options JSON:', optionsData, e);
4579            }
4580          }
4581          if (matchedOptions.length > 0) {
4582            selections[index + 1] = matchedOptions;
4583          }
4584        } else if (simpleInput && simpleInput.value.trim() !== '') {
4585          selections[index + 1] = simpleInput.value.trim();
4586        }
4587      });
4588      return selections;
4589    }
4590
4591    function parseDependencyString(depString) {
4592      try {
4593        if (depString === '[]' || depString === '') return [];
4594        if (depString.startsWith('[')) {
4595          depString = depString.replace(/&quot;/g, '"');
4596          return JSON.parse(depString);
4597        }
4598        return [depString];
4599      } catch (e) {
4600        console.error('Invalid dependency format:', depString, e);
4601        return [];
4602      }
4603    }
4604
4605    function parseConditionBlock(block) {
4606      return block
4607        .split('-')
4608        .map((c) => {
4609          const match = c.trim().match(/^(\d+)([A-Za-z0-9]+)$/);
4610          if (!match) return null;
4611          return {
4612            questionNum: parseInt(match[1], 10),
4613            expectedVal: match[2],
4614          };
4615        })
4616        .filter(Boolean);
4617    }
4618
4619    function matchesDependency(dependencyString, selections) {
4620      const groups = parseDependencyString(dependencyString);
4621      if (groups.length === 0) return true;
4622      return groups.some((group) => {
4623        if (typeof group !== 'string') return false;
4624        const orBlocks = group.split('||').map((b) => b.trim());
4625        return orBlocks.some((block) => {
4626          const conditions = parseConditionBlock(block);
4627          return conditions.every(({ questionNum, expectedVal }) => {
4628            const userAnswer = selections[questionNum];
4629            return Array.isArray(userAnswer)
4630              ? userAnswer.includes(expectedVal)
4631              : userAnswer === expectedVal;
4632          });
4633        });
4634      });
4635    }
4636
4637    function updateQuestionDependencies() {
4638      const allQuestions = Array.from(
4639        container.querySelectorAll('.question-wrapper')
4640      );
4641      const selections = getUserSelections();
4642      allQuestions.forEach((wrapper) => {
4643        const question = wrapper.querySelector('.question');
4644        const dependency = question?.dataset.dependency;
4645        if (!dependency || matchesDependency(dependency, selections)) {
4646          wrapper.classList.remove('hidden');
4647        } else {
4648          wrapper.classList.add('hidden');
4649          wrapper
4650            .querySelectorAll('input')
4651            ?.forEach((input) => (input.checked = false));
4652          const select = wrapper.querySelector('select');
4653          if (select) select.selectedIndex = 0;
4654        }
4655      });
4656      updateButtonStates();
4657    }
4658
4659    function updatePanelVisibility() {
4660      panels.forEach((panel, index) => {
4661        panel.classList.toggle('hidden', index !== currentPanelIndex);
4662      });
4663      resultsContainer.classList.add('hidden');
4664      updateButtonStates();
4665      updateProgressBar();
4666    }
4667
4668    function updateButtonStates() {
4669      prevBtn.disabled = currentPanelIndex === 0;
4670      prevBtn.classList.toggle('disabled', currentPanelIndex === 0);
4671
4672      const currentPanel = panels[currentPanelIndex];
4673      const visibleQuestions = currentPanel.querySelectorAll(
4674        '.question-wrapper:not(.hidden) .question'
4675      );
4676
4677      const allAnswered = Array.from(visibleQuestions).every((question) => {
4678        if (question.classList.contains('radio')) {
4679          return Array.from(
4680            question.querySelectorAll('input[type="radio"]')
4681          ).some((r) => r.checked);
4682        } else if (question.classList.contains('checkboxes')) {
4683          return Array.from(
4684            question.querySelectorAll('input[type="checkbox"]')
4685          ).some((c) => c.checked);
4686        } else if (question.classList.contains('dropdown')) {
4687          const select = question.querySelector('select');
4688          return select && select.value !== '';
4689        } else if (question.classList.contains('range')) {
4690          const input = question.querySelector(
4691            'input[type="text"], input[type="number"]'
4692          );
4693          return input && input.value.trim() !== '';
4694        }
4695        return true;
4696      });
4697
4698      nextBtn.disabled = !allAnswered;
4699      nextBtn.classList.toggle('disabled', !allAnswered);
4700    }
4701
4702    function evaluateResultsVisibility(simulatedSelections) {
4703      var selections;
4704      if (simulatedSelections == undefined) {
4705        selections = getUserSelections();
4706      } else {
4707        selections = simulatedSelections;
4708      }
4709
4710      visibleCards = [];
4711
4712      cardWrappers.forEach((el) => {
4713        const dependency = el.getAttribute('data-dependency');
4714        const isVisible =
4715          !dependency || matchesDependency(dependency, selections);
4716        el.style.display = isVisible ? '' : 'none';
4717        if (isVisible) visibleCards.push(el);
4718      });
4719
4720      container.querySelectorAll('.addon-wrapper').forEach((el) => {
4721        const dependency = el.getAttribute('data-dependency');
4722        el.style.display =
4723          !dependency || matchesDependency(dependency, selections)
4724            ? ''
4725            : 'none';
4726      });
4727
4728      updateCardCarousel();
4729      updateResultText('.cards-result-text', '.card-wrapper');
4730      updateResultText('.addons-result-text', '.addon-wrapper');
4731    }
4732
4733    function updateProgressBar() {
4734      progressSteps.forEach((step, index) => {
4735        step.classList.remove('is-active', 'is-complete');
4736        if (index < currentPanelIndex) step.classList.add('is-complete');
4737        else if (index === currentPanelIndex) step.classList.add('is-active');
4738      });
4739    }
4740
4741    function updateCardCarousel() {
4742      const isMobile = window.matchMedia('(max-width: 991px)').matches;
4743      if (mobileNextBtn && mobilePrevBtn) {
4744        mobilePrevBtn.classList.toggle('hidden', visibleCards.length < 2);
4745        mobileNextBtn.classList.toggle('hidden', visibleCards.length < 2);
4746      }
4747      if (isMobile && visibleCards.length > 1) {
4748        cardsContainer?.classList.add('carousel');
4749        currentCardIndex = 0;
4750        updateCardVisibility();
4751      } else {
4752        cardsContainer?.classList.remove('carousel');
4753        visibleCards.forEach((card) => (card.style.display = ''));
4754      }
4755    }
4756
4757    function updateCardVisibility() {
4758      visibleCards.forEach((card, index) => {
4759        card.style.display = index === currentCardIndex ? '' : 'none';
4760      });
4761      if (mobilePrevBtn) {
4762        mobilePrevBtn.disabled = currentCardIndex === 0;
4763        mobilePrevBtn.classList.toggle('disabled', currentCardIndex === 0);
4764      }
4765      if (mobileNextBtn) {
4766        mobileNextBtn.disabled = currentCardIndex === visibleCards.length - 1;
4767        mobileNextBtn.classList.toggle(
4768          'disabled',
4769          currentCardIndex === visibleCards.length - 1
4770        );
4771      }
4772    }
4773
4774    function goToNextPanel() {
4775      if (!nextBtn.disabled && currentPanelIndex < panels.length - 1) {
4776        currentPanelIndex++;
4777        updatePanelVisibility();
4778        scrollToProgressBar();
4779      } else if (!nextBtn.disabled) {
4780        questionnaireWrapper.classList.add('hidden');
4781        progressBar.classList.add('hidden');
4782        resultsContainer.classList.remove('hidden');
4783        ctaWrapper?.classList.remove('hidden');
4784        evaluateResultsVisibility();
4785      }
4786    }
4787
4788    function goToPrevPanel() {
4789      if (currentPanelIndex > 0) {
4790        currentPanelIndex--;
4791        updatePanelVisibility();
4792        scrollToProgressBar();
4793      }
4794    }
4795
4796    function resetQuestionnaire() {
4797      currentPanelIndex = 0;
4798      currentCardIndex = 0;
4799      visibleCards = [];
4800      simulatedSelections = "";
4801
4802      container.querySelectorAll('input').forEach((input) => {
4803        if (input.type === 'radio' || input.type === 'checkbox') {
4804          input.checked = false;
4805        } else if (input.type === 'text' || input.type === 'number') {
4806          input.value = '';
4807        }
4808      });
4809
4810      container.querySelectorAll('select').forEach((select) => {
4811        select.selectedIndex = 0;
4812      });
4813
4814      container.querySelectorAll('.question-wrapper').forEach((q) => {
4815        q.classList.remove('hidden');
4816      });
4817
4818      progressSteps.forEach((li) => {
4819        li.classList.remove('is-active', 'is-complete');
4820      });
4821
4822      questionnaireWrapper.classList.remove('hidden');
4823      progressBar.classList.remove('hidden');
4824      resultsContainer.classList.add('hidden');
4825      ctaWrapper?.classList.add('hidden');
4826
4827      updatePanelVisibility();
4828      updateQuestionDependencies();
4829      evaluateResultsVisibility();
4830      scrollToProgressBar();
4831    }
4832
4833    prevBtn?.addEventListener('click', goToPrevPanel);
4834    nextBtn?.addEventListener('click', goToNextPanel);
4835
4836    container
4837      .querySelectorAll(
4838        'input[type="radio"], input[type="checkbox"], select, input[type="text"], input[type="number"]'
4839      )
4840      .forEach((input) => {
4841        input.addEventListener('input', () => {
4842          updateQuestionDependencies();
4843          updateButtonStates();
4844        });
4845      });
4846
4847    resetBtn?.addEventListener('click', resetQuestionnaire);
4848    mobileNextBtn?.addEventListener('click', () => {
4849      if (currentCardIndex < visibleCards.length - 1) {
4850        currentCardIndex++;
4851        updateCardVisibility();
4852      }
4853    });
4854    mobilePrevBtn?.addEventListener('click', () => {
4855      if (currentCardIndex > 0) {
4856        currentCardIndex--;
4857        updateCardVisibility();
4858      }
4859    });
4860
4861    function scrollToProgressBar() {
4862      const progressSection = container.querySelector('.progress-section');
4863      if (progressSection) {
4864        const rect = progressSection.getBoundingClientRect();
4865        const scrollTop =
4866          window.pageYOffset || document.documentElement.scrollTop;
4867        const targetY = rect.top + scrollTop - 100;
4868        window.scrollTo({ top: targetY, behavior: 'smooth' });
4869      }
4870    }
4871
4872    function updateResultText(containerSelector, itemSelector) {
4873      const container = document.querySelector(containerSelector);
4874      const allItems = document.querySelectorAll(itemSelector);
4875      const visibleItems = Array.from(allItems).filter(
4876        (el) => el.style.display !== 'none'
4877      );
4878
4879      // First check: if there are NO items at all (even hidden)
4880      if (allItems.length === 0) {
4881        container?.querySelectorAll('[data-results]').forEach((el) => {
4882          el.style.display = 'none';
4883        });
4884        return; // Done early
4885      }
4886
4887      const visibleCount = visibleItems.length;
4888
4889      container?.querySelectorAll('[data-results]').forEach((el) => {
4890        const condition = el.getAttribute('data-results');
4891        if (condition === '0' && visibleCount === 0) {
4892          el.style.display = '';
4893        } else if (condition === '1' && visibleCount === 1) {
4894          el.style.display = '';
4895        } else if (condition === '>1' && visibleCount > 1) {
4896          el.style.display = '';
4897        } else {
4898          el.style.display = 'none';
4899        }
4900      });
4901    }
4902
4903    window.addEventListener('resize', updateCardCarousel);
4904  });
4905
4906  function getQueryParam(param) {
4907    const urlParams = new URLSearchParams(window.location.search);
4908    return urlParams.get(param);
4909  }
4910
4911  function parseCustomResultsString(resultString) {
4912    const selections = {};
4913    const pairs = resultString.split('-');
4914
4915    pairs.forEach((pair) => {
4916      const match = pair.match(/^(\d+)(.+)$/);
4917      if (match) {
4918        const [_, questionNum, answer] = match;
4919        if (!selections[questionNum]) {
4920          selections[questionNum] = answer;
4921        } else if (Array.isArray(selections[questionNum])) {
4922          selections[questionNum].push(answer);
4923        } else {
4924          selections[questionNum] = [selections[questionNum], answer];
4925        }
4926      }
4927    });
4928
4929    return selections;
4930  }
4931});
4932
4933$(function() {
4934  var SCREEN_MD = 992,
4935    prevMd,
4936    imgSelector = '.feature-story__container:not( .feature-story--avoid-polyfill) img';
4937
4938  function removePolyFillStyles() {
4939    var featureStoryImgs = document.querySelectorAll(imgSelector);
4940    for (var i = 0; i < featureStoryImgs.length; i++) {
4941      featureStoryImgs[i].removeAttribute('style');
4942      featureStoryImgs[i].classList.remove('object-fit');
4943    }
4944  }
4945
4946  function featureStoryImagePolyfill() {
4947    var md = window.matchMedia('(min-width: ' + SCREEN_MD + 'px)').matches;
4948    // if the window width changed between sm and md || the document initialized (since prevMd wasn't set on init)
4949    if (md != prevMd || !prevMd) {
4950      if (md) {
4951        if (document.querySelectorAll(imgSelector)) {
4952          var objectFit = new ObjectFitPolyfill(imgSelector);
4953        }
4954      } else {
4955        removePolyFillStyles();
4956      }
4957
4958      prevMd = md;
4959    }
4960  }
4961
4962  // initiate polyfill
4963  $(window).on('load', function() {
4964    featureStoryImagePolyfill();
4965  });
4966
4967  // add or remove object-fit polyfill when screen is resized above or below 992px
4968  window.addEventListener('resize', featureStoryImagePolyfill);
4969});
4970
4971(function() {
4972  var SCREEN_MD = 992,
4973    prevMd,
4974    promoCarouselSwipers = []; // Store multiple swiper instances
4975
4976  //custom function
4977  function initPromoCarouselSwiper() {
4978    var md = window.matchMedia('(min-width: ' + SCREEN_MD + 'px)').matches;
4979    if (md != prevMd || !prevMd) {
4980      if (md && promoCarouselSwipers.length > 0) {
4981        // destroy the swiper on desktop (992px and up)
4982        $('.promo-carousel').each(function(index) {
4983          if (promoCarouselSwipers[index] != undefined) {
4984            promoCarouselSwipers[index].destroy(true, true);
4985            promoCarouselSwipers[index] = undefined;
4986          }
4987        });
4988      } else if (!md && promoCarouselSwipers.length === 0) {
4989        // initialize the swiper on mobile
4990        $('.promo-carousel').each(function(index) {
4991          var $this = $(this);
4992          promoCarouselSwipers[index] = new Swiper($this[0], {
4993            speed: 400,
4994            spaceBetween: 20,
4995            slidesPerView: 3,
4996            grabCursor: true,
4997            pagination: {
4998              el: $this.children('.swiper-pagination')[0],
4999              type: 'bullets',
5000              clickable: true
5001            },
5002            breakpoints: {
5003              // when window width is <= 991px
5004              991: {
5005                slidesPerView: 1
5006              }
5007            }
5008          });
5009        });
5010      }
5011      prevMd = md;
5012    }
5013  }
5014
5015  /**
5016   * Handle hash-based navigation to slides
5017   * Navigates to a slide with matching anchor ID and scrolls to it
5018   */
5019  var handleHashNavigation = function() {
5020    console.log('handleHashNavigation');
5021    var hash = window.location.hash;
5022    if (!hash || hash === "") return;
5023    
5024    // Find slide with matching ID
5025    var targetSlide = document.querySelector('.promo-carousel__slides-item' + hash);
5026    if (!targetSlide) return;
5027    
5028    // Find the parent carousel
5029    var carousel = targetSlide.closest('.promo-carousel');
5030    if (!carousel) return;
5031    
5032    // Get the index of all carousels on the page
5033    var allCarousels = document.querySelectorAll('.promo-carousel');
5034    var carouselIndex = Array.prototype.indexOf.call(allCarousels, carousel);
5035    
5036    // Get the swiper instance for this carousel
5037    var swiperInstance = promoCarouselSwipers[carouselIndex];
5038    
5039    // Get the slide index within this carousel
5040    var slides = carousel.querySelectorAll('.promo-carousel__slides-item');
5041    var slideIndex = Array.prototype.indexOf.call(slides, targetSlide);
5042    
5043    if (slideIndex === -1) return;
5044    
5045    // Navigate to slide if swiper exists (mobile) - no animation
5046    if (swiperInstance && swiperInstance.slideTo) {
5047      swiperInstance.slideTo(slideIndex, 0);
5048    }
5049    
5050    // Calculate final scroll position as a single value
5051    var scrollAdjustment = parseInt(targetSlide.dataset.scrollAdjustment, 10) || 0;
5052    var carouselOffsetTop = carousel.offsetTop; // Use offsetTop instead of getBoundingClientRect
5053    var finalScrollPosition = carouselOffsetTop + scrollAdjustment;
5054    
5055    // Calculate duration based on scroll distance
5056    var currentPosition = window.pageYOffset;
5057    var distance = Math.abs(finalScrollPosition - currentPosition);
5058    var pixelsPerSecond = 1200;
5059    var duration = distance / pixelsPerSecond;
5060    duration = Math.max(0.3, Math.min(1.5, duration));
5061    
5062    setTimeout(function() {
5063      gsap.to(window, {
5064        duration: duration,
5065        scrollTo: { y: finalScrollPosition },
5066        ease: "power2.out",
5067        onComplete: function() {
5068          if (hash && window.location.hash !== hash) {
5069            window.history.pushState(null, null, hash);
5070          }
5071        }
5072      });
5073    }, 300);
5074  };
5075
5076  // Intercept clicks on hash links that match promo carousel slide IDs
5077  $(document).on('click', 'a[href^="#"]', function(e) {
5078    var href = $(this).attr('href');
5079    if (!href || href === '#') return;
5080    
5081    // Only intercept if hash matches a promo carousel slide ID
5082    var targetSlide = document.querySelector('.promo-carousel__slides-item' + href);
5083    if (!targetSlide) return;
5084    
5085    e.preventDefault();
5086    window.history.pushState(null, null, href);
5087    handleHashNavigation();
5088  });
5089
5090  // initiate slider
5091  $(window).on('load', function() {
5092    initPromoCarouselSwiper();
5093    
5094    // Check hash after swiper initialization
5095    setTimeout(handleHashNavigation, 500);
5096  });
5097
5098  $(window).on('resize', function() {
5099    initPromoCarouselSwiper();
5100  });
5101
5102  // Listen for hash changes
5103  window.addEventListener('hashchange', handleHashNavigation);
5104})();
5105
5106/**
5107 * @popperjs/core v2.9.2 - MIT License
5108 */
5109
5110(function (global, factory) {
5111  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
5112  typeof define === 'function' && define.amd ? define(['exports'], factory) :
5113  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Popper = {}));
5114}(this, (function (exports) { 'use strict';
5115
5116  function getBoundingClientRect(element) {
5117    var rect = element.getBoundingClientRect();
5118    return {
5119      width: rect.width,
5120      height: rect.height,
5121      top: rect.top,
5122      right: rect.right,
5123      bottom: rect.bottom,
5124      left: rect.left,
5125      x: rect.left,
5126      y: rect.top
5127    };
5128  }
5129
5130  function getWindow(node) {
5131    if (node == null) {
5132      return window;
5133    }
5134
5135    if (node.toString() !== '[object Window]') {
5136      var ownerDocument = node.ownerDocument;
5137      return ownerDocument ? ownerDocument.defaultView || window : window;
5138    }
5139
5140    return node;
5141  }
5142
5143  function getWindowScroll(node) {
5144    var win = getWindow(node);
5145    var scrollLeft = win.pageXOffset;
5146    var scrollTop = win.pageYOffset;
5147    return {
5148      scrollLeft: scrollLeft,
5149      scrollTop: scrollTop
5150    };
5151  }
5152
5153  function isElement(node) {
5154    var OwnElement = getWindow(node).Element;
5155    return node instanceof OwnElement || node instanceof Element;
5156  }
5157
5158  function isHTMLElement(node) {
5159    var OwnElement = getWindow(node).HTMLElement;
5160    return node instanceof OwnElement || node instanceof HTMLElement;
5161  }
5162
5163  function isShadowRoot(node) {
5164    // IE 11 has no ShadowRoot
5165    if (typeof ShadowRoot === 'undefined') {
5166      return false;
5167    }
5168
5169    var OwnElement = getWindow(node).ShadowRoot;
5170    return node instanceof OwnElement || node instanceof ShadowRoot;
5171  }
5172
5173  function getHTMLElementScroll(element) {
5174    return {
5175      scrollLeft: element.scrollLeft,
5176      scrollTop: element.scrollTop
5177    };
5178  }
5179
5180  function getNodeScroll(node) {
vendor: 5,124 bytes, lines 5181-5336
5181    if (node === getWindow(node) || !isHTMLElement(node)) {
5182      return getWindowScroll(node);
5183    } else {
5184      return getHTMLElementScroll(node);
5185    }
5186  }
5187
5188  function getNodeName(element) {
5189    return element ? (element.nodeName || '').toLowerCase() : null;
5190  }
5191
5192  function getDocumentElement(element) {
5193    // $FlowFixMe[incompatible-return]: assume body is always available
5194    return ((isElement(element) ? element.ownerDocument : // $FlowFixMe[prop-missing]
5195    element.document) || window.document).documentElement;
5196  }
5197
5198  function getWindowScrollBarX(element) {
5199    // If <html> has a CSS width greater than the viewport, then this will be
5200    // incorrect for RTL.
5201    // Popper 1 is broken in this case and never had a bug report so let's assume
5202    // it's not an issue. I don't think anyone ever specifies width on <html>
5203    // anyway.
5204    // Browsers where the left scrollbar doesn't cause an issue report `0` for
5205    // this (e.g. Edge 2019, IE11, Safari)
5206    return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft;
5207  }
5208
5209  function getComputedStyle(element) {
5210    return getWindow(element).getComputedStyle(element);
5211  }
5212
5213  function isScrollParent(element) {
5214    // Firefox wants us to check `-x` and `-y` variations as well
5215    var _getComputedStyle = getComputedStyle(element),
5216        overflow = _getComputedStyle.overflow,
5217        overflowX = _getComputedStyle.overflowX,
5218        overflowY = _getComputedStyle.overflowY;
5219
5220    return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX);
5221  }
5222
5223  // Composite means it takes into account transforms as well as layout.
5224
5225  function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed) {
5226    if (isFixed === void 0) {
5227      isFixed = false;
5228    }
5229
5230    var documentElement = getDocumentElement(offsetParent);
5231    var rect = getBoundingClientRect(elementOrVirtualElement);
5232    var isOffsetParentAnElement = isHTMLElement(offsetParent);
5233    var scroll = {
5234      scrollLeft: 0,
5235      scrollTop: 0
5236    };
5237    var offsets = {
5238      x: 0,
5239      y: 0
5240    };
5241
5242    if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {
5243      if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078
5244      isScrollParent(documentElement)) {
5245        scroll = getNodeScroll(offsetParent);
5246      }
5247
5248      if (isHTMLElement(offsetParent)) {
5249        offsets = getBoundingClientRect(offsetParent);
5250        offsets.x += offsetParent.clientLeft;
5251        offsets.y += offsetParent.clientTop;
5252      } else if (documentElement) {
5253        offsets.x = getWindowScrollBarX(documentElement);
5254      }
5255    }
5256
5257    return {
5258      x: rect.left + scroll.scrollLeft - offsets.x,
5259      y: rect.top + scroll.scrollTop - offsets.y,
5260      width: rect.width,
5261      height: rect.height
5262    };
5263  }
5264
5265  // means it doesn't take into account transforms.
5266
5267  function getLayoutRect(element) {
5268    var clientRect = getBoundingClientRect(element); // Use the clientRect sizes if it's not been transformed.
5269    // Fixes https://github.com/popperjs/popper-core/issues/1223
5270
5271    var width = element.offsetWidth;
5272    var height = element.offsetHeight;
5273
5274    if (Math.abs(clientRect.width - width) <= 1) {
5275      width = clientRect.width;
5276    }
5277
5278    if (Math.abs(clientRect.height - height) <= 1) {
5279      height = clientRect.height;
5280    }
5281
5282    return {
5283      x: element.offsetLeft,
5284      y: element.offsetTop,
5285      width: width,
5286      height: height
5287    };
5288  }
5289
5290  function getParentNode(element) {
5291    if (getNodeName(element) === 'html') {
5292      return element;
5293    }
5294
5295    return (// this is a quicker (but less type safe) way to save quite some bytes from the bundle
5296      // $FlowFixMe[incompatible-return]
5297      // $FlowFixMe[prop-missing]
5298      element.assignedSlot || // step into the shadow DOM of the parent of a slotted node
5299      element.parentNode || ( // DOM Element detected
5300      isShadowRoot(element) ? element.host : null) || // ShadowRoot detected
5301      // $FlowFixMe[incompatible-call]: HTMLElement is a Node
5302      getDocumentElement(element) // fallback
5303
5304    );
5305  }
5306
5307  function getScrollParent(node) {
5308    if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) {
5309      // $FlowFixMe[incompatible-return]: assume body is always available
5310      return node.ownerDocument.body;
5311    }
5312
5313    if (isHTMLElement(node) && isScrollParent(node)) {
5314      return node;
5315    }
5316
5317    return getScrollParent(getParentNode(node));
5318  }
5319
5320  /*
5321  given a DOM element, return the list of all scroll parents, up the list of ancesors
5322  until we get to the top window object. This list is what we attach scroll listeners
5323  to, because if any of these parent elements scroll, we'll need to re-calculate the
5324  reference element's position.
5325  */
5326
5327  function listScrollParents(element, list) {
5328    var _element$ownerDocumen;
5329
5330    if (list === void 0) {
5331      list = [];
5332    }
5333
5334    var scrollParent = getScrollParent(element);
5335    var isBody = scrollParent === ((_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body);
5336    var win = getWindow(scrollParent);
vendor: 4,110 bytes, lines 5337-5440
5337    var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent;
5338    var updatedList = list.concat(target);
5339    return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here
5340    updatedList.concat(listScrollParents(getParentNode(target)));
5341  }
5342
5343  function isTableElement(element) {
5344    return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0;
5345  }
5346
5347  function getTrueOffsetParent(element) {
5348    if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837
5349    getComputedStyle(element).position === 'fixed') {
5350      return null;
5351    }
5352
5353    return element.offsetParent;
5354  } // `.offsetParent` reports `null` for fixed elements, while absolute elements
5355  // return the containing block
5356
5357
5358  function getContainingBlock(element) {
5359    var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') !== -1;
5360    var isIE = navigator.userAgent.indexOf('Trident') !== -1;
5361
5362    if (isIE && isHTMLElement(element)) {
5363      // In IE 9, 10 and 11 fixed elements containing block is always established by the viewport
5364      var elementCss = getComputedStyle(element);
5365
5366      if (elementCss.position === 'fixed') {
5367        return null;
5368      }
5369    }
5370
5371    var currentNode = getParentNode(element);
5372
5373    while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) {
5374      var css = getComputedStyle(currentNode); // This is non-exhaustive but covers the most common CSS properties that
5375      // create a containing block.
5376      // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
5377
5378      if (css.transform !== 'none' || css.perspective !== 'none' || css.contain === 'paint' || ['transform', 'perspective'].indexOf(css.willChange) !== -1 || isFirefox && css.willChange === 'filter' || isFirefox && css.filter && css.filter !== 'none') {
5379        return currentNode;
5380      } else {
5381        currentNode = currentNode.parentNode;
5382      }
5383    }
5384
5385    return null;
5386  } // Gets the closest ancestor positioned element. Handles some edge cases,
5387  // such as table ancestors and cross browser bugs.
5388
5389
5390  function getOffsetParent(element) {
5391    var window = getWindow(element);
5392    var offsetParent = getTrueOffsetParent(element);
5393
5394    while (offsetParent && isTableElement(offsetParent) && getComputedStyle(offsetParent).position === 'static') {
5395      offsetParent = getTrueOffsetParent(offsetParent);
5396    }
5397
5398    if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle(offsetParent).position === 'static')) {
5399      return window;
5400    }
5401
5402    return offsetParent || getContainingBlock(element) || window;
5403  }
5404
5405  var top = 'top';
5406  var bottom = 'bottom';
5407  var right = 'right';
5408  var left = 'left';
5409  var auto = 'auto';
5410  var basePlacements = [top, bottom, right, left];
5411  var start = 'start';
5412  var end = 'end';
5413  var clippingParents = 'clippingParents';
5414  var viewport = 'viewport';
5415  var popper = 'popper';
5416  var reference = 'reference';
5417  var variationPlacements = /*#__PURE__*/basePlacements.reduce(function (acc, placement) {
5418    return acc.concat([placement + "-" + start, placement + "-" + end]);
5419  }, []);
5420  var placements = /*#__PURE__*/[].concat(basePlacements, [auto]).reduce(function (acc, placement) {
5421    return acc.concat([placement, placement + "-" + start, placement + "-" + end]);
5422  }, []); // modifiers that need to read the DOM
5423
5424  var beforeRead = 'beforeRead';
5425  var read = 'read';
5426  var afterRead = 'afterRead'; // pure-logic modifiers
5427
5428  var beforeMain = 'beforeMain';
5429  var main = 'main';
5430  var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state)
5431
5432  var beforeWrite = 'beforeWrite';
5433  var write = 'write';
5434  var afterWrite = 'afterWrite';
5435  var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite];
5436
5437  function order(modifiers) {
5438    var map = new Map();
5439    var visited = new Set();
5440    var result = [];
vendor: 6,266 bytes, lines 5441-5626
5441    modifiers.forEach(function (modifier) {
5442      map.set(modifier.name, modifier);
5443    }); // On visiting object, check for its dependencies and visit them recursively
5444
5445    function sort(modifier) {
5446      visited.add(modifier.name);
5447      var requires = [].concat(modifier.requires || [], modifier.requiresIfExists || []);
5448      requires.forEach(function (dep) {
5449        if (!visited.has(dep)) {
5450          var depModifier = map.get(dep);
5451
5452          if (depModifier) {
5453            sort(depModifier);
5454          }
5455        }
5456      });
5457      result.push(modifier);
5458    }
5459
5460    modifiers.forEach(function (modifier) {
5461      if (!visited.has(modifier.name)) {
5462        // check for visited object
5463        sort(modifier);
5464      }
5465    });
5466    return result;
5467  }
5468
5469  function orderModifiers(modifiers) {
5470    // order based on dependencies
5471    var orderedModifiers = order(modifiers); // order based on phase
5472
5473    return modifierPhases.reduce(function (acc, phase) {
5474      return acc.concat(orderedModifiers.filter(function (modifier) {
5475        return modifier.phase === phase;
5476      }));
5477    }, []);
5478  }
5479
5480  function debounce(fn) {
5481    var pending;
5482    return function () {
5483      if (!pending) {
5484        pending = new Promise(function (resolve) {
5485          Promise.resolve().then(function () {
5486            pending = undefined;
5487            resolve(fn());
5488          });
5489        });
5490      }
5491
5492      return pending;
5493    };
5494  }
5495
5496  function format(str) {
5497    for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
5498      args[_key - 1] = arguments[_key];
5499    }
5500
5501    return [].concat(args).reduce(function (p, c) {
5502      return p.replace(/%s/, c);
5503    }, str);
5504  }
5505
5506  var INVALID_MODIFIER_ERROR = 'Popper: modifier "%s" provided an invalid %s property, expected %s but got %s';
5507  var MISSING_DEPENDENCY_ERROR = 'Popper: modifier "%s" requires "%s", but "%s" modifier is not available';
5508  var VALID_PROPERTIES = ['name', 'enabled', 'phase', 'fn', 'effect', 'requires', 'options'];
5509  function validateModifiers(modifiers) {
5510    modifiers.forEach(function (modifier) {
5511      Object.keys(modifier).forEach(function (key) {
5512        switch (key) {
5513          case 'name':
5514            if (typeof modifier.name !== 'string') {
5515              console.error(format(INVALID_MODIFIER_ERROR, String(modifier.name), '"name"', '"string"', "\"" + String(modifier.name) + "\""));
5516            }
5517
5518            break;
5519
5520          case 'enabled':
5521            if (typeof modifier.enabled !== 'boolean') {
5522              console.error(format(INVALID_MODIFIER_ERROR, modifier.name, '"enabled"', '"boolean"', "\"" + String(modifier.enabled) + "\""));
5523            }
5524
5525          case 'phase':
5526            if (modifierPhases.indexOf(modifier.phase) < 0) {
5527              console.error(format(INVALID_MODIFIER_ERROR, modifier.name, '"phase"', "either " + modifierPhases.join(', '), "\"" + String(modifier.phase) + "\""));
5528            }
5529
5530            break;
5531
5532          case 'fn':
5533            if (typeof modifier.fn !== 'function') {
5534              console.error(format(INVALID_MODIFIER_ERROR, modifier.name, '"fn"', '"function"', "\"" + String(modifier.fn) + "\""));
5535            }
5536
5537            break;
5538
5539          case 'effect':
5540            if (typeof modifier.effect !== 'function') {
5541              console.error(format(INVALID_MODIFIER_ERROR, modifier.name, '"effect"', '"function"', "\"" + String(modifier.fn) + "\""));
5542            }
5543
5544            break;
5545
5546          case 'requires':
5547            if (!Array.isArray(modifier.requires)) {
5548              console.error(format(INVALID_MODIFIER_ERROR, modifier.name, '"requires"', '"array"', "\"" + String(modifier.requires) + "\""));
5549            }
5550
5551            break;
5552
5553          case 'requiresIfExists':
5554            if (!Array.isArray(modifier.requiresIfExists)) {
5555              console.error(format(INVALID_MODIFIER_ERROR, modifier.name, '"requiresIfExists"', '"array"', "\"" + String(modifier.requiresIfExists) + "\""));
5556            }
5557
5558            break;
5559
5560          case 'options':
5561          case 'data':
5562            break;
5563
5564          default:
5565            console.error("PopperJS: an invalid property has been provided to the \"" + modifier.name + "\" modifier, valid properties are " + VALID_PROPERTIES.map(function (s) {
5566              return "\"" + s + "\"";
5567            }).join(', ') + "; but \"" + key + "\" was provided.");
5568        }
5569
5570        modifier.requires && modifier.requires.forEach(function (requirement) {
5571          if (modifiers.find(function (mod) {
5572            return mod.name === requirement;
5573          }) == null) {
5574            console.error(format(MISSING_DEPENDENCY_ERROR, String(modifier.name), requirement, requirement));
5575          }
5576        });
5577      });
5578    });
5579  }
5580
5581  function uniqueBy(arr, fn) {
5582    var identifiers = new Set();
5583    return arr.filter(function (item) {
5584      var identifier = fn(item);
5585
5586      if (!identifiers.has(identifier)) {
5587        identifiers.add(identifier);
5588        return true;
5589      }
5590    });
5591  }
5592
5593  function getBasePlacement(placement) {
5594    return placement.split('-')[0];
5595  }
5596
5597  function mergeByName(modifiers) {
5598    var merged = modifiers.reduce(function (merged, current) {
5599      var existing = merged[current.name];
5600      merged[current.name] = existing ? Object.assign({}, existing, current, {
5601        options: Object.assign({}, existing.options, current.options),
5602        data: Object.assign({}, existing.data, current.data)
5603      }) : current;
5604      return merged;
5605    }, {}); // IE11 does not support Object.values
5606
5607    return Object.keys(merged).map(function (key) {
5608      return merged[key];
5609    });
5610  }
5611
5612  function getViewportRect(element) {
5613    var win = getWindow(element);
5614    var html = getDocumentElement(element);
5615    var visualViewport = win.visualViewport;
5616    var width = html.clientWidth;
5617    var height = html.clientHeight;
5618    var x = 0;
5619    var y = 0; // NB: This isn't supported on iOS <= 12. If the keyboard is open, the popper
5620    // can be obscured underneath it.
5621    // Also, `html.clientHeight` adds the bottom bar height in Safari iOS, even
5622    // if it isn't open, so if this isn't available, the popper will be detected
5623    // to overflow the bottom of the screen too early.
5624
5625    if (visualViewport) {
5626      width = visualViewport.width;
vendor: 4,327 bytes, lines 5627-5747
5627      height = visualViewport.height; // Uses Layout Viewport (like Chrome; Safari does not currently)
5628      // In Chrome, it returns a value very close to 0 (+/-) but contains rounding
5629      // errors due to floating point numbers, so we need to check precision.
5630      // Safari returns a number <= 0, usually < -1 when pinch-zoomed
5631      // Feature detection fails in mobile emulation mode in Chrome.
5632      // Math.abs(win.innerWidth / visualViewport.scale - visualViewport.width) <
5633      // 0.001
5634      // Fallback here: "Not Safari" userAgent
5635
5636      if (!/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
5637        x = visualViewport.offsetLeft;
5638        y = visualViewport.offsetTop;
5639      }
5640    }
5641
5642    return {
5643      width: width,
5644      height: height,
5645      x: x + getWindowScrollBarX(element),
5646      y: y
5647    };
5648  }
5649
5650  var max = Math.max;
5651  var min = Math.min;
5652  var round = Math.round;
5653
5654  // of the `<html>` and `<body>` rect bounds if horizontally scrollable
5655
5656  function getDocumentRect(element) {
5657    var _element$ownerDocumen;
5658
5659    var html = getDocumentElement(element);
5660    var winScroll = getWindowScroll(element);
5661    var body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body;
5662    var width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0);
5663    var height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0);
5664    var x = -winScroll.scrollLeft + getWindowScrollBarX(element);
5665    var y = -winScroll.scrollTop;
5666
5667    if (getComputedStyle(body || html).direction === 'rtl') {
5668      x += max(html.clientWidth, body ? body.clientWidth : 0) - width;
5669    }
5670
5671    return {
5672      width: width,
5673      height: height,
5674      x: x,
5675      y: y
5676    };
5677  }
5678
5679  function contains(parent, child) {
5680    var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method
5681
5682    if (parent.contains(child)) {
5683      return true;
5684    } // then fallback to custom implementation with Shadow DOM support
5685    else if (rootNode && isShadowRoot(rootNode)) {
5686        var next = child;
5687
5688        do {
5689          if (next && parent.isSameNode(next)) {
5690            return true;
5691          } // $FlowFixMe[prop-missing]: need a better way to handle this...
5692
5693
5694          next = next.parentNode || next.host;
5695        } while (next);
5696      } // Give up, the result is false
5697
5698
5699    return false;
5700  }
5701
5702  function rectToClientRect(rect) {
5703    return Object.assign({}, rect, {
5704      left: rect.x,
5705      top: rect.y,
5706      right: rect.x + rect.width,
5707      bottom: rect.y + rect.height
5708    });
5709  }
5710
5711  function getInnerBoundingClientRect(element) {
5712    var rect = getBoundingClientRect(element);
5713    rect.top = rect.top + element.clientTop;
5714    rect.left = rect.left + element.clientLeft;
5715    rect.bottom = rect.top + element.clientHeight;
5716    rect.right = rect.left + element.clientWidth;
5717    rect.width = element.clientWidth;
5718    rect.height = element.clientHeight;
5719    rect.x = rect.left;
5720    rect.y = rect.top;
5721    return rect;
5722  }
5723
5724  function getClientRectFromMixedType(element, clippingParent) {
5725    return clippingParent === viewport ? rectToClientRect(getViewportRect(element)) : isHTMLElement(clippingParent) ? getInnerBoundingClientRect(clippingParent) : rectToClientRect(getDocumentRect(getDocumentElement(element)));
5726  } // A "clipping parent" is an overflowable container with the characteristic of
5727  // clipping (or hiding) overflowing elements with a position different from
5728  // `initial`
5729
5730
5731  function getClippingParents(element) {
5732    var clippingParents = listScrollParents(getParentNode(element));
5733    var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle(element).position) >= 0;
5734    var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element;
5735
5736    if (!isElement(clipperElement)) {
5737      return [];
5738    } // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414
5739
5740
5741    return clippingParents.filter(function (clippingParent) {
5742      return isElement(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body';
5743    });
5744  } // Gets the maximum area that the element is visible in due to any number of
5745  // clipping parents
5746
5747
vendor: 4,117 bytes, lines 5748-5877
5748  function getClippingRect(element, boundary, rootBoundary) {
5749    var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary);
5750    var clippingParents = [].concat(mainClippingParents, [rootBoundary]);
5751    var firstClippingParent = clippingParents[0];
5752    var clippingRect = clippingParents.reduce(function (accRect, clippingParent) {
5753      var rect = getClientRectFromMixedType(element, clippingParent);
5754      accRect.top = max(rect.top, accRect.top);
5755      accRect.right = min(rect.right, accRect.right);
5756      accRect.bottom = min(rect.bottom, accRect.bottom);
5757      accRect.left = max(rect.left, accRect.left);
5758      return accRect;
5759    }, getClientRectFromMixedType(element, firstClippingParent));
5760    clippingRect.width = clippingRect.right - clippingRect.left;
5761    clippingRect.height = clippingRect.bottom - clippingRect.top;
5762    clippingRect.x = clippingRect.left;
5763    clippingRect.y = clippingRect.top;
5764    return clippingRect;
5765  }
5766
5767  function getVariation(placement) {
5768    return placement.split('-')[1];
5769  }
5770
5771  function getMainAxisFromPlacement(placement) {
5772    return ['top', 'bottom'].indexOf(placement) >= 0 ? 'x' : 'y';
5773  }
5774
5775  function computeOffsets(_ref) {
5776    var reference = _ref.reference,
5777        element = _ref.element,
5778        placement = _ref.placement;
5779    var basePlacement = placement ? getBasePlacement(placement) : null;
5780    var variation = placement ? getVariation(placement) : null;
5781    var commonX = reference.x + reference.width / 2 - element.width / 2;
5782    var commonY = reference.y + reference.height / 2 - element.height / 2;
5783    var offsets;
5784
5785    switch (basePlacement) {
5786      case top:
5787        offsets = {
5788          x: commonX,
5789          y: reference.y - element.height
5790        };
5791        break;
5792
5793      case bottom:
5794        offsets = {
5795          x: commonX,
5796          y: reference.y + reference.height
5797        };
5798        break;
5799
5800      case right:
5801        offsets = {
5802          x: reference.x + reference.width,
5803          y: commonY
5804        };
5805        break;
5806
5807      case left:
5808        offsets = {
5809          x: reference.x - element.width,
5810          y: commonY
5811        };
5812        break;
5813
5814      default:
5815        offsets = {
5816          x: reference.x,
5817          y: reference.y
5818        };
5819    }
5820
5821    var mainAxis = basePlacement ? getMainAxisFromPlacement(basePlacement) : null;
5822
5823    if (mainAxis != null) {
5824      var len = mainAxis === 'y' ? 'height' : 'width';
5825
5826      switch (variation) {
5827        case start:
5828          offsets[mainAxis] = offsets[mainAxis] - (reference[len] / 2 - element[len] / 2);
5829          break;
5830
5831        case end:
5832          offsets[mainAxis] = offsets[mainAxis] + (reference[len] / 2 - element[len] / 2);
5833          break;
5834      }
5835    }
5836
5837    return offsets;
5838  }
5839
5840  function getFreshSideObject() {
5841    return {
5842      top: 0,
5843      right: 0,
5844      bottom: 0,
5845      left: 0
5846    };
5847  }
5848
5849  function mergePaddingObject(paddingObject) {
5850    return Object.assign({}, getFreshSideObject(), paddingObject);
5851  }
5852
5853  function expandToHashMap(value, keys) {
5854    return keys.reduce(function (hashMap, key) {
5855      hashMap[key] = value;
5856      return hashMap;
5857    }, {});
5858  }
5859
5860  function detectOverflow(state, options) {
5861    if (options === void 0) {
5862      options = {};
5863    }
5864
5865    var _options = options,
5866        _options$placement = _options.placement,
5867        placement = _options$placement === void 0 ? state.placement : _options$placement,
5868        _options$boundary = _options.boundary,
5869        boundary = _options$boundary === void 0 ? clippingParents : _options$boundary,
5870        _options$rootBoundary = _options.rootBoundary,
5871        rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary,
5872        _options$elementConte = _options.elementContext,
5873        elementContext = _options$elementConte === void 0 ? popper : _options$elementConte,
5874        _options$altBoundary = _options.altBoundary,
5875        altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary,
5876        _options$padding = _options.padding,
5877        padding = _options$padding === void 0 ? 0 : _options$padding;
vendor: 4,535 bytes, lines 5878-5977
5878    var paddingObject = mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));
5879    var altContext = elementContext === popper ? reference : popper;
5880    var referenceElement = state.elements.reference;
5881    var popperRect = state.rects.popper;
5882    var element = state.elements[altBoundary ? altContext : elementContext];
5883    var clippingClientRect = getClippingRect(isElement(element) ? element : element.contextElement || getDocumentElement(state.elements.popper), boundary, rootBoundary);
5884    var referenceClientRect = getBoundingClientRect(referenceElement);
5885    var popperOffsets = computeOffsets({
5886      reference: referenceClientRect,
5887      element: popperRect,
5888      strategy: 'absolute',
5889      placement: placement
5890    });
5891    var popperClientRect = rectToClientRect(Object.assign({}, popperRect, popperOffsets));
5892    var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect; // positive = overflowing the clipping rect
5893    // 0 or negative = within the clipping rect
5894
5895    var overflowOffsets = {
5896      top: clippingClientRect.top - elementClientRect.top + paddingObject.top,
5897      bottom: elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom,
5898      left: clippingClientRect.left - elementClientRect.left + paddingObject.left,
5899      right: elementClientRect.right - clippingClientRect.right + paddingObject.right
5900    };
5901    var offsetData = state.modifiersData.offset; // Offsets can be applied only to the popper element
5902
5903    if (elementContext === popper && offsetData) {
5904      var offset = offsetData[placement];
5905      Object.keys(overflowOffsets).forEach(function (key) {
5906        var multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1;
5907        var axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x';
5908        overflowOffsets[key] += offset[axis] * multiply;
5909      });
5910    }
5911
5912    return overflowOffsets;
5913  }
5914
5915  var INVALID_ELEMENT_ERROR = 'Popper: Invalid reference or popper argument provided. They must be either a DOM element or virtual element.';
5916  var INFINITE_LOOP_ERROR = 'Popper: An infinite loop in the modifiers cycle has been detected! The cycle has been interrupted to prevent a browser crash.';
5917  var DEFAULT_OPTIONS = {
5918    placement: 'bottom',
5919    modifiers: [],
5920    strategy: 'absolute'
5921  };
5922
5923  function areValidElements() {
5924    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
5925      args[_key] = arguments[_key];
5926    }
5927
5928    return !args.some(function (element) {
5929      return !(element && typeof element.getBoundingClientRect === 'function');
5930    });
5931  }
5932
5933  function popperGenerator(generatorOptions) {
5934    if (generatorOptions === void 0) {
5935      generatorOptions = {};
5936    }
5937
5938    var _generatorOptions = generatorOptions,
5939        _generatorOptions$def = _generatorOptions.defaultModifiers,
5940        defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def,
5941        _generatorOptions$def2 = _generatorOptions.defaultOptions,
5942        defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2;
5943    return function createPopper(reference, popper, options) {
5944      if (options === void 0) {
5945        options = defaultOptions;
5946      }
5947
5948      var state = {
5949        placement: 'bottom',
5950        orderedModifiers: [],
5951        options: Object.assign({}, DEFAULT_OPTIONS, defaultOptions),
5952        modifiersData: {},
5953        elements: {
5954          reference: reference,
5955          popper: popper
5956        },
5957        attributes: {},
5958        styles: {}
5959      };
5960      var effectCleanupFns = [];
5961      var isDestroyed = false;
5962      var instance = {
5963        state: state,
5964        setOptions: function setOptions(options) {
5965          cleanupModifierEffects();
5966          state.options = Object.assign({}, defaultOptions, state.options, options);
5967          state.scrollParents = {
5968            reference: isElement(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [],
5969            popper: listScrollParents(popper)
5970          }; // Orders the modifiers based on their dependencies and `phase`
5971          // properties
5972
5973          var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers
5974
5975          state.orderedModifiers = orderedModifiers.filter(function (m) {
5976            return m.enabled;
5977          }); // Validate the provided modifiers so that the consumer will get warned
vendor: 4,110 bytes, lines 5978-6071
5978          // if one of the modifiers is invalid for any reason
5979
5980          {
5981            var modifiers = uniqueBy([].concat(orderedModifiers, state.options.modifiers), function (_ref) {
5982              var name = _ref.name;
5983              return name;
5984            });
5985            validateModifiers(modifiers);
5986
5987            if (getBasePlacement(state.options.placement) === auto) {
5988              var flipModifier = state.orderedModifiers.find(function (_ref2) {
5989                var name = _ref2.name;
5990                return name === 'flip';
5991              });
5992
5993              if (!flipModifier) {
5994                console.error(['Popper: "auto" placements require the "flip" modifier be', 'present and enabled to work.'].join(' '));
5995              }
5996            }
5997
5998            var _getComputedStyle = getComputedStyle(popper),
5999                marginTop = _getComputedStyle.marginTop,
6000                marginRight = _getComputedStyle.marginRight,
6001                marginBottom = _getComputedStyle.marginBottom,
6002                marginLeft = _getComputedStyle.marginLeft; // We no longer take into account `margins` on the popper, and it can
6003            // cause bugs with positioning, so we'll warn the consumer
6004
6005
6006            if ([marginTop, marginRight, marginBottom, marginLeft].some(function (margin) {
6007              return parseFloat(margin);
6008            })) {
6009              console.warn(['Popper: CSS "margin" styles cannot be used to apply padding', 'between the popper and its reference element or boundary.', 'To replicate margin, use the `offset` modifier, as well as', 'the `padding` option in the `preventOverflow` and `flip`', 'modifiers.'].join(' '));
6010            }
6011          }
6012
6013          runModifierEffects();
6014          return instance.update();
6015        },
6016        // Sync update – it will always be executed, even if not necessary. This
6017        // is useful for low frequency updates where sync behavior simplifies the
6018        // logic.
6019        // For high frequency updates (e.g. `resize` and `scroll` events), always
6020        // prefer the async Popper#update method
6021        forceUpdate: function forceUpdate() {
6022          if (isDestroyed) {
6023            return;
6024          }
6025
6026          var _state$elements = state.elements,
6027              reference = _state$elements.reference,
6028              popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements
6029          // anymore
6030
6031          if (!areValidElements(reference, popper)) {
6032            {
6033              console.error(INVALID_ELEMENT_ERROR);
6034            }
6035
6036            return;
6037          } // Store the reference and popper rects to be read by modifiers
6038
6039
6040          state.rects = {
6041            reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'),
6042            popper: getLayoutRect(popper)
6043          }; // Modifiers have the ability to reset the current update cycle. The
6044          // most common use case for this is the `flip` modifier changing the
6045          // placement, which then needs to re-run all the modifiers, because the
6046          // logic was previously ran for the previous placement and is therefore
6047          // stale/incorrect
6048
6049          state.reset = false;
6050          state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier
6051          // is filled with the initial data specified by the modifier. This means
6052          // it doesn't persist and is fresh on each update.
6053          // To ensure persistent data, use `${name}#persistent`
6054
6055          state.orderedModifiers.forEach(function (modifier) {
6056            return state.modifiersData[modifier.name] = Object.assign({}, modifier.data);
6057          });
6058          var __debug_loops__ = 0;
6059
6060          for (var index = 0; index < state.orderedModifiers.length; index++) {
6061            {
6062              __debug_loops__ += 1;
6063
6064              if (__debug_loops__ > 100) {
6065                console.error(INFINITE_LOOP_ERROR);
6066                break;
6067              }
6068            }
6069
6070            if (state.reset === true) {
6071              state.reset = false;
vendor: 6,095 bytes, lines 6072-6275
6072              index = -1;
6073              continue;
6074            }
6075
6076            var _state$orderedModifie = state.orderedModifiers[index],
6077                fn = _state$orderedModifie.fn,
6078                _state$orderedModifie2 = _state$orderedModifie.options,
6079                _options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2,
6080                name = _state$orderedModifie.name;
6081
6082            if (typeof fn === 'function') {
6083              state = fn({
6084                state: state,
6085                options: _options,
6086                name: name,
6087                instance: instance
6088              }) || state;
6089            }
6090          }
6091        },
6092        // Async and optimistically optimized update – it will not be executed if
6093        // not necessary (debounced to run at most once-per-tick)
6094        update: debounce(function () {
6095          return new Promise(function (resolve) {
6096            instance.forceUpdate();
6097            resolve(state);
6098          });
6099        }),
6100        destroy: function destroy() {
6101          cleanupModifierEffects();
6102          isDestroyed = true;
6103        }
6104      };
6105
6106      if (!areValidElements(reference, popper)) {
6107        {
6108          console.error(INVALID_ELEMENT_ERROR);
6109        }
6110
6111        return instance;
6112      }
6113
6114      instance.setOptions(options).then(function (state) {
6115        if (!isDestroyed && options.onFirstUpdate) {
6116          options.onFirstUpdate(state);
6117        }
6118      }); // Modifiers have the ability to execute arbitrary code before the first
6119      // update cycle runs. They will be executed in the same order as the update
6120      // cycle. This is useful when a modifier adds some persistent data that
6121      // other modifiers need to use, but the modifier is run after the dependent
6122      // one.
6123
6124      function runModifierEffects() {
6125        state.orderedModifiers.forEach(function (_ref3) {
6126          var name = _ref3.name,
6127              _ref3$options = _ref3.options,
6128              options = _ref3$options === void 0 ? {} : _ref3$options,
6129              effect = _ref3.effect;
6130
6131          if (typeof effect === 'function') {
6132            var cleanupFn = effect({
6133              state: state,
6134              name: name,
6135              instance: instance,
6136              options: options
6137            });
6138
6139            var noopFn = function noopFn() {};
6140
6141            effectCleanupFns.push(cleanupFn || noopFn);
6142          }
6143        });
6144      }
6145
6146      function cleanupModifierEffects() {
6147        effectCleanupFns.forEach(function (fn) {
6148          return fn();
6149        });
6150        effectCleanupFns = [];
6151      }
6152
6153      return instance;
6154    };
6155  }
6156
6157  var passive = {
6158    passive: true
6159  };
6160
6161  function effect$2(_ref) {
6162    var state = _ref.state,
6163        instance = _ref.instance,
6164        options = _ref.options;
6165    var _options$scroll = options.scroll,
6166        scroll = _options$scroll === void 0 ? true : _options$scroll,
6167        _options$resize = options.resize,
6168        resize = _options$resize === void 0 ? true : _options$resize;
6169    var window = getWindow(state.elements.popper);
6170    var scrollParents = [].concat(state.scrollParents.reference, state.scrollParents.popper);
6171
6172    if (scroll) {
6173      scrollParents.forEach(function (scrollParent) {
6174        scrollParent.addEventListener('scroll', instance.update, passive);
6175      });
6176    }
6177
6178    if (resize) {
6179      window.addEventListener('resize', instance.update, passive);
6180    }
6181
6182    return function () {
6183      if (scroll) {
6184        scrollParents.forEach(function (scrollParent) {
6185          scrollParent.removeEventListener('scroll', instance.update, passive);
6186        });
6187      }
6188
6189      if (resize) {
6190        window.removeEventListener('resize', instance.update, passive);
6191      }
6192    };
6193  } // eslint-disable-next-line import/no-unused-modules
6194
6195
6196  var eventListeners = {
6197    name: 'eventListeners',
6198    enabled: true,
6199    phase: 'write',
6200    fn: function fn() {},
6201    effect: effect$2,
6202    data: {}
6203  };
6204
6205  function popperOffsets(_ref) {
6206    var state = _ref.state,
6207        name = _ref.name;
6208    // Offsets are the actual position the popper needs to have to be
6209    // properly positioned near its reference element
6210    // This is the most basic placement, and will be adjusted by
6211    // the modifiers in the next step
6212    state.modifiersData[name] = computeOffsets({
6213      reference: state.rects.reference,
6214      element: state.rects.popper,
6215      strategy: 'absolute',
6216      placement: state.placement
6217    });
6218  } // eslint-disable-next-line import/no-unused-modules
6219
6220
6221  var popperOffsets$1 = {
6222    name: 'popperOffsets',
6223    enabled: true,
6224    phase: 'read',
6225    fn: popperOffsets,
6226    data: {}
6227  };
6228
6229  var unsetSides = {
6230    top: 'auto',
6231    right: 'auto',
6232    bottom: 'auto',
6233    left: 'auto'
6234  }; // Round the offsets to the nearest suitable subpixel based on the DPR.
6235  // Zooming can change the DPR, but it seems to report a value that will
6236  // cleanly divide the values into the appropriate subpixels.
6237
6238  function roundOffsetsByDPR(_ref) {
6239    var x = _ref.x,
6240        y = _ref.y;
6241    var win = window;
6242    var dpr = win.devicePixelRatio || 1;
6243    return {
6244      x: round(round(x * dpr) / dpr) || 0,
6245      y: round(round(y * dpr) / dpr) || 0
6246    };
6247  }
6248
6249  function mapToStyles(_ref2) {
6250    var _Object$assign2;
6251
6252    var popper = _ref2.popper,
6253        popperRect = _ref2.popperRect,
6254        placement = _ref2.placement,
6255        offsets = _ref2.offsets,
6256        position = _ref2.position,
6257        gpuAcceleration = _ref2.gpuAcceleration,
6258        adaptive = _ref2.adaptive,
6259        roundOffsets = _ref2.roundOffsets;
6260
6261    var _ref3 = roundOffsets === true ? roundOffsetsByDPR(offsets) : typeof roundOffsets === 'function' ? roundOffsets(offsets) : offsets,
6262        _ref3$x = _ref3.x,
6263        x = _ref3$x === void 0 ? 0 : _ref3$x,
6264        _ref3$y = _ref3.y,
6265        y = _ref3$y === void 0 ? 0 : _ref3$y;
6266
6267    var hasX = offsets.hasOwnProperty('x');
6268    var hasY = offsets.hasOwnProperty('y');
6269    var sideX = left;
6270    var sideY = top;
6271    var win = window;
6272
6273    if (adaptive) {
6274      var offsetParent = getOffsetParent(popper);
6275      var heightProp = 'clientHeight';
6276      var widthProp = 'clientWidth';
6277
6278      if (offsetParent === getWindow(popper)) {
6279        offsetParent = getDocumentElement(popper);
6280
6281        if (getComputedStyle(offsetParent).position !== 'static') {
6282          heightProp = 'scrollHeight';
6283          widthProp = 'scrollWidth';
6284        }
6285      } // $FlowFixMe[incompatible-cast]: force type refinement, we compare offsetParent with window above, but Flow doesn't detect it
6286
6287
6288      offsetParent = offsetParent;
6289
6290      if (placement === top) {
6291        sideY = bottom; // $FlowFixMe[prop-missing]
6292
6293        y -= offsetParent[heightProp] - popperRect.height;
6294        y *= gpuAcceleration ? 1 : -1;
6295      }
6296
6297      if (placement === left) {
6298        sideX = right; // $FlowFixMe[prop-missing]
6299
6300        x -= offsetParent[widthProp] - popperRect.width;
6301        x *= gpuAcceleration ? 1 : -1;
6302      }
6303    }
6304
6305    var commonStyles = Object.assign({
6306      position: position
6307    }, adaptive && unsetSides);
6308
6309    if (gpuAcceleration) {
6310      var _Object$assign;
6311
6312      return Object.assign({}, commonStyles, (_Object$assign = {}, _Object$assign[sideY] = hasY ? '0' : '', _Object$assign[sideX] = hasX ? '0' : '', _Object$assign.transform = (win.devicePixelRatio || 1) < 2 ? "translate(" + x + "px, " + y + "px)" : "translate3d(" + x + "px, " + y + "px, 0)", _Object$assign));
6313    }
6314
6315    return Object.assign({}, commonStyles, (_Object$assign2 = {}, _Object$assign2[sideY] = hasY ? y + "px" : '', _Object$assign2[sideX] = hasX ? x + "px" : '', _Object$assign2.transform = '', _Object$assign2));
6316  }
6317
6318  function computeStyles(_ref4) {
6319    var state = _ref4.state,
6320        options = _ref4.options;
6321    var _options$gpuAccelerat = options.gpuAcceleration,
6322        gpuAcceleration = _options$gpuAccelerat === void 0 ? true : _options$gpuAccelerat,
6323        _options$adaptive = options.adaptive,
6324        adaptive = _options$adaptive === void 0 ? true : _options$adaptive,
6325        _options$roundOffsets = options.roundOffsets,
6326        roundOffsets = _options$roundOffsets === void 0 ? true : _options$roundOffsets;
6327
6328    {
6329      var transitionProperty = getComputedStyle(state.elements.popper).transitionProperty || '';
6330
6331      if (adaptive && ['transform', 'top', 'right', 'bottom', 'left'].some(function (property) {
6332        return transitionProperty.indexOf(property) >= 0;
6333      })) {
6334        console.warn(['Popper: Detected CSS transitions on at least one of the following', 'CSS properties: "transform", "top", "right", "bottom", "left".', '\n\n', 'Disable the "computeStyles" modifier\'s `adaptive` option to allow', 'for smooth transitions, or remove these properties from the CSS', 'transition declaration on the popper element if only transitioning', 'opacity or background-color for example.', '\n\n', 'We recommend using the popper element as a wrapper around an inner', 'element that can have any CSS property transitioned for animations.'].join(' '));
6335      }
6336    }
6337
6338    var commonStyles = {
6339      placement: getBasePlacement(state.placement),
6340      popper: state.elements.popper,
6341      popperRect: state.rects.popper,
6342      gpuAcceleration: gpuAcceleration
6343    };
6344
6345    if (state.modifiersData.popperOffsets != null) {
6346      state.styles.popper = Object.assign({}, state.styles.popper, mapToStyles(Object.assign({}, commonStyles, {
6347        offsets: state.modifiersData.popperOffsets,
6348        position: state.options.strategy,
6349        adaptive: adaptive,
6350        roundOffsets: roundOffsets
6351      })));
6352    }
6353
6354    if (state.modifiersData.arrow != null) {
6355      state.styles.arrow = Object.assign({}, state.styles.arrow, mapToStyles(Object.assign({}, commonStyles, {
6356        offsets: state.modifiersData.arrow,
6357        position: 'absolute',
6358        adaptive: false,
6359        roundOffsets: roundOffsets
6360      })));
6361    }
6362
6363    state.attributes.popper = Object.assign({}, state.attributes.popper, {
6364      'data-popper-placement': state.placement
6365    });
6366  } // eslint-disable-next-line import/no-unused-modules
6367
6368
6369  var computeStyles$1 = {
6370    name: 'computeStyles',
6371    enabled: true,
6372    phase: 'beforeWrite',
6373    fn: computeStyles,
6374    data: {}
6375  };
6376
6377  // and applies them to the HTMLElements such as popper and arrow
6378
6379  function applyStyles(_ref) {
6380    var state = _ref.state;
vendor: 8,677 bytes, lines 6381-6635
6381    Object.keys(state.elements).forEach(function (name) {
6382      var style = state.styles[name] || {};
6383      var attributes = state.attributes[name] || {};
6384      var element = state.elements[name]; // arrow is optional + virtual elements
6385
6386      if (!isHTMLElement(element) || !getNodeName(element)) {
6387        return;
6388      } // Flow doesn't support to extend this property, but it's the most
6389      // effective way to apply styles to an HTMLElement
6390      // $FlowFixMe[cannot-write]
6391
6392
6393      Object.assign(element.style, style);
6394      Object.keys(attributes).forEach(function (name) {
6395        var value = attributes[name];
6396
6397        if (value === false) {
6398          element.removeAttribute(name);
6399        } else {
6400          element.setAttribute(name, value === true ? '' : value);
6401        }
6402      });
6403    });
6404  }
6405
6406  function effect$1(_ref2) {
6407    var state = _ref2.state;
6408    var initialStyles = {
6409      popper: {
6410        position: state.options.strategy,
6411        left: '0',
6412        top: '0',
6413        margin: '0'
6414      },
6415      arrow: {
6416        position: 'absolute'
6417      },
6418      reference: {}
6419    };
6420    Object.assign(state.elements.popper.style, initialStyles.popper);
6421    state.styles = initialStyles;
6422
6423    if (state.elements.arrow) {
6424      Object.assign(state.elements.arrow.style, initialStyles.arrow);
6425    }
6426
6427    return function () {
6428      Object.keys(state.elements).forEach(function (name) {
6429        var element = state.elements[name];
6430        var attributes = state.attributes[name] || {};
6431        var styleProperties = Object.keys(state.styles.hasOwnProperty(name) ? state.styles[name] : initialStyles[name]); // Set all values to an empty string to unset them
6432
6433        var style = styleProperties.reduce(function (style, property) {
6434          style[property] = '';
6435          return style;
6436        }, {}); // arrow is optional + virtual elements
6437
6438        if (!isHTMLElement(element) || !getNodeName(element)) {
6439          return;
6440        }
6441
6442        Object.assign(element.style, style);
6443        Object.keys(attributes).forEach(function (attribute) {
6444          element.removeAttribute(attribute);
6445        });
6446      });
6447    };
6448  } // eslint-disable-next-line import/no-unused-modules
6449
6450
6451  var applyStyles$1 = {
6452    name: 'applyStyles',
6453    enabled: true,
6454    phase: 'write',
6455    fn: applyStyles,
6456    effect: effect$1,
6457    requires: ['computeStyles']
6458  };
6459
6460  function distanceAndSkiddingToXY(placement, rects, offset) {
6461    var basePlacement = getBasePlacement(placement);
6462    var invertDistance = [left, top].indexOf(basePlacement) >= 0 ? -1 : 1;
6463
6464    var _ref = typeof offset === 'function' ? offset(Object.assign({}, rects, {
6465      placement: placement
6466    })) : offset,
6467        skidding = _ref[0],
6468        distance = _ref[1];
6469
6470    skidding = skidding || 0;
6471    distance = (distance || 0) * invertDistance;
6472    return [left, right].indexOf(basePlacement) >= 0 ? {
6473      x: distance,
6474      y: skidding
6475    } : {
6476      x: skidding,
6477      y: distance
6478    };
6479  }
6480
6481  function offset(_ref2) {
6482    var state = _ref2.state,
6483        options = _ref2.options,
6484        name = _ref2.name;
6485    var _options$offset = options.offset,
6486        offset = _options$offset === void 0 ? [0, 0] : _options$offset;
6487    var data = placements.reduce(function (acc, placement) {
6488      acc[placement] = distanceAndSkiddingToXY(placement, state.rects, offset);
6489      return acc;
6490    }, {});
6491    var _data$state$placement = data[state.placement],
6492        x = _data$state$placement.x,
6493        y = _data$state$placement.y;
6494
6495    if (state.modifiersData.popperOffsets != null) {
6496      state.modifiersData.popperOffsets.x += x;
6497      state.modifiersData.popperOffsets.y += y;
6498    }
6499
6500    state.modifiersData[name] = data;
6501  } // eslint-disable-next-line import/no-unused-modules
6502
6503
6504  var offset$1 = {
6505    name: 'offset',
6506    enabled: true,
6507    phase: 'main',
6508    requires: ['popperOffsets'],
6509    fn: offset
6510  };
6511
6512  var hash$1 = {
6513    left: 'right',
6514    right: 'left',
6515    bottom: 'top',
6516    top: 'bottom'
6517  };
6518  function getOppositePlacement(placement) {
6519    return placement.replace(/left|right|bottom|top/g, function (matched) {
6520      return hash$1[matched];
6521    });
6522  }
6523
6524  var hash = {
6525    start: 'end',
6526    end: 'start'
6527  };
6528  function getOppositeVariationPlacement(placement) {
6529    return placement.replace(/start|end/g, function (matched) {
6530      return hash[matched];
6531    });
6532  }
6533
6534  function computeAutoPlacement(state, options) {
6535    if (options === void 0) {
6536      options = {};
6537    }
6538
6539    var _options = options,
6540        placement = _options.placement,
6541        boundary = _options.boundary,
6542        rootBoundary = _options.rootBoundary,
6543        padding = _options.padding,
6544        flipVariations = _options.flipVariations,
6545        _options$allowedAutoP = _options.allowedAutoPlacements,
6546        allowedAutoPlacements = _options$allowedAutoP === void 0 ? placements : _options$allowedAutoP;
6547    var variation = getVariation(placement);
6548    var placements$1 = variation ? flipVariations ? variationPlacements : variationPlacements.filter(function (placement) {
6549      return getVariation(placement) === variation;
6550    }) : basePlacements;
6551    var allowedPlacements = placements$1.filter(function (placement) {
6552      return allowedAutoPlacements.indexOf(placement) >= 0;
6553    });
6554
6555    if (allowedPlacements.length === 0) {
6556      allowedPlacements = placements$1;
6557
6558      {
6559        console.error(['Popper: The `allowedAutoPlacements` option did not allow any', 'placements. Ensure the `placement` option matches the variation', 'of the allowed placements.', 'For example, "auto" cannot be used to allow "bottom-start".', 'Use "auto-start" instead.'].join(' '));
6560      }
6561    } // $FlowFixMe[incompatible-type]: Flow seems to have problems with two array unions...
6562
6563
6564    var overflows = allowedPlacements.reduce(function (acc, placement) {
6565      acc[placement] = detectOverflow(state, {
6566        placement: placement,
6567        boundary: boundary,
6568        rootBoundary: rootBoundary,
6569        padding: padding
6570      })[getBasePlacement(placement)];
6571      return acc;
6572    }, {});
6573    return Object.keys(overflows).sort(function (a, b) {
6574      return overflows[a] - overflows[b];
6575    });
6576  }
6577
6578  function getExpandedFallbackPlacements(placement) {
6579    if (getBasePlacement(placement) === auto) {
6580      return [];
6581    }
6582
6583    var oppositePlacement = getOppositePlacement(placement);
6584    return [getOppositeVariationPlacement(placement), oppositePlacement, getOppositeVariationPlacement(oppositePlacement)];
6585  }
6586
6587  function flip(_ref) {
6588    var state = _ref.state,
6589        options = _ref.options,
6590        name = _ref.name;
6591
6592    if (state.modifiersData[name]._skip) {
6593      return;
6594    }
6595
6596    var _options$mainAxis = options.mainAxis,
6597        checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,
6598        _options$altAxis = options.altAxis,
6599        checkAltAxis = _options$altAxis === void 0 ? true : _options$altAxis,
6600        specifiedFallbackPlacements = options.fallbackPlacements,
6601        padding = options.padding,
6602        boundary = options.boundary,
6603        rootBoundary = options.rootBoundary,
6604        altBoundary = options.altBoundary,
6605        _options$flipVariatio = options.flipVariations,
6606        flipVariations = _options$flipVariatio === void 0 ? true : _options$flipVariatio,
6607        allowedAutoPlacements = options.allowedAutoPlacements;
6608    var preferredPlacement = state.options.placement;
6609    var basePlacement = getBasePlacement(preferredPlacement);
6610    var isBasePlacement = basePlacement === preferredPlacement;
6611    var fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipVariations ? [getOppositePlacement(preferredPlacement)] : getExpandedFallbackPlacements(preferredPlacement));
6612    var placements = [preferredPlacement].concat(fallbackPlacements).reduce(function (acc, placement) {
6613      return acc.concat(getBasePlacement(placement) === auto ? computeAutoPlacement(state, {
6614        placement: placement,
6615        boundary: boundary,
6616        rootBoundary: rootBoundary,
6617        padding: padding,
6618        flipVariations: flipVariations,
6619        allowedAutoPlacements: allowedAutoPlacements
6620      }) : placement);
6621    }, []);
6622    var referenceRect = state.rects.reference;
6623    var popperRect = state.rects.popper;
6624    var checksMap = new Map();
6625    var makeFallbackChecks = true;
6626    var firstFittingPlacement = placements[0];
6627
6628    for (var i = 0; i < placements.length; i++) {
6629      var placement = placements[i];
6630
6631      var _basePlacement = getBasePlacement(placement);
6632
6633      var isStartVariation = getVariation(placement) === start;
6634      var isVertical = [top, bottom].indexOf(_basePlacement) >= 0;
6635      var len = isVertical ? 'width' : 'height';
vendor: 9,084 bytes, lines 6636-6874
6636      var overflow = detectOverflow(state, {
6637        placement: placement,
6638        boundary: boundary,
6639        rootBoundary: rootBoundary,
6640        altBoundary: altBoundary,
6641        padding: padding
6642      });
6643      var mainVariationSide = isVertical ? isStartVariation ? right : left : isStartVariation ? bottom : top;
6644
6645      if (referenceRect[len] > popperRect[len]) {
6646        mainVariationSide = getOppositePlacement(mainVariationSide);
6647      }
6648
6649      var altVariationSide = getOppositePlacement(mainVariationSide);
6650      var checks = [];
6651
6652      if (checkMainAxis) {
6653        checks.push(overflow[_basePlacement] <= 0);
6654      }
6655
6656      if (checkAltAxis) {
6657        checks.push(overflow[mainVariationSide] <= 0, overflow[altVariationSide] <= 0);
6658      }
6659
6660      if (checks.every(function (check) {
6661        return check;
6662      })) {
6663        firstFittingPlacement = placement;
6664        makeFallbackChecks = false;
6665        break;
6666      }
6667
6668      checksMap.set(placement, checks);
6669    }
6670
6671    if (makeFallbackChecks) {
6672      // `2` may be desired in some cases – research later
6673      var numberOfChecks = flipVariations ? 3 : 1;
6674
6675      var _loop = function _loop(_i) {
6676        var fittingPlacement = placements.find(function (placement) {
6677          var checks = checksMap.get(placement);
6678
6679          if (checks) {
6680            return checks.slice(0, _i).every(function (check) {
6681              return check;
6682            });
6683          }
6684        });
6685
6686        if (fittingPlacement) {
6687          firstFittingPlacement = fittingPlacement;
6688          return "break";
6689        }
6690      };
6691
6692      for (var _i = numberOfChecks; _i > 0; _i--) {
6693        var _ret = _loop(_i);
6694
6695        if (_ret === "break") break;
6696      }
6697    }
6698
6699    if (state.placement !== firstFittingPlacement) {
6700      state.modifiersData[name]._skip = true;
6701      state.placement = firstFittingPlacement;
6702      state.reset = true;
6703    }
6704  } // eslint-disable-next-line import/no-unused-modules
6705
6706
6707  var flip$1 = {
6708    name: 'flip',
6709    enabled: true,
6710    phase: 'main',
6711    fn: flip,
6712    requiresIfExists: ['offset'],
6713    data: {
6714      _skip: false
6715    }
6716  };
6717
6718  function getAltAxis(axis) {
6719    return axis === 'x' ? 'y' : 'x';
6720  }
6721
6722  function within(min$1, value, max$1) {
6723    return max(min$1, min(value, max$1));
6724  }
6725
6726  function preventOverflow(_ref) {
6727    var state = _ref.state,
6728        options = _ref.options,
6729        name = _ref.name;
6730    var _options$mainAxis = options.mainAxis,
6731        checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,
6732        _options$altAxis = options.altAxis,
6733        checkAltAxis = _options$altAxis === void 0 ? false : _options$altAxis,
6734        boundary = options.boundary,
6735        rootBoundary = options.rootBoundary,
6736        altBoundary = options.altBoundary,
6737        padding = options.padding,
6738        _options$tether = options.tether,
6739        tether = _options$tether === void 0 ? true : _options$tether,
6740        _options$tetherOffset = options.tetherOffset,
6741        tetherOffset = _options$tetherOffset === void 0 ? 0 : _options$tetherOffset;
6742    var overflow = detectOverflow(state, {
6743      boundary: boundary,
6744      rootBoundary: rootBoundary,
6745      padding: padding,
6746      altBoundary: altBoundary
6747    });
6748    var basePlacement = getBasePlacement(state.placement);
6749    var variation = getVariation(state.placement);
6750    var isBasePlacement = !variation;
6751    var mainAxis = getMainAxisFromPlacement(basePlacement);
6752    var altAxis = getAltAxis(mainAxis);
6753    var popperOffsets = state.modifiersData.popperOffsets;
6754    var referenceRect = state.rects.reference;
6755    var popperRect = state.rects.popper;
6756    var tetherOffsetValue = typeof tetherOffset === 'function' ? tetherOffset(Object.assign({}, state.rects, {
6757      placement: state.placement
6758    })) : tetherOffset;
6759    var data = {
6760      x: 0,
6761      y: 0
6762    };
6763
6764    if (!popperOffsets) {
6765      return;
6766    }
6767
6768    if (checkMainAxis || checkAltAxis) {
6769      var mainSide = mainAxis === 'y' ? top : left;
6770      var altSide = mainAxis === 'y' ? bottom : right;
6771      var len = mainAxis === 'y' ? 'height' : 'width';
6772      var offset = popperOffsets[mainAxis];
6773      var min$1 = popperOffsets[mainAxis] + overflow[mainSide];
6774      var max$1 = popperOffsets[mainAxis] - overflow[altSide];
6775      var additive = tether ? -popperRect[len] / 2 : 0;
6776      var minLen = variation === start ? referenceRect[len] : popperRect[len];
6777      var maxLen = variation === start ? -popperRect[len] : -referenceRect[len]; // We need to include the arrow in the calculation so the arrow doesn't go
6778      // outside the reference bounds
6779
6780      var arrowElement = state.elements.arrow;
6781      var arrowRect = tether && arrowElement ? getLayoutRect(arrowElement) : {
6782        width: 0,
6783        height: 0
6784      };
6785      var arrowPaddingObject = state.modifiersData['arrow#persistent'] ? state.modifiersData['arrow#persistent'].padding : getFreshSideObject();
6786      var arrowPaddingMin = arrowPaddingObject[mainSide];
6787      var arrowPaddingMax = arrowPaddingObject[altSide]; // If the reference length is smaller than the arrow length, we don't want
6788      // to include its full size in the calculation. If the reference is small
6789      // and near the edge of a boundary, the popper can overflow even if the
6790      // reference is not overflowing as well (e.g. virtual elements with no
6791      // width or height)
6792
6793      var arrowLen = within(0, referenceRect[len], arrowRect[len]);
6794      var minOffset = isBasePlacement ? referenceRect[len] / 2 - additive - arrowLen - arrowPaddingMin - tetherOffsetValue : minLen - arrowLen - arrowPaddingMin - tetherOffsetValue;
6795      var maxOffset = isBasePlacement ? -referenceRect[len] / 2 + additive + arrowLen + arrowPaddingMax + tetherOffsetValue : maxLen + arrowLen + arrowPaddingMax + tetherOffsetValue;
6796      var arrowOffsetParent = state.elements.arrow && getOffsetParent(state.elements.arrow);
6797      var clientOffset = arrowOffsetParent ? mainAxis === 'y' ? arrowOffsetParent.clientTop || 0 : arrowOffsetParent.clientLeft || 0 : 0;
6798      var offsetModifierValue = state.modifiersData.offset ? state.modifiersData.offset[state.placement][mainAxis] : 0;
6799      var tetherMin = popperOffsets[mainAxis] + minOffset - offsetModifierValue - clientOffset;
6800      var tetherMax = popperOffsets[mainAxis] + maxOffset - offsetModifierValue;
6801
6802      if (checkMainAxis) {
6803        var preventedOffset = within(tether ? min(min$1, tetherMin) : min$1, offset, tether ? max(max$1, tetherMax) : max$1);
6804        popperOffsets[mainAxis] = preventedOffset;
6805        data[mainAxis] = preventedOffset - offset;
6806      }
6807
6808      if (checkAltAxis) {
6809        var _mainSide = mainAxis === 'x' ? top : left;
6810
6811        var _altSide = mainAxis === 'x' ? bottom : right;
6812
6813        var _offset = popperOffsets[altAxis];
6814
6815        var _min = _offset + overflow[_mainSide];
6816
6817        var _max = _offset - overflow[_altSide];
6818
6819        var _preventedOffset = within(tether ? min(_min, tetherMin) : _min, _offset, tether ? max(_max, tetherMax) : _max);
6820
6821        popperOffsets[altAxis] = _preventedOffset;
6822        data[altAxis] = _preventedOffset - _offset;
6823      }
6824    }
6825
6826    state.modifiersData[name] = data;
6827  } // eslint-disable-next-line import/no-unused-modules
6828
6829
6830  var preventOverflow$1 = {
6831    name: 'preventOverflow',
6832    enabled: true,
6833    phase: 'main',
6834    fn: preventOverflow,
6835    requiresIfExists: ['offset']
6836  };
6837
6838  var toPaddingObject = function toPaddingObject(padding, state) {
6839    padding = typeof padding === 'function' ? padding(Object.assign({}, state.rects, {
6840      placement: state.placement
6841    })) : padding;
6842    return mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));
6843  };
6844
6845  function arrow(_ref) {
6846    var _state$modifiersData$;
6847
6848    var state = _ref.state,
6849        name = _ref.name,
6850        options = _ref.options;
6851    var arrowElement = state.elements.arrow;
6852    var popperOffsets = state.modifiersData.popperOffsets;
6853    var basePlacement = getBasePlacement(state.placement);
6854    var axis = getMainAxisFromPlacement(basePlacement);
6855    var isVertical = [left, right].indexOf(basePlacement) >= 0;
6856    var len = isVertical ? 'height' : 'width';
6857
6858    if (!arrowElement || !popperOffsets) {
6859      return;
6860    }
6861
6862    var paddingObject = toPaddingObject(options.padding, state);
6863    var arrowRect = getLayoutRect(arrowElement);
6864    var minProp = axis === 'y' ? top : left;
6865    var maxProp = axis === 'y' ? bottom : right;
6866    var endDiff = state.rects.reference[len] + state.rects.reference[axis] - popperOffsets[axis] - state.rects.popper[len];
6867    var startDiff = popperOffsets[axis] - state.rects.reference[axis];
6868    var arrowOffsetParent = getOffsetParent(arrowElement);
6869    var clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0;
6870    var centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the popper if the center point is
6871    // outside of the popper bounds
6872
6873    var min = paddingObject[minProp];
6874    var max = clientSize - arrowRect[len] - paddingObject[maxPro
6874p];
6875    var center = clientSize / 2 - arrowRect[len] / 2 + centerToReference;
6876    var offset = within(min, center, max); // Prevents breaking syntax highlighting...
6877
6878    var axisProp = axis;
6879    state.modifiersData[name] = (_state$modifiersData$ = {}, _state$modifiersData$[axisProp] = offset, _state$modifiersData$.centerOffset = offset - center, _state$modifiersData$);
6880  }
6881
6882  function effect(_ref2) {
6883    var state = _ref2.state,
6884        options = _ref2.options;
6885    var _options$element = options.element,
6886        arrowElement = _options$element === void 0 ? '[data-popper-arrow]' : _options$element;
6887
6888    if (arrowElement == null) {
6889      return;
6890    } // CSS selector
6891
6892
6893    if (typeof arrowElement === 'string') {
6894      arrowElement = state.elements.popper.querySelector(arrowElement);
6895
6896      if (!arrowElement) {
6897        return;
6898      }
6899    }
6900
6901    {
6902      if (!isHTMLElement(arrowElement)) {
6903        console.error(['Popper: "arrow" element must be an HTMLElement (not an SVGElement).', 'To use an SVG arrow, wrap it in an HTMLElement that will be used as', 'the arrow.'].join(' '));
6904      }
6905    }
6906
6907    if (!contains(state.elements.popper, arrowElement)) {
6908      {
6909        console.error(['Popper: "arrow" modifier\'s `element` must be a child of the popper', 'element.'].join(' '));
6910      }
6911
6912      return;
6913    }
6914
6915    state.elements.arrow = arrowElement;
6916  } // eslint-disable-next-line import/no-unused-modules
6917
6918
6919  var arrow$1 = {
6920    name: 'arrow',
6921    enabled: true,
6922    phase: 'main',
6923    fn: arrow,
6924    effect: effect,
6925    requires: ['popperOffsets'],
6926    requiresIfExists: ['preventOverflow']
6927  };
6928
6929  function getSideOffsets(overflow, rect, preventedOffsets) {
6930    if (preventedOffsets === void 0) {
6931      preventedOffsets = {
6932        x: 0,
6933        y: 0
6934      };
6935    }
6936
6937    return {
6938      top: overflow.top - rect.height - preventedOffsets.y,
6939      right: overflow.right - rect.width + preventedOffsets.x,
6940      bottom: overflow.bottom - rect.height + preventedOffsets.y,
6941      left: overflow.left - rect.width - preventedOffsets.x
6942    };
6943  }
6944
6945  function isAnySideFullyClipped(overflow) {
6946    return [top, right, bottom, left].some(function (side) {
6947      return overflow[side] >= 0;
6948    });
6949  }
6950
6951  function hide(_ref) {
6952    var state = _ref.state,
6953        name = _ref.name;
6954    var referenceRect = state.rects.reference;
6955    var popperRect = state.rects.popper;
6956    var preventedOffsets = state.modifiersData.preventOverflow;
6957    var referenceOverflow = detectOverflow(state, {
6958      elementContext: 'reference'
6959    });
6960    var popperAltOverflow = detectOverflow(state, {
6961      altBoundary: true
6962    });
6963    var referenceClippingOffsets = getSideOffsets(referenceOverflow, referenceRect);
6964    var popperEscapeOffsets = getSideOffsets(popperAltOverflow, popperRect, preventedOffsets);
6965    var isReferenceHidden = isAnySideFullyClipped(referenceClippingOffsets);
6966    var hasPopperEscaped = isAnySideFullyClipped(popperEscapeOffsets);
6967    state.modifiersData[name] = {
6968      referenceClippingOffsets: referenceClippingOffsets,
6969      popperEscapeOffsets: popperEscapeOffsets,
6970      isReferenceHidden: isReferenceHidden,
6971      hasPopperEscaped: hasPopperEscaped
6972    };
6973    state.attributes.popper = Object.assign({}, state.attributes.popper, {
6974      'data-popper-reference-hidden': isReferenceHidden,
6975      'data-popper-escaped': hasPopperEscaped
6976    });
6977  } // eslint-disable-next-line import/no-unused-modules
6978
6979
6980  var hide$1 = {
6981    name: 'hide',
6982    enabled: true,
6983    phase: 'main',
6984    requiresIfExists: ['preventOverflow'],
6985    fn: hide
6986  };
6987
6988  var defaultModifiers$1 = [eventListeners, popperOffsets$1, computeStyles$1, applyStyles$1];
6989  var createPopper$1 = /*#__PURE__*/popperGenerator({
6990    defaultModifiers: defaultModifiers$1
6991  }); // eslint-disable-next-line import/no-unused-modules
6992
6993  var defaultModifiers = [eventListeners, popperOffsets$1, computeStyles$1, applyStyles$1, offset$1, flip$1, preventOverflow$1, arrow$1, hide$1];
6994  var createPopper = /*#__PURE__*/popperGenerator({
6995    defaultModifiers: defaultModifiers
6996  }); // eslint-disable-next-line import/no-unused-modules
6997
6998  exports.applyStyles = applyStyles$1;
6999  exports.arrow = arrow$1;
7000  exports.computeStyles = computeStyles$1;
7001  exports.createPopper = createPopper;
7002  exports.createPopperLite = createPopper$1;
7003  exports.defaultModifiers = defaultModifiers;
7004  exports.detectOverflow = detectOverflow;
7005  exports.eventListeners = eventListeners;
7006  exports.flip = flip$1;
7007  exports.hide = hide$1;
7008  exports.offset = offset$1;
7009  exports.popperGenerator = popperGenerator;
7010  exports.popperOffsets = popperOffsets$1;
7011  exports.preventOverflow = preventOverflow$1;
7012
7013  Object.defineProperty(exports, '__esModule', { value: true });
7014
7015})));
7016$(function(){
7017
7018    function setupPopper(targetID, popoverID)
7019    {
7020            var popcorn = document.querySelector('#'+targetID);
7021            var tooltip = document.querySelector('#'+popoverID);
7022            var width = document.getElementById(targetID).getBoundingClientRect().width*0.5 - 8 + (137 - document.getElementById(targetID).getBoundingClientRect().width);//document.getElementById(targetID).offsetWidth * -1;
7023
7024            if (window.innerWidth >= 992) {
7025                Popper.createPopper(popcorn, tooltip, {
7026                  placement: 'bottom',
7027                  modifiers: [
7028                    {
7029                      name: 'offset',
7030                      options: {
7031                        offset: [width, 15],
7032                      },
7033                    },
7034                  ],
7035                });
7036            }
7037            else {
7038                  Popper.createPopper(popcorn, tooltip, {
7039                  placement: 'top',
7040                  modifiers: [
7041                    {
7042                      name: 'offset',
7043                      options: {
7044                        offset: [0, 15],
7045                      },
7046                    },
7047                  ],
7048                });
7049
7050            }
7051
7052    }
7053
7054    function popoverCookieExists(popoverID)
7055    {
7056        var headline = $('#'+popoverID).data('headline');
7057        var modifiedDate = $('#'+popoverID).data('modified-date');
7058        var cookieString = headline + '-' +modifiedDate;
7059        return document.cookie.indexOf(cookieString) > -1;
7060    }
7061
7062    function createPopoverCookie(popoverID)
7063    {
7064        var headline = $('#'+popoverID).data('headline');
7065        var modifiedDate = $('#'+popoverID).data('modified-date');
7066        var cookieString = headline + '-' +modifiedDate;
7067        //1 year
7068        var expirationDate  = new Date();
7069        expirationDate = new Date(expirationDate.getTime() +1000*60*60*24*365);
7070        document.cookie = 'popover='+cookieString+'; expires='+expirationDate.toGMTString()+';';
7071    }
7072
7073    function setupPopoverInteraction(targetID, popoverID)
7074    {
7075            //Desktop - Hover
7076            $('#'+targetID).hover(function(){
7077                if (window.innerWidth >= 992) {
7078                    setupPopper(targetID, popoverID)
7079                    $('#'+popoverID).removeClass('popover-alert--hide');
7080                }
7081            },function(){
7082                if (window.innerWidth >= 992) {
7083                    //Check if use is hovering over popover
7084
7085                    if(window.document.documentMode)
7086                    {
7087                        setTimeout(function(){
7088                            var popoverHover = $('#'+popoverID).is(":hover")
7089
7090                            if (!popoverHover) {
7091                                $('#'+popoverID).addClass('popover-alert--hide');
7092                            }
7093                        }, 250);
7094                    }
7095                    else {
7096                        var popoverHover = $('#'+popoverID).is(":hover")
7097
7098                        if (!popoverHover) {
7099                            $('#'+popoverID).addClass('popover-alert--hide');
7100                        }
7101                    }
7102                }
7103            });
7104
7105            //Desktop - Hide popover when leaving it
7106            $('#'+popoverID).on('mouseleave', function(){
7107                if ($('#'+targetID  + ':hover').length == 0) {
7108                    $('#'+popoverID).addClass('popover-alert--hide');
7109                }
7110            });
7111
7112            //Mobile
7113            $('#'+targetID).click(function(e){
7114                if (window.innerWidth < 992) {
7115                    if($('#'+popoverID).hasClass('popover-alert--hide') && !popoverCookieExists(popoverID))
7116                    {
7117                        e.preventDefault();
7118                        $('#'+popoverID).removeClass('popover-alert--hide');
7119                        setupPopper(targetID, popoverID)
7120                    }
7121                }
7122            });
7123
7124            //Reset popover on resize
7125            $(window).on('resize', function() {
7126                setupPopper(targetID, popoverID);
7127                $('#'+popoverID).addClass('popover-alert--hide');
7128            });
7129
7130            $('.popover-alert__cta').click(function(){
7131                $('#'+popoverID).addClass('popover-alert--hide');
7132                createPopoverCookie(popoverID);
7133            });
7134
7135            $('.header__menu-trigger ').click(function(){
7136                $('#'+popoverID).addClass('popover-alert--hide');
7137            });
7138    }
7139    $(document).ready(function(){
7140        $('.popover-alert').each(function(){
7141
7142            //Setup
7143            var targetID = $(this).data('target');
7144            var popoverID = $(this).attr('id');
7145
7146            setupPopper(targetID, popoverID);
7147            $('#'+popoverID).removeClass('popover-alert--invisible');
7148            $('#'+popoverID).addClass('popover-alert--hide');
7149            if(!popoverCookieExists(popoverID))
7150            {
7151                setupPopoverInteraction(targetID, popoverID);
7152            }
7153
7154        })
7155    })
7156});
7157var pipelineMapList = [];
7158var jsonData = [];
7159
7160addEventListener('DOMContentLoaded', () => {
7161  //initial setup
7162  $('.pipeline-map').each(function (index, map) {
7163    var hybridTilt = $(map).data('hybrid-tilt');
7164    var satelliteTilt = $(map).data('satellite-tilt');
7165    var type = $(map).data('map-type');
7166    var setTilt = 0;
7167    var id = $(map).attr('id');
7168    console.log($(map))
7169    console.log(id);
7170    if (type == 'satellite' && satelliteTilt) {
7171      setTilt = 45;
7172    }
7173    if (type == 'hybrid' && hybridTilt) {
7174      setTilt = 45;
7175    }
7176    //Create a Google map under pipe map
7177    let newMap = new google.maps.Map(map, {
7178      zoom: $(map).data('zoom'),
7179      center: {
7180        lat: parseFloat($(map).data('latitude')),
7181        lng: parseFloat($(map).data('longitude')),
7182      },
7183      mapTypeId: $(map).data('map-type'),
7184      mapId: id,
7185      tilt: setTilt,
7186    });
7187    pipelineMapList.push(newMap);
7188    createData(index);
7189  });
7190  function createData(index) {
7191    $($('.google-map-wrapper')[index])
7192      .find('.pipeline-map--data')
7193      .each(function () {
7194        //Get Data point Data
7195        var name = $(this).data('name');
7196        var color = $(this).data('color');
7197        var snapToRoad = $(this).data('snap-to-road') != null;
7198        var thickness = $(this).data('thickness');
7199        var opacity = $(this).data('opacity');
7200
7201        var data = [];
7202        $(this)
7203          .children('.pipeline-map--coordinate')
7204          .each(function () {
7205            data.push({
7206              lng: parseFloat($(this).data('longitude')),
7207              lat: parseFloat($(this).data('latitude')),
7208            });
7209          });
7210
7211        if (!snapToRoad) {
7212          createDefaultLine(data, color, thickness, opacity, index);
7213        }
7214        if (snapToRoad) {
7215          createSnapToRoadLine(data, color, thickness, opacity, index);
7216        }
7217        createPipelineMarkers(index);
7218      });
7219  }
7220});
7221
7222function createDefaultLine(data, color, thickness, opacity, index) {
7223  var planCoordinates = data;
7224  var path = new google.maps.Polyline({
7225    path: planCoordinates,
7226    geodesic: true,
7227    strokeColor: color,
7228    strokeOpacity: opacity,
7229    strokeWeight: thickness,
7230  });
7231  path.setMap(pipelineMapList[index]);
7232}
7233
7234function createSnapToRoadLine(data, color, thickness, opacity, index) {
7235  // Create a URL for the Snap to Roads Google API request.
7236  var url =
7237    'https://roads.googleapis.com/v1/snapToRoads?path=' +
7238    data
7239      .map(function (point) {
7240        return point.lat + ',' + point.lng;
7241      })
7242      .join('|');
7243  url += '&interpolate=true';
7244  (url += '&key=' + $($('.pipeline-map')[index]).data('api')),
7245    // Make the request to the Google API.
7246    $.ajax({
7247      url: url,
7248      dataType: 'json',
7249      success: function (response) {
7250        // Get the snapped points from the response.
7251        var snappedPoints = response.snappedPoints;
7252        var output = [];
7253        for (var i = 0; i < snappedPoints.length; i++) {
7254          // Get the location object from the current `snappedPoints` object.
7255          var location = snappedPoints[i]['location'];
7256
7257          // Add the latitude and longitude to the output array.
7258          output.push({
7259            lat: location['latitude'],
7260            lng: location['longitude'],
7261          });
7262        }
7263        // Create a polyline object from the snapped points.
7264        var polyline = new google.maps.Polyline({
7265          path: output,
7266          strokeColor: color,
7267          strokeOpacity: opacity,
7268          strokeWeight: thickness,
7269        });
7270
7271        // Add the polyline object to the map.
7272        polyline.setMap(pipelineMapList[index]);
7273      },
7274    });
7275}
7276
7277function createPipelineMarkers(index) {
7278  var markers = [];
7279  $($('.pipelinemap .google-map-wrapper')[index])
7280    .find('.pipeline-map--marker')
7281    .each(function () {
7282      var pointTitle = $(this).data('title');
7283      var pointIcon = $(this).data('custom-icon');
7284      var pointContent = $(this).data('content');
7285      // Create the content for the info window
7286      
7287      var infowindowContent = "";
7288      infowindowContent = pointTitle != undefined ? infowindowContent + "<h5 class='marker--title mb-3'>" +
7289        pointTitle +
7290        '</h5>' : infowindowContent + "";
7291        
7292      infowindowContent = pointContent != undefined ? infowindowContent + "<p class='marker-content'>" +
7293        pointContent +
7294        '</p>' : infowindowContent + "";
7295        var infowindow = new google.maps.InfoWindow({
7296          content: infowindowContent,
7297        });
7298      
7299
7300      var markerElement = null;
7301      // Create the element for the AdvancedMarkerElement
7302      if (pointIcon != null && pointIcon != undefined) {
7303        markerElement = document.createElement('div');
7304        markerElement.classList.add('custom-marker');
7305        markerElement.innerHTML =
7306          "<img src='" + pointIcon + "' alt='" + pointTitle + "' />";
7307      }
7308
7309      var marker = new google.maps.marker.AdvancedMarkerElement({
7310        position: {
7311          lat: parseFloat($(this).data('latitude')),
7312          lng: parseFloat($(this).data('longitude')),
7313        },
7314        title: pointTitle,
7315        content: markerElement, // Set the custom marker content
7316        map: pipelineMapList[index], // Add to the map
7317      });
7318
7319      // Add a click listener for each marker to open the info window
7320      marker.addListener('click', function () {
7321        infowindow.open({
7322          map: pipelineMapList[index],
7323          anchor: marker,
7324          shouldFocus: false,
7325        });
7326      });
7327
7328      markers.push(marker);
7329    });
7330}
7331
7332/**
7333 * Page Animations - Scroll Effects
7334 * 
7335 * This script handles:
7336 * - Hiding scrollbars on specified elements
7337 * - Applying GSAP ScrollTrigger parallax effects to elements
7338 * 
7339 * Uses ScrollTrigger with scrub for parallax effects (no wrapper structure needed).
7340 * Speed: < 1 = slower/parallax up, > 1 = faster, 1 = normal
7341 * Lag: Smoothing delay in seconds (maps to scrub value)
7342 */
7343
7344document.addEventListener('DOMContentLoaded', function() {
7345  const pageAnimationsElement = document.querySelector('.pageanimations[data-instance-id]');
7346  
7347  if (!pageAnimationsElement) {
7348    return;
7349  }
7350
7351  // Parse hide scrollbar selectors
7352  let hideScrollbarSelectors = [];
7353  try {
7354    hideScrollbarSelectors = JSON.parse(pageAnimationsElement.dataset.hideScrollbarSelectors || '[]');
7355  } catch (e) {
7356    console.warn('Page Animations: Failed to parse hideScrollbarSelectors JSON');
7357  }
7358
7359  // Parse scroll effects
7360  let scrollEffects = [];
7361  try {
7362    scrollEffects = JSON.parse(pageAnimationsElement.dataset.scrollEffects || '[]');
7363  } catch (e) {
7364    console.warn('Page Animations: Failed to parse scrollEffects JS
7364ON');
7365  }
7366
7367  // Handle hide scrollbar for each specified element
7368  if (hideScrollbarSelectors && hideScrollbarSelectors.length > 0) {
7369    hideScrollbarSelectors.forEach(function(selector) {
7370      const elements = document.querySelectorAll(selector);
7371      elements.forEach(function(el) {
7372        el.classList.add('hide-page-scrollbar');
7373      });
7374    });
7375  }
7376
7377  // Initialize ScrollTrigger parallax effects if there are any configured
7378  if (scrollEffects && scrollEffects.length > 0) {
7379    initScrollTriggerEffects(scrollEffects);
7380  }
7381});
7382
7383/**
7384 * Initialize GSAP ScrollTrigger parallax effects
7385 * @param {Array} effects - Array of effect configurations
7386 */
7387const initScrollTriggerEffects = function(effects) {
7388  // Check for GSAP availability
7389  if (typeof gsap === 'undefined') {
7390    console.warn('Page Animations: GSAP is not available. Scroll effects will not be applied.');
7391    return;
7392  }
7393
7394  // Check for ScrollTrigger
7395  if (typeof ScrollTrigger === 'undefined') {
7396    console.warn('Page Animations: ScrollTrigger plugin is not available. Scroll effects will not be applied.');
7397    return;
7398  }
7399
7400  // Register plugin
7401  gsap.registerPlugin(ScrollTrigger);
7402
7403  // Apply effects to each configured element
7404  effects.forEach(function(effect) {
7405    if (!effect.elementSelector) {
7406      return;
7407    }
7408
7409    const elements = document.querySelectorAll(effect.elementSelector);
7410    if (elements.length === 0) {
7411      return;
7412    }
7413
7414    // Convert speed to yPercent
7415    // speed < 1 = element moves slower (parallax up effect)
7416    // speed > 1 = element moves faster
7417    // speed = 1 = no effect (normal scroll)
7418    const speed = effect.speed !== undefined ? effect.speed : 1;
7419    
7420    // Skip if speed is 1 (no parallax effect)
7421    if (speed === 1) {
7422      return;
7423    }
7424
7425    // Calculate movement: (1 - speed) * 100 gives us the relative movement
7426    // speed 0.5 = yPercent -50 (moves up 50% relative to scroll)
7427    // speed 1.5 = yPercent 50 (moves down 50% relative to scroll)
7428    const yPercent = (1 - speed) * 100;
7429
7430    // Lag becomes the scrub value (smoothing delay)
7431    const scrub = effect.lag !== undefined && effect.lag > 0 ? effect.lag : true;
7432
7433    // Get custom scroll container if specified
7434    const scrollContainer = effect.scrollContainer || null;
7435
7436    elements.forEach(function(element) {
7437      const scrollTriggerConfig = {
7438        trigger: element,
7439        start: 'top bottom',
7440        end: 'bottom top',
7441        scrub: scrub
7442      };
7443
7444      // Add custom scroller if specified
7445      if (scrollContainer) {
7446        scrollTriggerConfig.scroller = scrollContainer;
7447      }
7448
7449      gsap.to(element, {
7450        yPercent: yPercent,
7451        ease: 'none',
7452        scrollTrigger: scrollTriggerConfig
7453      });
7454    });
7455  });
7456};
7457
7458/**
7459 * Page Animations - GSAP Directionally Aware Header
7460 * 
7461 * This script handles the header hide/show behavior using GSAP ScrollTrigger.
7462 * The header hides when scrolling down and shows when scrolling up.
7463 * 
7464 * Configuration:
7465 * - Header Selector: CSS selector for header element (auto-detects if empty)
7466 * - Scroll Container Selector: CSS selector for scroll container (uses html if empty)
7467 */
7468
7469document.addEventListener('DOMContentLoaded', function() {
7470  const pageAnimationsElement = document.querySelector('.pageanimations[data-enable-hide-on-scroll="true"]');
7471  
7472  if (!pageAnimationsElement) {
7473    return;
7474  }
7475
7476  const enableHideOnScroll = pageAnimationsElement.dataset.enableHideOnScroll === 'true';
7477  
7478  if (!enableHideOnScroll) {
7479    return;
7480  }
7481
7482  // Check if GSAP and ScrollTrigger are available
7483  if (typeof gsap === 'undefined') {
7484    console.warn('Page Animations: GSAP is not available');
7485    return;
7486  }
7487
7488  if (typeof ScrollTrigger === 'undefined') {
7489    console.warn('Page Animations: GSAP ScrollTrigger plugin is not available');
7490    return;
7491  }
7492
7493  // Register ScrollTrigger plugin
7494  gsap.registerPlugin(ScrollTrigger);
7495
7496  /**
7497   * Gets the header element using the provided selector or auto-detection
7498   * @param {string} headerSelector - CSS selector for header element
7499   * @returns {HTMLElement|null} Header element or null if not found
7500   */
7501  const getHeader = (headerSelector) => {
7502    if (headerSelector && headerSelector.trim()) {
7503      return document.querySelector(headerSelector);
7504    }
7505    // Auto-detection fallback
7506    return document.querySelector('header, .header, #header, [role="banner"]');
7507  };
7508
7509  /**
7510   * Gets the scroll container element using the provided selector or default to html
7511   * @param {string}
7511 scrollContainerSelector - CSS selector for scroll container
7512   * @returns {HTMLElement} Scroll container element (document.documentElement if not specified)
7513   */
7514  const getScrollContainer = (scrollContainerSelector) => {
7515    if (scrollContainerSelector && scrollContainerSelector.trim()) {
7516      const container = document.querySelector(scrollContainerSelector);
7517      if (container) {
7518        return container;
7519      }
7520      console.warn('Page Animations: Scroll container not found, using default html');
7521    }
7522    // Return html element as default
7523    return document.documentElement;
7524  };
7525
7526  // Get configuration from data attributes
7527  const headerSelector = pageAnimationsElement.dataset.headerSelector || '';
7528  const scrollContainerSelector = pageAnimationsElement.dataset.scrollContainerSelector || '';
7529
7530  // Get elements
7531  const header = getHeader(headerSelector);
7532  const scrollContainer = getScrollContainer(scrollContainerSelector);
7533
7534  if (!header) {
7535    console.warn('Page Animations: No header element found');
7536    return;
7537  }
7538
7539  var headerHeight = header.offsetHeight;
7540  var isHeaderHidden = false;
7541  var refreshSuppressed = false;
7542
7543  const suppressRefresh = () => {
7544    if (refreshSuppressed) return;
7545    refreshSuppressed = true;
7546    ScrollTrigger.config({ autoRefreshEvents: 'none' });
7547  };
7548
7549  const restoreRefresh = () => {
7550    if (!refreshSuppressed) return;
7551    refreshSuppressed = false;
7552    ScrollTrigger.config({ autoRefreshEvents: 'visibilitychange,DOMContentLoaded,load' });
7553  };
7554
7555  const showAnim = gsap.timeline({ paused: true })
7556    .from(header, {
7557      yPercent: -100,
7558      duration: 0.2,
7559      force3D: true,
7560      onStart: suppressRefresh,
7561      onComplete: restoreRefresh,
7562      onReverseComplete: restoreRefresh
7563    }, 0);
7564
7565  showAnim.progress(1);
7566
7567  const marginTarget = '.experiencefragment:first-of-type + .responsivegrid';
7568  var siteHeaderHeight = $('.site-header').height();
7569
7570  const buildMarginAnim = (targetHeight) => {
7571    var anim = gsap.timeline({ paused: true })
7572      .fromTo(marginTarget, {
7573        marginTop: 0
7574      }, {
7575        marginTop: targetHeight,
7576        duration: 0.2,
7577        onStart: suppressRefresh,
7578        onComplete: restoreRefresh,
7579        onReverseComplete: restoreRefresh
7580      }, 0);
7581    anim.progress(1);
7582    return anim;
7583  };
7584
7585  var marginAnim = buildMarginAnim(siteHeaderHeight);
7586
7587  ScrollTrigger.create({
7588    scroller: scrollContainer,
7589    start: "top top",
7590    end: "max",
7591    onUpdate: (self) => {
7592      const scrollPosition = scrollContainer === document.documentElement 
7593        ? window.scrollY 
7594        : scrollContainer.scrollTop;
7595
7596      if (!isHeaderHidden && self.direction === 1 && scrollPosition > headerHeight) {
7597        isHeaderHidden = true;
7598        showAnim.reverse();
7599        marginAnim.reverse();
7600      } else if (isHeaderHidden && self.direction === -1) {
7601        isHeaderHidden = false;
7602        showAnim.play();
7603        marginAnim.play();
7604      }
7605    }
7606  });
7607
7608  var resizeTimeout;
7609  window.addEventListener('resize', function() {
7610    clearTimeout(resizeTimeout);
7611    resizeTimeout = setTimeout(function() {
7612      headerHeight = header.offsetHeight;
7613      var newSiteHeaderHeight = $('.site-header').height();
7614      if (newSiteHeaderHeight !== siteHeaderHeight) {
7615        siteHeaderHeight = newSiteHeaderHeight;
7616        marginAnim.kill();
7617        marginAnim = buildMarginAnim(siteHeaderHeight);
7618      }
7619      isHeaderHidden = false;
7620      showAnim.progress(1);
7621      ScrollTrigger.refresh();
7622    }, 250);
7623  });
7624});
7625
7626/**
7627 * Page Animations - Scroll Lock
7628 *
7629 * Prevents internal scrolling on specified containers until the page
7630 * is scrolled to the very top (window.scrollY === 0). Once the page
7631 * reaches the top, the container's internal scroll is unlocked.
7632 *
7633 * Configuration is read from the data-scroll-lock-selectors attribute
7634 * on the .pageanimations element (JSON array of CSS selectors).
7635 */
7636
7637document.addEventListener('DOMContentLoaded', function() {
7638  const pageAnimationsElement = document.querySelector('.pageanimations[data-instance-id]');
7639
7640  if (!pageAnimationsElement) {
7641    return;
7642  }
7643
7644  // Parse scroll lock selectors from data attribute
7645  let scrollLockSelectors = [];
7646  try {
7647    scrollLockSelectors = JSON.parse(pageAnimationsElement.dataset.scrollLockSelectors || '[]');
7648  } catch (e) {
7649    console.warn('Page Animations: Failed to parse scrollLockSelectors JSON');
7650  }
7651
7652  if (!scrollLockSelectors || scrollLockSelectors.length === 0) {
7653    return;
7654  }
7655
7656  /**
7657   * Tracks each locked container with its element reference and original
7658   * overflow style so we can restore it cleanly when the page hits the top.
7659   * @type {Array<{element: HTMLElement, originalOverflow: string}>}
7660   */
7661  const lockedContainers = [];
7662
7663  // Resolve selectors to elements and store their original overflow values
7664  scrollLockSelectors.forEach(function(selector) {
7665    const elements = document.querySelectorAll(selector);
7666    elements.forEach(function(el) {
7667      lockedContainers.push({
7668        element: el,
7669        originalOverflow: el.style.overflow || ''
7670      });
7671    });
7672  });
7673
7674  if (lockedContainers.length === 0) {
7675    return;
7676  }
7677
7678  /**
7679   * Applies or removes the scroll lock on every tracked container
7680   * based on the current page scroll position.
7681   */
7682  const handleScrollLock = function() {
7683    const isAtTop = window.scrollY === 0;
7684
7685    lockedContainers.forEach(function(container) {
7686      if (isAtTop) {
7687        // Unlock: restore the original overflow value
7688        container.element.style.overflow = container.originalOverflow;
7689      } else {
7690        // Lock: prevent internal scrolling
7691        container.element.style.overflow = 'hidden';
7692      }
7693    });
7694  };
7695
7696  // Set initial state
7697  handleScrollLock();
7698
7699  // Listen for scroll with passive flag for performance
7700  window.addEventListener('scroll', handleScrollLock, { passive: true });
7701});
7702
7703/*!
7704 * jQuery.ellipsis
7705 * http://github.com/jjenzz/jquery.ellipsis
7706 * --------------------------------------------------------------------------
7707 * Copyright (c) 2013 J. Smith (@jjenzz)
7708 * Dual licensed under the MIT and GPL licenses:
7709 * http://www.opensource.org/licenses/mit-license.php
7710 * http://www.gnu.org/licenses/gpl.html
7711 *
7712 * adds a class to the last 'allowed' line of text so you can apply
7713 * text-overflow: ellipsis;
7714 */
7715(function(factory) {
7716  'use strict';
7717
7718  if (typeof define === 'function' && define.amd) {
7719    // AMD. register as an anonymous module
7720    define(['jquery'], factory);
7721  } else {
7722    // browser globals
7723    factory(jQuery);
7724  }
7725}(function($) {
7726  'use strict';
7727
7728  var namespace = 'ellipsis',
7729      span = '<span style="white-space: nowrap;">',
7730      defaults = {
7731        lines: 'auto',
7732        ellipClass: 'ellip',
7733        responsive: false
7734      };
7735
7736  /**
7737   * Ellipsis()
7738   * --------------------------------------------------------------------------
7739   * @param {Node} el
7740   * @param {Object} opts
7741   */
7742  function Ellipsis(el, opts) {
7743    var base = this,
7744        currLine = 0,
7745        lines = [],
7746        setStartEllipAt,
7747        startEllipAt,
7748        resizeTimer,
7749        currOffset,
7750        lineHeight,
7751        contHeight,
7752        words,
7753        htmlEntities;
7754
7755    // List of HTML entities for escaping.
7756    htmlEntities = {
7757      '&': '&amp;',
7758      '<': '&lt;',
7759      '>': '&gt;',
7760      '"': '&quot;',
7761      "'": '&#x27;',
7762      '`': '&#x60;',
7763      '/': '&#x2F;',
7764      '\\': '&#x5C;'
7765    };
7766
7767    base.$cont = $(el);
7768    base.opts = $.extend({}, defaults, opts);
7769
7770    /**
7771     * create() happens once when
7772     * instance is created
7773     */
7774    function create() {
7775      base.text = base.$cont.text();
7776      base.opts.ellipLineClass = base.opts.ellipClass + '-line';
7777
7778      base.$el = $('<span class="' + base.opts.ellipClass + '" />');
7779      base.$el.text(base.text);
7780
7781      base.$cont.empty().append(base.$el);
7782
7783      init();
7784    }
7785
7786    /**
7787     * init()
7788     */
7789    function init() {
7790
7791      // if they only want one line just add
7792      // the class and do nothing else
7793      if (typeof base.opts.lines === 'number' && base.opts.lines < 2) {
7794        base.$el.addClass(base.opts.ellipLineClass);
7795        return;
7796      }
7797
7798      contHeight = base.$cont.height();
7799
7800      // if they only want to ellipsis the overflow
7801      // then do nothing if there is no overflow
7802      if (base.opts.lines === 'auto' && base.$el.prop('scrollHeight') <= contHeight) {
7803        return;
7804      }
7805
7806      if (!setStartEllipAt) {
7807        return;
7808      }
7809
7810      // create an array of words from our string
7811      words = $.trim(escapeText(base.text)).split(/\s+/);
7812
7813      // wrap each word in a span and temporarily append to the DOM
7814      base.$el.html(span + words.join('</span> ' + span) + '</span>');
7815
7816      // loop through words to determine which word the
7817      // ellipsis container should start from (need to
7818      // re-query spans from DOM so we can get their offset)
7819      base.$el.find('span').each(setStartEllipAt);
7820
7821      // startEllipAt could be 0 so make sure we're
7822      // checking undefined instead of falsey
7823      if (startEllipAt != null) {
7824        updateText(startEllipAt);
7825      }
7826    }
7827
7828    /**
7829     * updateText() updates the text in the DOM
7830     * with a span around the line that needs
7831     * to be truncated
7832     *
7833     * @param {Number} i
7834     */
7835    function updateText(nth) {
7836      // add a span that wraps from nth
7837      // word to the end of the string
7838      words[nth] = '<span class="' + base.opts.ellipLineClass + '">' + words[nth];
7839      words.push('</span>');
7840
7841      // update the DOM with
7842      // our new string/markup
7843      base.$el.html(words.join(' '));
7844    }
7845
7846    function escapeText(text){
7847      return String(text).replace(/[&<>"'``\/]/g, function (s) {
7848        return htmlEntities[s];
7849      });
7850    }
7851
7852    // only define the method if it's required
7853    if (base.opts.lines === 'auto') {
7854
7855      /**
7856       * setStartEllipByHeight() sets the start
7857       * position to the first word in the last
7858       * line of the element that doesn't overflow
7859       *
7860       * @param {Number} i
7861       * @param {Node} word
7862       */
7863      var setStartEllipByHeight = function(i, word) {
7864        var $word = $(word),
7865            top = $word.position().top;
7866
7867        lineHeight = lineHeight || $word.height();
7868
7869        if (top === currOffset) {
7870          // if it's top matches currOffset
7871          // then it's on the same line
7872          // as the previous word
7873          lines[currLine].push($word);
7874        } else {
7875          // otherwise we're
7876          // on a new line
7877          currOffset = top;
7878          currLine += 1;
7879          lines[currLine] = [$word];
7880        }
7881
7882        // if the bottom of the word is outside
7883        // the element (overflowing) then
7884        // stop looping and set startEllipAt to
7885        // the first word in the previous line
7886        if (top + lineHeight > contHeight) {
7887          startEllipAt = i - lines[currLine - 1].length;
7888          return false;
7889        }
7890      };
7891
7892      setStartEllipAt = setStartEllipByHeight;
7893    }
7894
7895    // only define the method if it's required
7896    if (typeof base.opts.lines === 'number' && base.opts.lines > 1) {
7897
7898        /**
7899         * setStartEllipByLine() sets the start
7900         * position to the first word in the line
7901         * that was passed to opts. This forces
7902         * the ellipsis on a specific line
7903         * regardless of overflow
7904         *
7905         * @param {Number} i
7906         * @param {Node} word
7907         */
7908        var setStartEllipByLine = function(i, word) {
7909          var $word = $(word),
7910              top = $word.position().top;
7911
7912          // if top isn't currOfset
7913          // then we're on a new line
7914          if (top !== currOffset) {
7915            currOffset = top;
7916            currLine += 1;
7917          }
7918
7919          // if the word's currLine is equal
7920          // to the line limit passed via options
7921          // then start ellip from this
7922          // word and stop looping
7923          if (currLine === base.opts.lines) {
7924            startEllipAt = i;
7925            return false;
7926          }
7927      };
7928
7929      setStartEllipAt = setStartEllipByLine;
7930    }
7931
7932    // only bind to window resize if required
7933    if (base.opts.responsive) {
7934
7935      /**
7936       * resize() resets necessary vars
7937       * and content and then re-initialises
7938       * the Ellipsis script
7939       */
7940      var resize = function() {
7941        lines = [];
7942        currLine = 0;
7943        currOffset = null;
7944        startEllipAt = null;
7945        base.$el.html(escapeText(base.text));
7946
7947        clearTimeout(resizeTimer);
7948        resizeTimer = setTimeout(init, 100);
7949      };
7950
7951      $(window).on('resize.' + namespace, resize);
7952    }
7953
7954    // start 'er up
7955    create();
7956  }
7957
7958  $.fn[namespace] = function(opts) {
7959    return this.each(function() {
7960      try {
7961        $(this).data(namespace, (new Ellipsis(this, opts)));
7962      } catch (err) {
7963        if (window.console) {
7964          console.error(namespace + ': ' + err);
7965        }
7966      }
7967    });
7968  };
7969
7970}));
7971/**
7972 * Created by yzheng on 2/1/16.
7973 */
7974
7975
7976$(function() {
7977
7978    $.fn.news_center=function(){
7979        // we only allow object parameter
7980        var options;
7981        if(arguments.length==0){
7982            // using default value
7983            options = DEFAULT_OPTIONS;
7984        }else if(typeof arguments[0]==="object"){
7985             options = $.extend({}, DEFAULT_OPTIONS, arguments[0]);
7986        }else {
7987            throw TypeError('Unsupported arguments for news center');
7988        }
7989        $.each(this,function(){
7990          initialize($(this),options);
7991        });
7992
7993    };
7994    /**
7995     * Initialize the the news center by making an ajax call to grab the data
7996     * @param selector
7997     * @param options
7998     */
7999    function initialize(selector,options){
8000        var fallbackImage = selector.data('fallbackImage');
8001        var jsonVersion = selector.data('jsonVersion');
8002        //we make the ajax call
8003        $.ajax({
8004            url:options.endpoint+"?page="+options.page+"&filter[posts_per_page]="+options.postsPerPage,
8005            dataType:options.dataType,
8006            success:function(data,status){
8007                if(status==="success"){
8008                    var results=[];
8009                    // meaning we have made a successful call
8010                    for(var i=0;i<data.length;i++){
8011                       var news;
8012                       if(typeof options.dataParser!=="undefined" && typeof options.dataParser ==="function"){
8013                            if(jsonVersion === 1) {
8014                                news = options.dataParser(data[i]);
8015                            }
8016                            else {
8017                                news = options.dataParserVersion2(data[i]);
8018                            }
8019                       }else{
8020                            if (jsonVersion === 2) {
8021                                news = dataParserVersion2(data[i]);
8022                            } else {
8023                                if(jsonVersion === 3) {
8024                                 news = dataParserVersion3(data[i]);
8025                                } else {
8026                                    news = dataParser(data[i]);
8027                                }
8028                            }
8029                        }
8030                        
8031
8032                    
8033                       results.push(news);
8034                    }
8035                    if(results.length>0) {
8036                        var renderItem;
8037                        if (typeof options.builder !== "undefined" && typeof options.builder === "function") {
8038                            renderItem = options.builder;
8039                        } else {
8040                            renderItem = builder;
8041                        }
8042                        var renderOptions = {
8043                            news_dataSet: results,
8044                            fallbackImage: fallbackImage
8045                        }
8046                        renderItem(selector, renderOptions, options);
8047                    }
8048                    selector.find(options.spinner).hide();
8049                    $('.news--article--headline').ellipsis({ lines: 4 });
8050                }
8051            }
8052        });
8053
8054    }
8055
8056    /**
8057     *
8058     * @type {{endpoint: string, page: number, postsPerPage: number, hbtpl: string, blockCount: number}}
8059     */
8060    DEFAULT_OPTIONS={
8061        endpoint:"https://alabamanewscenter.com/wp-json/posts",
8062        page:1,
8063        postsPerPage:3,
8064        hbtpl:"#news-template",
8065        blockCount:3
8066    }
8067    /**
8068     * Default Builder to use if none is pass in
8069     * @param selector
8070     * @param results
8071     * @param options
8072     */
8073    function builder(selector,results,options){
8074        //we need to check the post per page
8075        var endIndex=options.blockCount;
8076        if(options.postsPerPage===3){
8077            $newsRow = buildTemplate(options,results);// pass in the result object to let the handle bar take care the rest
8078            selector.append($newsRow);
8079        }else{
8080            // meaning we need to slice
8081            for(var j=1;j<=(options.postsPerPage/options.blockCount);j++){
8082                var slicingIndex = (j-1)*options.blockCount;
8083                var slicedDataSet = results.news_dataSet;
8084                var data = slicedDataSet.slice(slicingIndex,endIndex);
8085                var curNewsRow = buildTemplate(options,{
8086                    news_dataSet : data,
8087                    fallbackImage: results.fallbackImage
8088                });
8089                if(typeof curNewsRow !=="undefined") {
8090                    selector.append(curNewsRow);
8091                }
8092                endIndex=endIndex*2;
8093            }
8094        }
8095    }
8096
8097    /**
8098     * Default parser to use if none is pass in
8099     * @param news
8100     * @returns {{title: (title|*|string|string), link: (link|*|string), imageSrc: (*|string|$.validator.classRuleSettings.url|{url}|boolean|$.validator.methods.url), categoryName: *, categorySlug: *, dateRaw: (string|$.validator.classRuleSettings.date|{date}|boolean|$.validator.methods.date|Function|*), dateFormated: *}}
8101     */
8102    function dataParser(news){
8103        var title = $('<p />').html(news.title).text();
8104        var link = news.link;
8105        var picture = news.featured_image &&
8106              news.featured_image.attachment_meta &&
8107              news.featured_image.attachment_meta.sizes &&
8108              news.featured_image.attachment_meta.sizes.medium &&
8109              news.featured_image.attachment_meta.sizes.medium.url;
8110        var categories = news.terms.category;
8111        var date = news.date;
8112        if(typeof categories !=="undefined"){
8113            if(categories.length>0){
8114                var categoryName = categories[0].name;
8115                var categorySlug = categories[0].slug;
8116            }
8117        }
8118        return {title:title,link:link,imageSrc:picture,
8119            categoryName:categoryName,categorySlug:categorySlug,
8120            dateRaw:date,dateFormated:formatDate(new Date(date))};
8121
8122    }
8123
8124        /**
8125         * Default parser to use if none is pass in
8126         * @param news
8127         * @returns {{title: (title|*|string|string), link: (link|*|string), imageSrc: (*|string|$.validator.classRuleSettings.url|{url}|boolean|$.validator.methods.url), categoryName: *, categorySlug: *, dateRaw: (string|$.validator.classRuleSettings.date|{date}|boolean|$.validator.methods.date|Function|*), dateFormated: *}}
8128         */
8129        function dataParserVersion2(news){
8130            var title =  news.acf && news.acf.teaser__short_title ? news.acf.teaser__short_title : $('<p />').html(news.title.rendered).text();
8131            var link = news.link;
8132            var picture = news.featured_image_src;
8133            var categoryName = news.categories_names ? news.categories_names[0] : null;
8134            var categorySlug = news.slug;
8135            var date = news.date;
8136            return {title:title,link:link,imageSrc:picture,
8137                categoryName:categoryName,categorySlug:categorySlug,
8138                dateRaw:date,dateFormated:formatDate(new Date(date))};
8139
8140        }
8141        function dataParserVersion3(news){
8142            var title =  news.acf && news.acf.teaser__short_title ? news.acf.teaser__short_title : $('<p />').html(news.title.rendered).text();
8143            var link = news.link;
8144            var picture = news.jetpack_featured_media_url;
8145            var categoryName = news.categories_names ? news.categories_names[0] : null;
8146            var categorySlug = news.slug;
8147            var date = news.date;
8148            return {title:title,link:link,imageSrc:picture,
8149                categoryName:categoryName,categorySlug:categorySlug,
8150                dateRaw:date,dateFormated:formatDate(new Date(date))};
8151
8152        }
8153
8154    /**
8155     * Build template based on handlebars template implementation
8156     * @param options
8157     * @param news
8158     * @returns {*|{options, files}|{files}}
8159     */
8160
8161    function buildTemplate(options,news){
8162        var source = $(options.hbtpl).html();
8163        if(source && source.length!==0) {
8164            var template = Handlebars.compile(source);
8165            return template(news);
8166        }
8167
8168    }
8169
8170    /**
8171     * Helper function to format the date on MMMM dd, yyyyy
8172     * @param date
8173     * @returns {string}
8174     */
8175    function formatDate(date){
8176        var monthNames = [
8177            "January", "February", "March",
8178            "April", "May", "June", "July",
8179            "August", "September", "October",
8180            "November", "December"
8181        ];
8182        var dateFormated = monthNames[date.getMonth()]+" "+date.getDate()+", "+date.getFullYear();
8183        return dateFormated;
8184    }
8185});
8186
8187$(function(){
8188    $(".news-container").news_center({postsPerPage:$("#postPerPage").val(),endpoint:$("#endPoint").val(),spinner:".spinner"});
8189});
8190
8191$(function () {
8192  $('.more-article-data').each(function () {
8193    var data = $(this);
8194    var root = $(this);
8195
8196    $.get('/bin/southern/global/latestarticles', {
8197      tags: data.attr('data-tags'),
8198      path: data.attr('data-path'),
8199      limit: 4,
8200      filter: 'most-recent',
8201      start: 0,
8202    }).done(function (data) {
8203      var pages = data.pages;
8204      pages = pages
8205        .filter(function (s) {
8206          return s.path != window.location.href;
8207        })
8208        .slice(0, 3);
8209
8210      for (var i = 0; i < pages.length; i++) {
8211        root.append(
8212          '<li><a href="' + pages[i].path + '">' + pages[i].title + '</a></li>'
8213        );
8214      }
8215    });
8216  });
8217});
8218
8219/*!
8220 * clipboard.js v2.0.6
8221 * https://clipboardjs.com/
8222 *
8223 * Licensed MIT © Zeno Rocha
8224 */
8225!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exp
8225orts.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return o={},r.m=n=[function(t,e){t.exports=function(t){var e;if("SELECT"===t.nodeName)t.focus(),e=t.value;else if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName){var n=t.hasAttribute("readonly");n||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),n||t.removeAttribute("readonly"),e=t.value}else{t.hasAttribute("contenteditable")&&t.focus();var o=window.getSelection(),r=document.createRange();r.selectNodeContents(t),o.removeAllRanges(),o.addRange(r),e=o.toString()}return e}},function(t,e){function n(){}n.prototype={on:function(t,e,n){var o=this.e||(this.e={});return(o[t]||(o[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){var o=this;function r(){o.off(t,r),e.apply(n,arguments)}return r._=e,this.on(t,r,n)},emit:function(t){for(var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),o=0,r=n.length;o<r;o++)n[o].fn.apply(n[o].ctx,e);return this},off:function(t,e){var n=this.e||(this.e={}),o=n[t],r=[];if(o&&e)for(var i=0,a=o.length;i<a;i++)o[i].fn!==e&&o[i].fn._!==e&&r.push(o[i]);return r.length?n[t]=r:delete n[t],this}},t.exports=n,t.exports.TinyEmitter=n},function(t,e,n){var d=n(3),h=n(4);t.exports=function(t,e,n){if(!t&&!e&&!n)throw new Error("Missing required arguments");if(!d.string(e))throw new TypeError("Second argument must be a String");if(!d.fn(n))throw new TypeError("Third argument must be a Function");if(d.node(t))return s=e,f=n,(u=t).addEventListener(s,f),{destroy:function(){u.removeEventListener(s,f)}};if(d.nodeList(t))return a=t,c=e,l=n,Array.prototype.forEach.call(a,function(t){t.addEventListener(c,l)}),{destroy:function(){Array.prototype.forEach.call(a,function(t){t.removeEventListener(c,l)})}};if(d.string(t))return o=t,r=e,i=n,h(document.body,o,r,i);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList");var o,r,i,a,c,l,u,s,f}},function(t,n){n.node=function(t){return void 0!==t&&t instanceof HTMLElement&&1===t.nodeType},n.nodeList=function(t){var e=Object.prototype.toString.call(t);return void 0!==t&&("[object NodeList]"===e||"[object HTMLCollection]"===e)&&"length"in t&&(0===t.length||n.node(t[0]))},n.string=function(t){return"string"==typeof t||t instanceof String},n.fn=function(t){return"[object Function]"===Object.prototype.toString.call(t)}},function(t,e,n){var a=n(5);function i(t,e,n,o,r){var i=function(e,n,t,o){return function(t){t.delegateTarget=a(t.target,n),t.delegateTarget&&o.call(e,t)}}.apply(this,arguments);return t.addEventListener(n,i,r),{destroy:function(){t.removeEventListener(n,i,r)}}}t.exports=function(t,e,n,o,r){return"function"==typeof t.addEventListener?i.apply(null,arguments):"function"==typeof n?i.bind(null,document).apply(null,arguments):("string"==typeof t&&(t=document.querySelectorAll(t)),Array.prototype.map.call(t,function(t){return i(t,e,n,o,r)}))}},function(t,e){if("undefined"!=typeof Element&&!Element.prototype.matches){var n=Element.prototype;n.matches=n.matchesSelector||n.mozMatchesSelector||n.msMatchesSelector||n.oMatchesSelector||n.webkitMatchesSelector}t.exports=function(t,e){for(;t&&9!==t.nodeType;){if("function"==typeof t.matches&&t.matches(e))return t;t=t.parentNode}}},function(t,e,n){"use strict";n.r(e);var o=n(0),r=n.n(o),i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};function a(t,e){for(var n=0;n<e.length;n++){var o=e[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(t,o.key,o)}}function c(t){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,c),this.resolveOptions(t),this.initSelection()}var l=(function(t,e,n){return e&&a(t.prototype,e),n&&a(t,n),t}(c,[{key:"resolveOptions",value:function(t){var e=0<arguments.length&&void 0!==t?t:{};this.action=e.action,this.container=e.container,this.emitter=e.emitter,this.target=e.target,this.text=e.text,this.trigger=e.trigger,this.selectedText=""}}
8225,{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var t=this,e="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[e?"right":"left"]="-9999px";var n=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=n+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=r()(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=r()(this.target),this.copyText()}},{key:"copyText",value:function(){var e=void 0;try{e=document.execCommand(this.action)}catch(t){e=!1}this.handleResult(e)}},{key:"handleResult",value:function(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),document.activeElement.blur(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(t){var e=0<arguments.length&&void 0!==t?t:"copy";if(this._action=e,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(t){if(void 0!==t){if(!t||"object"!==(void 0===t?"undefined":i(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function(){return this._target}}]),c),u=n(1),s=n.n(u),f=n(2),d=n.n(f),h="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},p=function(t,e,n){return e&&y(t.prototype,e),n&&y(t,n),t};function y(t,e){for(var n=0;n<e.length;n++){var o=e[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(t,o.key,o)}}var m=(function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(v,s.a),p(v,[{key:"resolveOptions",value:function(t){var e=0<arguments.length&&void 0!==t?t:{};this.action="function"==typeof e.action?e.action:this.defaultAction,this.target="function"==typeof e.target?e.target:this.defaultTarget,this.text="function"==typeof e.text?e.text:this.defaultText,this.container="object"===h(e.container)?e.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=d()(t,"click",function(t){return e.onClick(t)})}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new l({action:this.action(e),target:this.target(e),text:this.text(e),container:this.container,trigger:e,emitter:this})}},{key:"defaultAction",value:function(t){return b("action",t)}},{key:"defaultTarget",value:function(t){var e=b("target",t);if(e)return document.querySelector(e)}},{key:"defaultText",value:function(t){return b("text",t)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(t){var e=0<arguments.length&&void 0!==t?t:["copy","cut"],n="string"==typeof e?[e]:e,o=!!document.queryCommandSupported;
8225return n.forEach(function(t){o=o&&!!document.queryCommandSupported(t)}),o}}]),v);function v(t,e){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,v);var n=function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}(this,(v.__proto__||Object.getPrototypeOf(v)).call(this));return n.resolveOptions(e),n.listenClick(t),n}function b(t,e){var n="data-clipboard-"+t;if(e.hasAttribute(n))return e.getAttribute(n)}e.default=m}],r.c=o,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=6).default;function r(t){if(o[t])return o[t].exports;var e=o[t]={i:t,l:!1,exports:{}};return n[t].call(e.exports,e,e.exports,r),e.l=!0,e.exports}var n,o});
8226(function () {
8227    var $popup = $('.modal__mobile-popup');
8228    var arrowHeight = 8;
8229    var facebookTemplate = "https://www.facebook.com/sharer/sharer.php?u=";
8230    var twitterTemplate = "https://twitter.com/share?status="
8231    var linkedInTemplate = "https://www.linkedin.com/sharing/share-offsite/?url="
8232     var recaptchaTemplate = '<div id="modal--recaptcha" class=" g-recaptcha" data-sitekey="{sitekey}"></div>'
8233    function setArrowButtonHeight() {
8234        $(".modal__mobile-arrow").css("bottom", $popup.height()-arrowHeight);
8235    }
8236
8237
8238
8239    $(document).ready(function(){
8240
8241        $(window).resize(function(){
8242            $(window.location.hash).closest(".modal__mobile-arrow").css("bottom", $popup.height()-arrowHeight);
8243        });
8244
8245        if(window.location.hash) {
8246            $(window.location.hash).closest(".modal__container").show();
8247            $(window.location.hash).closest(".modal__container").css("margin-bottom", "0");
8248            setTimeout(function() {
8249             setupRecaptcha()
8250             },1000)
8251        }
8252
8253        $('.modal__mobile-arrow').click(function(){
8254
8255                $(this).toggleClass("modal__mobile-arrow--open");
8256                $(this).css("bottom",  $(this).parent().find(".modal__mobile-popup").height()-arrowHeight);
8257
8258        });
8259
8260        $('.modal__close').click(function(){
8261            $(this).closest(".modal__container").hide();
8262            var noHashURL = window.location.href.replace(/#.*$/, '');
8263            window.history.replaceState('', document.title, noHashURL)
8264            $(".email-validation-error").removeClass("show-validation");
8265            $(".email-validation-mobile-error").removeClass("show-validation");
8266            $(".recaptcha-validation-error").removeClass("show-validation");
8267            $(".recaptcha-mobile-validation-error").removeClass("show-validation");
8268
8269            //turn off any error
8270        });
8271
8272        $('.js-copy').click(function (e) {
8273                e.preventDefault();
8274                var copyText = window.location.href;
8275
8276
8277                var dummy = document.createElement('input'),
8278                    text = window.location.href;
8279
8280                document.body.appendChild(dummy);
8281                dummy.value = text;
8282                dummy.select();
8283                document.execCommand('copy');
8284                document.body.removeChild(dummy);
8285
8286                $(this).tooltip({
8287                    content: "Copied!"
8288                });
8289
8290        });
8291
8292        $('.js-copy').mouseleave(function (e) {
8293            $(this).tooltip( "close" );
8294            $(this).tooltip({
8295                    content: "Copy?"
8296            });
8297        });
8298    });
8299
8300    $( window ).on('hashchange', function( e ) {
8301
8302        if(window.location.hash) {
8303         $(window.location.hash).closest(".modal__container").show();
8304         $(window.location.hash).closest(".modal__container").css("margin-bottom", "0");
8305         }
8306        setupRecaptcha()
8307
8308    });
8309
8310    function setupRecaptcha()
8311    {
8312        $('#modal--recaptcha').remove();
8313        if(!checkModalCookie() && $('.modal__content').length)
8314        {
8315             if (window.innerWidth >= 992) {
8316                var hashTemplate = recaptchaTemplate.replace("{siteKey}", $(window.location.hash).data("siteKey"))
8317                 $(window.location.hash).find(".recaptcha").before(hashTemplate)
8318               }
8319               else {
8320                    $(window.location.hash).find(".recaptcha--mobile").before(hashTemplate)
8321               }
8322               if(window.grecaptcha)
8323               {
8324               window.grecaptcha.render("modal--recaptcha", {
8325                     sitekey: "6LcF28YUAAAAAOpvSk72p8oHRBGvYWB-mAavWHG-"
8326                   });
8327                   }
8328
8329        }
8330        if(checkModalCookie()) {
8331            $('.modal__container .g-recaptcha').remove();
8332            $('.modal__container input').remove();
8333            $('.modal__container label').remove();
8334        }
8335    }
8336
8337
8338
8339
8340
8341    $(document).ready(function() {
8342         $('[data-toggle="tooltip"]').tooltip()
8343        $('.js-share').click(function(e) {
8344            e.preventDefault();
8345            var href = window.location.href.replace('#', '%23');
8346            var url = "" ;
8347            var title = $(this).closest('.modal__container').find('.js-title').text();
8348            var description = $(this).closest('.modal__container').find('.js-description').text();
8349            if($(this).hasClass('js-facebook'))
8350            {
8351                url += facebookTemplate;
8352                url += href;
8353            }
8354            else if($(this).hasClass('js-linkedIn'))
8355            {
8356                url += linkedInTemplate;
8357                url += href;
8358                url += ("&title=" + title);
8359                url += ("&summary=" + description);
8360            }
8361            else if($(this).hasClass('js-twitter'))
8362            {
8363                url += twitterTemplate;
8364                url += href;
8365                url += ("&text=" + title);
8366            }
8367
8368            window.open(url, 'fbShareWindow', 'height=450, width=550, top=' + ($(window).height() / 2 - 275) + ', left=' + ($(window).width() / 2 - 225) + ', toolbar=0, location=0, menubar=0, directories=0, scrollbars=0');
8369            return false;
8370        });
8371    });
8372
8373    $(document).ready(function() {
8374        var ipAddress;
8375        if($('.require-email-download').length > 0)
8376        {
8377            $.get('https://ipinfo.io/json?token=a142ff36811fed', function(data) {
8378                ipAddress = data.ip;
8379            });
8380        }
8381
8382        $('.require-email-download').click(function(e) {
8383            e.preventDefault();
8384           if(checkModalCookie())
8385           {
8386               window.location = $(this).closest('.modal__container').data('path');
8387           }
8388           else {
8389            var anchor = $(this).attr('anchor');
8390            var path = getResourcePath("#require-email-download-" + anchor);
8391            var email = validateEmail("require-email-download-input-" + anchor, ".email-validation-error");
8392            if (email == '') {
8393                return;
8394            }
8395            var recaptcha = validateRecaptcha(".recaptcha-validation-error");
8396            if (recaptcha == '') {
8397                return;
8398            }
8399
8400                //call servlet
8401                callDownloader(path, email, recaptcha);
8402            }
8403      
8404        });
8405
8406
8407        $('.require-email-mobile-download').click(function(e) {
8408            e.preventDefault();
8409            var recaptcha = $("#g-recaptcha-response").val();
8410            var anchor = $(this).attr('anchor');
8411            var path = getResourcePath("#require-email-download-" + anchor);            
8412            var email = validateEmail("require-email-mobile-download-input-" + anchor, ".email-validation-mobile-error");
8413            
8414            if (email == '') {
8415                return;
8416            }
8417            var recaptcha = validateRecaptcha(".recaptcha-mobile-validation-error");
8418            if (recaptcha == '') {
8419                return;
8420            }
8421            //call servlet
8422            callDownloader(path, email, recaptcha);   
8423        });
8424           
8425        function getResourcePath(resourceHook) {
8426            return [$(resourceHook).attr("parentPath"), $(resourceHook).attr("childPath")];
8427        }
8428
8429        function validateEmail(inputId, errorClass) {
8430            var inputEmail = document.getElementById(inputId).value;
8431            if (inputEmail.toLowerCase().match(
8432                /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
8433                )) {
8434                $(errorClass).removeClass("show-validation");
8435                //make sure to remove when modal is closed 
8436                return inputEmail;
8437            } else {            
8438                $(errorClass).addClass("show-validation");
8439                return '';
8440            }
8441        }
8442
8443        function validateRecaptcha(errorClass) {
8444            var recaptcha = window.grecaptcha.getResponse();
8445            if (recaptcha != null && recaptcha != '') {
8446                $(errorClass).removeClass("show-validation");
8447                return recaptcha;
8448            } else {
8449                $(errorClass).addClass("show-validation");
8450                return '';
8451            }
8452            
8453        }
8454
8455        function callDownloader(path, email, token) {
8456            //parent - component path
8457            //child - individual modal in this set of modals
8458            //email - of input
8459            //ipAddress
8460            //recaptcha token
8461            var url = '/bin/southern/global/downloadasset.json?'  +
8462                'parentPath=' + path[0] +
8463                '&childPath=' + path[1] +
8464                '&email=' + email +
8465                '&ipAddress=' + ipAddress +
8466                '&token=' + token;
8467            $.get(url, function (data, status) {
8468                if (data.assetReturned) {
8469                    createModalCookie();
8470                    setupRecaptcha()
8471                    window.location = data.asset;
8472
8473                } else {
8474                    console.log("Download Modal Servlet failed. See logs.")
8475                }
8476            });
8477
8478        }
8479    });
8480    function checkModalCookie()
8481    {
8482        var cookieString = 'modalEmailAcquired';
8483        return document.cookie.indexOf(cookieString) > -1;
8484    }
8485
8486    function createModalCookie()
8487    {
8488
8489        var cookieString = 'modalEmailAcquired';
8490        //1 year
8491        var expirationDate  = new Date();
8492        expirationDate = new Date(expirationDate.getTime() +1000*60*60*24*365);
8493        document.cookie = 'modal='+cookieString+'; expires='+expirationDate.toGMTString()+';';
8494    }
8495}());
8496var totalMarketingSwipers = [];
8497$(function () {
8498    function swiperSwitchBoard(index, active) {
8499        var swiper = '.swiper' + (index + 1);
8500        var next = '.next' + index;
8501        var prev = '.prev' + index;
8502        var swiper = new Swiper(swiper, {
8503            slidesPerView: 'auto',
8504            simulateTouch: true,
8505            resizeReInit: true,
8506            initialSlide: active,
8507            keyboard: {
8508                enabled: true,
8509            },
8510            navigation: {
8511                nextEl: next,
8512                prevEl: prev,
8513            },
8514            freeMode: {
8515                enabled: true,
8516                sticky: true,
8517            },
8518        });
8519        totalMarketingSwipers.push(swiper);
8520    }
8521
8522    function checkSliderButtons(currentTabList) {
8523        if (currentTabList[0].offsetWidth > currentTabList.parent()[0].offsetWidth) {
8524            var prevButton = currentTabList.closest('.marketing-tab__tab-container').find(".swiper-button-prev-custom");
8525            var nextButton = currentTabList.closest('.marketing-tab__tab-container').find(".swiper-button-next-custom");
8526            $(prevButton[0]).css("display", "flex");
8527            $(nextButton[0]).css("display", "flex");
8528
8529            return true;
8530        } else {
8531            var prevButton = currentTabList.closest('.marketing-tab__tab-container').find(".swiper-button-prev-custom");
8532            var nextButton = currentTabList.closest('.marketing-tab__tab-container').find(".swiper-button-next-custom");
8533            $(prevButton[0]).css("display", "none");
8534            $(nextButton[0]).css("display", "none");
8535            return false;
8536        }
8537    }
8538
8539      function createSwipers() {
8540          var tabs = $('.marketing-tab');
8541          var numberOfSwipers = [];
8542          tabs.each(function(index, tab) {
8543            var rightCheck = $(tab).find(".marketing-tab--right");
8544            var leftCheck = $(tab).find(".marketing-tab--left");
8545            var topCheck = $(tab).find(".marketing-tab--top");
8546            //todo add centered tab options here
8547            
8548            var parent = $(tab).find("ol");
8549            var activeTab = $(tab).find(".cmp-tabs__tab--active");
8550            if (activeTab.length == 0) {
8551                activeTab = parent.children()[0];
8552            }
8553           
8554            updateActiveTab(activeTab);
8555            setSingleTabColor(activeTab);
8556            updateActivePanel(activeTab, 0);
8557            if (((rightCheck.length == 1 || leftCheck.length == 1) && window.innerWidth < 990) || topCheck.length == 1) {
8558                if (checkSliderButtons(parent)) {
8559                    var swiperLevel = $(tab).find(".swiper-container");
8560                    var nextButton = $(tab).find(".swiper-button-next-custom");
8561                    var prevButton = $(tab).find(".swiper-button-prev-custom");
8562                    activeTab = $(activeTab).index();
8563                    nextButton.addClass("next" + (index));
8564                    prevButton.addClass("prev" + (index));
8565                    swiperLevel.addClass("swiper" + (index + 1));
8566                    swiperSwitchBoard(index, activeTab);
8567                } 
8568            } else {
8569                checkSliderButtons(parent);
8570            } 
8571          });
8572    }
8573
8574    $(".marketing-tab .swiper-slide").click(function () {
8575        var parent = $(this).parent();
8576        var previouslyActive = $(parent).attr("active-tab");
8577        $(parent[0].children[previouslyActive]).css({ "border-bottom": "4px s
8577olid " + "transparent" });
8578        setSingleTabColor(this);
8579        updateActiveTab(this);
8580        updateActivePanel(this, previouslyActive);
8581        
8582    });
8583
8584    $(".marketing-tab .swiper-slide").keydown(function (key) {
8585        //update bot coloring active tab and swiper
8586        var parent = $(this).parent()[0];
8587        var currentActive = $(parent).attr("active-tab");
8588        var children = $(parent)[0].children;
8589        if (key.keyCode == 37 || key.keyCode == 38) {
8590            if (currentActive != 0) {
8591                $(children[currentActive]).css({ "border-bottom": "4px solid " + "transparent" });
8592                var nextKey = $($(parent)[0].children[currentActive - 1]);
8593                setSingleTabColor(nextKey);
8594                updateActiveTab(nextKey);
8595                updateActivePanel(nextKey, currentActive);
8596            }
8597        }
8598
8599        if (key.keyCode == 39 || key.keyCode == 40) {
8600            if (currentActive != children.length - 1) {
8601                $(children[currentActive]).css({ "border-bottom": "4px s
8601olid " + "transparent" });
8602                var nextKey = $($(parent)[0].children[currentActive - -1]);
8603                setSingleTabColor(nextKey);
8604                updateActiveTab(nextKey);
8605                updateActivePanel(nextKey, currentActive);
8606            }
8607        }
8608    });
8609
8610    function hideAllContent() {
8611        if ($(".wcmmode-title-only").length == 0) {
8612            $(".marketing-tab--content").each(function (index, item) {
8613                $(item).css("display", "none");
8614            })
8615        }
8616    }
8617
8618    function setSingleTabColor(tab) {
8619        var colorCheck = $(tab).closest('.cmp-tabs.marketing-tab');
8620        var color = colorCheck[0].className.includes("dark") ? $(colorCheck[0]).attr("active-tab-color--dark") : $(colorCheck[0]).attr("active-tab-color--light");
8621        $(tab).css({ "border-bottom": "4px solid " + color });
8622    }
8623
8624    function updateActiveTab(tab) {
8625        var ol = $(tab).parent();
8626        $(ol[0]).attr("active-tab", $(tab).index());
8627    }
8628
8629    function updateActivePanel(tab, oldIndex) {
8630        var wrapper = $(tab).closest('.marketing-tab--layer').find(".bootstrap-content-wrapper");
8631        $($(wrapper[0]).children()[oldIndex]).css("display", "none");
8632        $($(wrapper[0]).children()[$(tab).index()]).css("display", "block");
8633    }
8634
8635    function destroySwipers() {
8636        while(totalMarketingSwipers.length > 0) {
8637            totalMarketingSwipers.shift().destroy();
8638        } 
8639    }
8640
8641    function adjustMarketingTabs() {
8642        destroySwipers();
8643        createSwipers();
8644    }
8645
8646    hideAllContent();
8647    destroySwipers();
8648    createSwipers();
8649    
8650    window.addEventListener('resize', adjustMarketingTabs);
8651    window.addEventListener("orientationchange", adjustMarketingTabs);
8652    
8653});
8654
8655
8656
8657$(function () {
8658
8659    function search(zipcode, component) {
8660        if (window.lookup.includes(zipcode)) {
8661            component.find(".results").show();
8662            component.find(".no-results").hide();
8663        } else {
8664            component.find(".results").hide();
8665            component.find(".no-results").show();
8666        }
8667    }
8668
8669    $(".lookup-form .btn").click(function (event) {
8670        event.preventDefault();
8671        console.log($(this))
8672        search($(this).prev().val(), $(this).closest('.lookup-form'));
8673    });
8674
8675    $(".lookup-form input").keydown(function (event) {
8676        if (event.which == 13) {
8677            search($(this).val(), $(this).closest('.lookup-form'));
8678        }
8679    });
8680});
8681$(function () {
8682    var width = $(window).width();
8683    var height = $(window).height();
8684    var $itemGrid = $('.item-grid');
8685    var itemGrid = $('.item-grid');
8686    var mobile = $('.item-grid').data('mobile');
8687    var tablet = $('.item-grid').data('tablet');
8688    var desktop = $('.item-grid').data('desktop');
8689    var style = "margin";
8690    var style2 = "margin-bottom";
8691    var widths = [768, 992, 1200];
8692
8693    $(document).ready(function(){
8694        itemGridAdjustMarginBasedOnPathWidth()
8695		$(document).resize(itemGridAdjustMarginBasedOnPathWidth());
8696		$(window).resize(itemGridAdjustMarginBasedOnPathWidth());
8697    });
8698
8699
8700
8701	function itemGridAdjustMarginBasedOnPathWidth() {
8702	    reset();
8703	    $itemGrid.each(function(index)  {
8704	        itemGrid = $(this);
8705	        mobile = itemGrid.data('mobile');
8706            tablet = itemGrid.data('tablet');
8707            desktop = itemGrid.data('desktop');
8708            if(width < widths[0])
8709            {
8710                itemGridAdjustMargin(mobile);
8711            }
8712            else if(width < widths[1])
8713            {
8714                itemGridAdjustMargin(tablet);
8715            }
8716            else {
8717                itemGridAdjustMargin(desktop);
8718            }
8719	    });
8720	}
8721
8722	function reset() {
8723	     width = $(window).width();
8724	     height = $(window).height();
8725	     $itemGrid.find('.item-grid__single').each(function(index) {
8726            $(this).find('.item-grid__link-container').css(style, "");
8727            $(this).css(style2, "");
8728         });
8729	}
8730
8731	function itemGridAdjustMargin(itemGridNum) {
8732	    var itemGridSingles = itemGrid.find('.item-grid__single');
8733	    var rows =  Math.ceil(itemGridSingles.length/itemGridNum);
8734	    itemGridSingles.each(function(index)  {
8735	        var currentRow = Math.ceil((index+1)/itemGridNum);
8736	        $(this).find('.item-grid__link-container').css(style, "0 10px");
8737	        if(index >= itemGridSingles.length - itemGridNum && rows === currentRow)
8738	        {
8739            	 $(this).css(style2, "0");
8740	        }
8741	    })
8742	}
8743
8744    window.onresize = itemGridAdjustMarginBasedOnPathWidth;
8745
8746});
8747(function($) {
8748    /** jQuery plugin for category-based content filtering
8749     *
8750     * Usage:
8751     *   <div class="item-filter">
8752     *     <div class="item-filter--title">Filter the Things</div>
8753     *     <form action="" class="item-filter--search">
8754     *       <input name="q">
8755     *     </form>
8756     *     <ul>
8757     *       <li class="filter-selector--all checked">All</li>
8758     *       <li class="filter-selector--category checked" data-tag-slug="things">Things</li>
8759     *       <li class="filter-selector--category checked" data-tag-slug="stuff">Stuff</li>
8760     *       <li class="filter-selector--category checked" data-tag-slug="whoozits">Whoozits</li>
8761     *     </ul>
8762     *     <ul class="filterable">
8763     *       <li class="filterable--item visible" data-tag-slugs="things stuff">Something</li>
8764     *       <li class="filterable--item visible" data-tag-slugs="things">Nothing</li>
8765     *       <li class="filterable--item visible" data-tag-slugs="whoozits">Everything</li>
8766     *     </ul>
8767     *     <div class="item-filter--close">Close</div>
8768     *   </div>
8769     *
8770     * An item_filter manages a bunch of categories and a bunch of categorized
8771     * items. It adds "visible" classes to items that should be visible based
8772     * on which categories have the "checked" class. You can use an optional
8773     * "all" selection to check/uncheck all categories: It follows along
8774     * sensibly as categories are individually checked and unchecked. When
8775     * "all" is checked, all items are visible, even those that have no
8776     * categories.
8777     */
8778
8779    // Single point of entry for the item_filter plugin. Individual actions are
8780    // described inside this function.
8781    //   $(...).item_filter()
8782    //   $(...).item_filter({...})
8783    //   $(...).item_filter('toggle_all');
8784    //   $(...).item_filter('toggle_category', category_node);
8785    //   $(...).item_filter('toggle_category', 'category-slug');
8786    //   $(...).item_filter('select_category', category_node);
8787    //   $(...).item_filter('select_category', 'category-slug');
8788    //   $(...).item_filter('refresh');
8789    //   $(...).item_filter('submit_search');
8790    $.fn.item_filter = function() {
8791
8792        if (arguments.length == 0) {
8793
8794            // .item_filter() -> initialize with default options
8795            var options = $.extend({}, DEFAULT_OPTIONS);
8796            this.each(function() {
8797                initialize_filter(this, options);
8798                return this;
8799            });
8800
8801        } else if (typeof arguments[0] === 'string') {
8802
8803            var action = arguments[0];
8804            switch (action) {
8805
8806                // .item_filter('toggle_all') -> toggle the 'all' category.
8807                // Turns all categories off or on.
8808                case 'toggle_all':
8809                    return toggle_all(this);
8810                    break;
8811
8812                // .item_filter('toggle_category', category_node);
8813                // .item_filter('toggle_category', 'category-slug');
8814                // Toggle a single category.
8815                case 'toggle_category':
8816                    return toggle_category(this, arguments[1]);
8817                    break;
8818
8819                // .item_filter('select_category', category_node);
8820                // .item_filter('select_category', 'category-slug');
8821                // Toggle a single category.
8822                case 'select_category':
8823                    return select_category(this, arguments[1]);
8824                    break;
8825
8826                // .item_filter('refresh') -> refresh visible items
8827                // This happens automatically when categories toggle, but it can also be
8828                // called manually when external code changes categories or the expected
8829                // result of the .extra_match option below.
8830                case 'refresh':
8831                    return update_visible_items(this);
8832                    break;
8833
8834                // .item_filter('submit_search') -> submit the search form
8835                // This refreshes visible items, and in mobile environments it hides the
8836                // filter.
8837                case 'submit_search':
8838                    return submit_search(this);
8839                    break;
8840
8841                default:
8842                    throw TypeError('Unsupported action for item_filter');
8843            }
8844
8845            return this;
8846
8847        } else if (typeof arguments[0] == 'object') {
8848
8849            // .item_filter({...}) -> initialize with options
8850
8851            var options = $.extend({}, DEFAULT_OPTIONS, arguments[0]);
8852            return this.each(function() {
8853                initialize_filter(this, options);
8854            });
8855
8856        } else {
8857            throw TypeError('Unsupported arguments for item_filter');
8858        }
8859    };
8860
8861    // Default options for every item_filter object. You can pass an object to
8862    // the item_filter constructor (above) to override these options.
8863    var DEFAULT_OPTIONS = {
8864        // html classes used for tagging nodes
8865        checked_class: 'checked',
8866        open_class: 'open',
8867        visible_class: 'visible',
8868
8869        // jquery data fields for storing category and item tags
8870        category_tag_data: 'tag-slug',
8871        item_tags_data: 'tag-slugs',
8872
8873        // the title, or how to find it ($filter.find(options.title_selector))
8874        $title: undefined,
8875        title_selector: '.item-filter--title',
8876
8877        // the close button, or how to find it ($filter.find(options.close_selector))
8878        $close: undefined,
8879        close_selector: '.item-filter--close',
8880
8881        // the search form, or how to find it ($filter.find(options.search_selector))
8882        $search: undefined,
8883        search_selector: '.item-filter--search',
8884
8885        // the search term field, or how to find it ($filter.find(options.search_field_selector))
8886        $search_field: undefined,
8887        search_field_selector: '.item-filter__input',
8888
8889        // the items, or how to find them $(options.items_selector)
8890        $items: undefined,
8891        items_selector: '.filterable--item',
8892
8893        // an "all" selection, or how to find it ($filter.find(options.all_selector))
8894        $all: undefined,
8895        all_selector: '.filter-selector--all',
8896
8897        // the categories, or how to find them ($filter.find(options.categories_selector))
8898        $categories: undefined,
8899        categories_selector: '.filter-selector--category',
8900
8901        // an optional extra function to further filter visibility
8902        //
8903        // After determining that an item has appropriate categories, item_filter
8904        // calls this function with the item's jquery object as an argument. If
8905        // the function returns true, the item is visible. If it returns false,
8906        // the item is not visible. Use this to incorporate factors other than
8907        // categories into filtering. The default shows all objects.
8908        extra_match: default_extra_match,
8909
8910        // an optional extra function to customize how to collect item search text
8911        //
8912        // When the filter search is active, the filter calls this function on
8913        // each item to find the item's searchable text. The default
8914        // implementation simply returns all text in the item.
8915        item_text: default_item_text
8916    };
8917
8918    // Initialize an item_filter selector with filterable items
8919    function initialize_filter(selector, options) {
8920        var $selector = $(selector);
8921        console.log("test")
8922        // Find related DOM nodes
8923        if (typeof options.$title === 'undefined') {
8924            options.$title = find_title($selector, options);
8925        }
8926        if (typeof options.$close === 'undefined') {
8927            options.$close = find_close($selector, options);
8928        }
8929        if (typeof options.$search === 'undefined') {
8930            options.$search = find_search($selector, options);
8931        }
8932        if (typeof options.$search_field === 'undefined') {
8933            options.$search_field = find_search_field($selector, options);
8934        }
8935        if (typeof options.$items === 'undefined') {
8936            options.$items = find_items($selector, options);
8937        }
8938        if (typeof options.$all === 'undefined') {
8939            options.$all = find_all($selector, options);
8940        }
8941        if (typeof options.$categories ==='undefined') {
8942            options.$categories = find_categories($selector, options);
8943        }
8944
8945        // If we have a search form, tie it into extra_match.
8946        if (options.$search.length > 0) {
8947            options.extra_match = match_search;
8948        }
8949
8950        // Set options
8951        filter_options($selector, options);
8952
8953        // Set up UI handlers
8954        options.$title.click(function() {
8955            $selector.toggleClass(options.open_class);
8956            return false;
8957        });
8958        options.$close.click(function() {
8959            $selector.toggleClass(options.open_class);
8960            return false;
8961        });
8962        options.$search.submit(function() {
8963            $selector.item_filter('submit_search');
8964            return false;
8965        });
8966        options.$all.click(function() {
8967            $selector.item_filter('toggle_all');
8968            return false;
8969        });
8970        options.$categories.click(function() {
8971            $selector.item_filter('select_category', $(this));
8972            return false;
8973        });
8974    }
8975
8976    // Get or set the filter options for a selector
8977    function filter_options($selector) {
8978        if (arguments.length > 1) {
8979            var value = arguments[1];
8980            return $selector.data('item_filter_options', value);
8981        } else {
8982            return $selector.data('item_filter_options');
8983        }
8984    }
8985
8986    // Find the title for a filter selector
8987    //
8988    // This is used only if $title isn't specified in construction options.
8989    function find_title($selector, options) {
8990        return $selector.find(options.title_selector);
8991    }
8992
8993    // Find the close button for a filter selector
8994    //
8995    // This is used only if $close isn't specified in construction options.
8996    function find_close($selector, options) {
8997        return $selector.find(options.close_selector);
8998    }
8999
9000    // Find the search form for a filter selector
9001    //
9002    // This is used only if $search isn't specified in construction options.
9003    function find_search($selector, options) {
9004        return $selector.find(options.search_selector);
9005    }
9006
9007    // Find the search term field for a filter selector
9008    //
9009    // This is used only if $search_field isn't specified in construction options.
9010    function find_search_field($selector, options) {
9011        return $selector.find(options.search_field_selector);
9012    }
9013
9014    // Find filterable items for a filter selector
9015    //
9016    // This is used only if $items isn't specified in construction options.
9017    function find_items($selector, options) {
9018        // For now we assume there's only one set of filterable items on the
9019        // page. If there are more than that, either set them explicitly in the
9020        // options or else write some interesting general code here to find them.
9021        return $(options.items_selector);
9022    }
9023
9024    // Find the "all" pseudo-category for a filter selector
9025    //
9026    // This is used only if $all isn't specified in construction options.
9027    function find_all($selector, options) {
9028        return $selector.find(options.all_selector);
9029    }
9030
9031    // Find categories for a filter selector
9032    //
9033    // This is used only if $categories isn't specified in construction options.
9034    function find_categories($selector, options) {
9035        return $selector.find(options.categories_selector);
9036    }
9037
9038    // Toggle the "all" pseudo-category for a filter selector
9039    //
9040    // All regular categories follow the "all" category: If this turns "all"
9041    // on then all categories are toggled on. If this turns "all" off then all
9042    // categories are toggled off. Filtered items are hidden or shown
9043    // according to the current category selection.
9044    function toggle_all($selector) {
9045        var options = filter_options($selector);
9046        if (options.$all.hasClass(options.checked_class)) {
9047            options.$all.removeClass(options.checked_class);
9048            options.$categories.removeClass(options.checked_class);
9049        } else {
9050            options.$all.addClass(options.checked_class);
9051            options.$categories.removeClass(options.checked_class);
9052        }
9053        update_visible_items($selector);
9054    }
9055
9056    // Toggle a single category for a filter selector
9057    //
9058    // Takes a category slug or DOM node. If after toggling all categories are
9059    // on the "all" pseudo-category is also turned on. If any are off then the
9060    // "all" pseudo-category is turned off. Filtered items are hidden or shown
9061    // according to the current category selection.
9062    function toggle_category($selector, category_or_slug) {
9063        var options = filter_options($selector);
9064        var $category = get_category($selector, category_or_slug);
9065
9066        $category.toggleClass(options.checked_class);
9067        update_all($selector);
9068        update_visible_items($selector);
9069    }
9070
9071    // Select a single category in a filter selector
9072    //
9073    // Takes a category slug or DOM node. If "all" is currently selected, then
9074    // this selects that category and deselects others. Otherwise, this
9075    // toggles the category. After updating, if any categories are off then
9076    // the "all" pseudo-category is turned off. Filtered items are hidden or
9077    // shown according to the current category selection.
9078    function select_category($selector, category_or_slug) {
9079        var options = filter_options($selector);
9080        var $category = get_category($selector, category_or_slug);
9081
9082        if (options.$all.hasClass(options.checked_class)) {
9083            options.$categories.removeClass(options.checked_class);
9084            $category.addClass(options.checked_class);
9085        } else {
9086            $category.toggleClass(options.checked_class);
9087        }
9088        update_all($selector);
9089        update_visible_items($selector);
9090    }
9091
9092    // Update the state of the "all" pseudo-category
9093    //
9094    // This is called internally in response to individual categories changing
9095    // to make sure the "all" selector accurately reflects the state of
9096    // selected categories. If all categories are on, turn "all" on. If any
9097    // are off, turn "all" off.
9098    //
9099    // This does not impact visible items. The caller is responsible for that.
9100    function update_all($selector) {
9101        var options = filter_options($selector),
9102            all_checked = true;
9103        options.$categories.each(function() {
9104            if ( ! $(this).hasClass(options.checked_class) ) {
9105                all_checked = false;
9106            }
9107        });
9108        options.$all.toggleClass(options.checked_class, all_checked);
9109    }
9110
9111    // Update visible items to match the category selection
9112    //
9113    // An item is visible only if any of its tag slugs match a checked
9114    // category. If "all" is checked then all items are visible irrespective
9115    // of categories. If an .extra_match option was passed at construction, it
9116    // will be called for each item that passed category filtering, and that
9117    // function will control visibility.
9118    function update_visible_items($selector) {
9119        var options = filter_options($selector);
9120
9121        options.$items.each(function() {
9122            var $this = $(this),
9123                is_visible = item_visible($this, $selector);
9124            $this.toggleClass(options.visible_class, is_visible);
9125        });
9126
9127        return $selector;
9128    }
9129
9130    // Handle a user request to submit the integrated search form
9131    //
9132    // This refreshes the visible items. In mobile environments it also hides
9133    // the filter categories.
9134    function submit_search($selector) {
9135        /* Handle search form submission by refreshing the visible items. Because
9136         * of the item_filter extra_match arg at construciton below, this calls
9137         * match_search to evaluate which items are visible. */
9138        var options = filter_options($selector);
9139
9140        update_visible_items($selector);
9141        $selector.removeClass(options.open_class);
9142        // On some mobile platforms the search cursor remains visible after
9143        // .item-filter is closed. Explicitly blur to work around this.
9144        options.$search_field.blur();
9145
9146        return $selector;
9147    }
9148
9149    // Is an individual item visible according to the selector?
9150    function item_visible($item, $selector) {
9151        var options = filter_options($selector);
9152        return (item_in_checked_category($item, $selector) &&
9153            options.extra_match($selector, $item));
9154    }
9155
9156    // By default show all items with matching categories. Override this
9157    // behavior in the extra_match constructor option to show only a subset of
9158    // those.
9159    function default_extra_match($selector, $item) {
9160        return true;
9161    }
9162
9163    // Filter visible items by search results
9164    //
9165    // This is an extra_match callback that filters items according to the
9166    // integrated search. The item_filter uses this automatically when a
9167    // search form is present.
9168    //
9169    // Here we do a naive string search to test for match. In an indexed
9170    // implementation we'd probably calculate search results on form submit,
9171    // then in this function just check if a particular question is present.
9172    function match_search($selector, $item) {
9173        var options = filter_options($selector),
9174            raw_term = options.$search_field.val() || '',
9175            term = raw_term.toLowerCase(),
9176            text = options.item_text($item).toLowerCase();
9177
9178        if (term == '') {
9179            return true;
9180        }
9181
9182        var idx = text.indexOf(term);
9183        return (idx > -1);
9184    }
9185
9186    // Get all the text from an item for use in matching
9187    function default_item_text($item) {
9188        return $item.text();
9189    }
9190
9191    // Is the item a member of a checked category?
9192    function item_in_checked_category($item, $selector) {
9193        var options = filter_options($selector),
9194            all_checked = options.$all.hasClass(options.checked_class);
9195
9196        if (all_checked) {
9197            return true;
9198        }
9199
9200        var categories = get_checked_categories($selector),
9201            slugs_str = $item.data(options.item_tags_data) || ' ',
9202            slugs = slugs_str.split(' ');
9203        for (var i = 0; i < slugs.length; i++) {
9204            if (categories[slugs[i]]) {
9205                return true;
9206            }
9207        }
9208        return false;
9209    }
9210
9211    // Get slugs for categories that are currently checked
9212    function get_checked_categories($selector) {
9213        var options = filter_options($selector),
9214            categories = {};
9215
9216        options.$categories.each(function() {
9217            var $this = $(this),
9218                slug = $this.data(options.category_tag_data);
9219            if ($this.hasClass(options.checked_class)) {
9220                categories[slug] = true;
9221            }
9222        });
9223
9224        return categories;
9225    }
9226
9227    // Given a category node or slug, return a category jq object. Returns
9228    // undefined if category not found.
9229    function get_category($selector, category_or_slug) {
9230        var options = filter_options($selector),
9231            $category;
9232
9233        if (typeof category_or_slug === 'string') {
9234            $category = options.$categories.filter(function() {
9235                return $(this).data(options.category_tag_data) == category_or_slug;
9236            });
9237        } else {
9238            $category = $(category_or_slug);
9239        }
9240        return $category;
9241    }
9242
9243    $(function() {
9244        $('.item-filter').item_filter();
9245        var SCREEN_SM = 768
9246        var display = false;
9247        $('.js-item__filters--select').click(function(){
9248        if(display)
9249        {
9250            $('.item-filter--categories').show();
9251            $('.item-filter--categories').css("width", $('.category').css("width"));
9252            $('.item_filter--selectbox .selectboxit').css('background', '#E6E6E6')
9253            $('.accordioncomp').css("min-height", '400px');
9254            display = false;
9255        }
9256        else
9257        {
9258            $('.item-filter--categories').hide();
9259            $('.item-filter--categories').css("width",'');
9260            $('.accordioncomp').css("min-height", '');
9261            $('.item_filter--selectbox .selectboxit').css('background', 'white')
9262            display = true;
9263        }
9264        return false;
9265        });
9266
9267        $(window).on('resize', function() {
9268            if($(this).width() > SCREEN_SM)
9269            {
9270                $('.item-filter--categories').show();
9271            }
9272            else {
9273                $('.item-filter--categories').hide();
9274            }
9275        });
9276    });
9277
9278
9279}(jQuery));
9280
9281document.addEventListener("DOMContentLoaded", () => {
9282  document.querySelectorAll(".interactive-image").forEach((interactiveImage) => {
9283    const instanceID = interactiveImage.id;
9284    const modalWrapper = document.querySelector(`.modal-wrapper#${instanceID}`);
9285    const closeButton = modalWrapper.querySelector(".close-icon.icon-x");
9286    const nextButton = modalWrapper.querySelector(".right-icon.icon-rightarrow");
9287    const prevButton = modalWrapper.querySelector(".left-icon.icon-leftarrow");
9288    const ul = interactiveImage.querySelector(".interactive-image-wrapper ul");
9289
9290    let currentIndex = -1;
9291    let previouslyFocusedElement = null;
9292    const interactiveImages = document.querySelectorAll(".interactive-image");
9293
9294    // Disable focus on background elements
9295    function disableBackgroundFocus() {
9296      interactiveImages.forEach((image) => {
9297        if (image !== modalWrapper) {
9298          image.setAttribute("inert", "true");
9299          image.setAttribute("aria-hidden", "true");
9300        }
9301      });
9302    }
9303
9304    // Enable focus on background elements
9305    function enableBackgroundFocus() {
9306      interactiveImages.forEach((image) => {
9307        image.removeAttribute("inert");
9308        image.setAttribute("aria-hidden", "false");
9309      });
9310    }
9311
9312    // Open Modal
9313    interactiveImage.querySelector(".interactive-image-wrapper").addEventListener("click", (event) => {
9314      if (event.target.tagName === "BUTTON") {
9315        previouslyFocusedElement = document.activeElement;
9316
9317        const button = event.target;
9318        button.classList.add("is-disabled");
9319        currentIndex = Array.from(ul.querySelectorAll("li")).indexOf(button.closest("li"));
9320
9321        updateModalContent(currentIndex);
9322        modalWrapper.classList.add("is-visible");
9323
9324        disableBackgroundFocus();
9325        trapFocus();
9326
9327        document.addEventListener("keydown", handleKeydown);
9328      }
9329    });
9330
9331    function updateModalContent(index) {
9332      const items = ul.querySelectorAll("li");
9333      const item = items[index];
9334      const button = item.querySelector("button");
9335    
9336      button.classList.add("is-disabled");
9337    
9338      const title = button.getAttribute("data-title");
9339      const description = button.getAttribute("data-description");
9340      const image = button.getAttribute("data-image");
9341      const imageText = button.getAttribute("data-imagetext");
9342    
9343      const contentDiv = modalWrapper.querySelector(".content");
9344      const titleWrapper = contentDiv.querySelector(".title-wrapper");
9345      let pointContainer = titleWrapper.querySelector(".point-container");
9346    
9347      // If .point-container does NOT exist, do NOT add the button
9348      if (pointContainer) {
9349        // Clear and reuse point-container
9350        pointContainer.innerHTML = "";
9351    
9352        // Clone the button and add it to the point-container
9353        const clonedButton = button.cloneNode(true);
9354        pointContainer.appendChild(clonedButton);
9355      }
9356    
9357      // Update title
9358      const existingTitle = titleWrapper.querySelector("h3");
9359      if (existingTitle) existingTitle.remove();
9360      if (title) {
9361        const h3 = document.createElement("h3");
9362        h3.textContent = title;
9363        h3.tabIndex = 0;
9364        titleWrapper.appendChild(h3);
9365      }
9366    
9367      // Update image
9368      const existingImageWrapper = contentDiv.querySelector(".image-wrapper");
9369      if (existingImageWrapper) existingImageWrapper.remove();
9370      if (image) {
9371        const imageWrapper = document.createElement("div");
9372        imageWrapper.className = "image-wrapper";
9373    
9374        const img = document.createElement("img");
9375        img.src = image;
9376        img.alt = imageText || title || "Image";
9377        img.className = "description-image";
9378        img.tabIndex = 0;
9379    
9380        imageWrapper.appendChild(img);
9381        contentDiv.appendChild(imageWrapper);
9382      }
9383    
9384      // Update description
9385      const existingDescription = contentDiv.querySelector(".description");
9386      if (existingDescription) existingDescription.remove();
9387      if (description) {
9388        const p = document.createElement("p");
9389        p.className = "description";
9390        p.innerHTML = description;
9391        p.tabIndex = 0;
9392        contentDiv.appendChild(p);
9393      }
9394    }
9395    
9396
9397    // Handle Next Button Click
9398    function goToNext() {
9399      if (!nextButton) return;
9400      const contentDiv = modalWrapper.querySelector(".content");
9401
9402      contentDiv.classList.add("transition-next");
9403      setTimeout(() => {
9404        contentDiv.classList.remove("transition-next");
9405
9406        const items = ul.querySelectorAll("li");
9407        currentIndex = (currentIndex + 1) % items.length;
9408        updateModalContent(currentIndex);
9409
9410        contentDiv.classList.add("transition-in-from-left");
9411        setTimeout(() => contentDiv.classList.remove("transition-in-from-left"), 200);
9412      }, 200);
9413    }
9414
9415    nextButton?.addEventListener("click", goToNext);
9416
9417    // Handle Previous Button Click
9418    function goToPrev() {
9419      if (!prevButton) return;
9420      const contentDiv = modalWrapper.querySelector(".content");
9421
9422      contentDiv.classList.add("transition-prev");
9423      setTimeout(() => {
9424        contentDiv.classList.remove("transition-prev");
9425
9426        const items = ul.querySelectorAll("li");
9427        currentIndex = (currentIndex - 1 + items.length) % items.length;
9428        updateModalContent(currentIndex);
9429
9430        contentDiv.classList.add("transition-in-from-right");
9431        setTimeout(() => contentDiv.classList.remove("transition-in-from-right"), 200);
9432      }, 200);
9433    }
9434
9435    prevButton?.addEventListener("click", goToPrev);
9436
9437    // Close Modal
9438    function closeModal() {
9439      modalWrapper.classList.remove("is-visible");
9440      currentIndex = -1;
9441
9442      document.removeEventListener("keydown", handleKeydown);
9443      enableBackgroundFocus();
9444
9445      if (previouslyFocusedElement) {
9446        previouslyFocusedElement.focus();
9447      }
9448    }
9449
9450    closeButton.addEventListener("click", closeModal);
9451    modalWrapper.addEventListener("click", (event) => {
9452      if (event.target === modalWrapper) {
9453        closeModal();
9454      }
9455    });
9456
9457    // Handle keyboard navigation with improved focus movement
9458    function handleKeydown(event) {
9459      if (!modalWrapper.classList.contains("is-visible")) return;
9460
9461      const focusableElements = modalWrapper.querySelectorAll(
9462        'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
9463      );
9464      const firstElement = focusableElements[0];
9465      const lastElement = focusableElements[focusableElements.length - 1];
9466      const activeIndex = Array.from(focusableElements).indexOf(document.activeElement);
9467
9468      switch (event.key) {
9469        case "Escape":
9470          closeModal();
9471          break;
9472        case "Enter":
9473            if (document.activeElement) document.activeElement.click();
9474            break;
9475        case "ArrowRight":
9476          if (nextButton) goToNext();
9477          break;
9478        case "ArrowLeft":
9479          if (prevButton) goToPrev();
9480          break;
9481        case "ArrowUp":
9482          event.preventDefault();
9483          if (activeIndex > 0) {
9484            focusableElements[activeIndex - 1].focus();
9485          } else {
9486            lastElement.focus(); // Wrap focus to last element
9487          }
9488          break;
9489        case "ArrowDown":
9490          event.preventDefault();
9491          if (activeIndex < focusableElements.length - 1) {
9492            focusableElements[activeIndex + 1].focus();
9493          } else {
9494            firstElement.focus(); // Wrap focus to first element
9495          }
9496          break;
9497        case "Tab":
9498          if (event.shiftKey && document.activeElement === firstElement) {
9499            event.preventDefault();
9500            lastElement.focus();
9501          } else if (!event.shiftKey && document.activeElement === lastElement) {
9502            event.preventDefault();
9503            firstElement.focus();
9504          }
9505          break;
9506      }
9507    }
9508
9509    function trapFocus() {
9510      const focusableElements = modalWrapper.querySelectorAll(
9511        'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
9512      );
9513      if (focusableElements.length > 0) {
9514        focusableElements[0].focus();
9515      }
9516    }
9517    // Open Modal - Save previous focus and trap focus inside modal
9518    interactiveImage
9519    .querySelector('.interactive-image-wrapper')
9520    .addEventListener('click', (event) => {
9521      if (event.target.tagName === 'BUTTON') {
9522        const button = event.target;
9523
9524        // Save previously focused element
9525        previouslyFocusedElement = document.activeElement;
9526
9527        button.classList.add('is-disabled');
9528        currentIndex = Array.from(ul.querySelectorAll('li')).indexOf(
9529          button.closest('li')
9530        );
9531
9532        updateModalContent(currentIndex);
9533        modalWrapper.classList.add('is-visible');
9534
9535        // Trap focus inside modal
9536        trapFocus();
9537
9538        document.addEventListener('keydown', handleKeydown);
9539      }
9540    });
9541  });
9542  
9543});
9544
9545$(document).ready(function () {
9546    function isAuthorMode() {
9547        return document.cookie.split("; ").some(cookie => cookie.startsWith("wcmmode=edit"));
9548    }
9549
9550    if (isAuthorMode()) {
9551        const $container = $(".interactive-container .interactive-image-wrapper");
9552        console.log($container);
9553
9554        if ($container.length) {
9555            $container.on("click", function (event) {
9556                console.log("clicked");
9557
9558                const rect = this.getBoundingClientRect();
9559                const offsetX = event.clientX - rect.left;
9560                const offsetY = event.clientY - rect.top;
9561
9562                const percentX = ((offsetX / rect.width) * 100).toFixed(2);
9563                const percentY = ((offsetY / rect.height) * 100).toFixed(2);
9564
9565                console.log(`top: ${offsetY}px; left: ${offsetX}px`);
9566                console.log(`top: ${percentY}%; left: ${percentX}%`);
9567            });
9568        }
9569    }
9570});
9571
9572
9573$(function(){
9574   var $galleryContainer = $(".gallery");
9575    $galleryContainer.each(function(){
9576        var galleryTemplate = $(this).data("galleryTemplate");
9577        var autoPlay = $(this).data("galleryAutoplay");
9578        var id = $(this).attr("id");
9579        var galleryEle = '#'+id+' .gallery-top';
9580        var thumbnailEle ='#'+id+' .gallery-thumbs';
9581        var paginationEle = '#'+id+ ' .swiper-pagination';
9582        var countSlides = $(galleryEle + ' .swiper-slide').length;
9583        var default_autoplay = 8000;
9584        var params = {
9585            keyboardControl: true,
9586            navigation: {
9587                nextEl: '.gallery-button-next',
9588                prevEl: '.gallery-button-prev',
9589            },
9590            loop: (countSlides >= 6),
9591            loopedSlides: 7,
9592            preloadImages: false,
9593            autoHeight: true,
9594            lazyLoading: true,
9595            lazyLoadingInPrevNext: true,
9596            lazyLoadingOnTransitionStart: true,
9597            lazyLoadingInPrevNextAmount: 2,
9598            pagination:{
9599                el: paginationEle,
9600                type: 'fraction'
9601            },
9602            a11y: true
9603        };
9604        if(autoPlay){
9605            params.autoplay={
9606                delay: default_autoplay,
9607            };
9608        }
9609
9610        var thumbnailConfig={
9611            spaceBetween: 0,
9612            centeredSlides: true,
9613            slidesPerView: (countSlides <= 6) ? 5 : 6,
9614            touchRatio: 0.2,
9615            slideToClickedSlide: true,
9616            loop: (countSlides >= 6), // loop is false when fewer slides than slidesPerView
9617            loopedSlides: 7
9618        };
9619
9620        // setup the gallery
9621        var galleryTop = new Swiper (galleryEle,params);
9622        if(galleryTemplate!=="nothumbnail"){
9623           var galleryThumbs = new Swiper(thumbnailEle, thumbnailConfig);
9624           galleryTop.thumbs = galleryThumbs;
9625           galleryTop.controller.control =galleryThumbs;
9626           galleryThumbs.controller.control = galleryTop;
9627
9628
9629        }
9630    });
9631
9632    var parseId = function(url) {
9633        var re = /(embed|video)\/(.+)?\??/;
9634        return re.exec(url)[2];
9635      };
9636
9637    //video modal
9638      $('.video--link').magnificPopup({
9639        type: 'iframe',
9640        mainClass: 'mfp-zoom-in',
9641        removalDelay: 500, /*needed for out animation*/
9642        midClick: true,
9643        iframe: {
9644          patterns: {
9645            youtube: {
9646              index: 'youtube.com/',
9647              id: parseId,
9648              src: 'https://www.youtube.com/embed/%id%?autoplay=1&rel=0'
9649            },
9650            vimeo: {
9651              index: 'vimeo.com/',
9652              id: parseId,
9653              src: '//player.vimeo.com/video/%id%?autoplay=1'
9654            }
9655          }
9656        }
9657      });
9658
9659      //brightcove video modal
9660      $('.video__brightcove--link').magnificPopup({
9661        type:'inline',
9662        mainClass: 'mfp-zoom-in',
9663        removalDelay: 500, /*needed for out animation*/
9664        midClick: true,
9665        callbacks: {
9666          close: function() {
9667            var allPlayers = videojs.getPlayers();
9668            for(var p in allPlayers){
9669              if(typeof p !=="undefined"){
9670       	        allPlayers[p].pause();
9671              }
9672            }
9673          }
9674        }
9675      });
9676
9677
9678});
9679
9680
9681$(function() {
9682  // initiate polyfill
9683  function resizeHeroBannerOnMobile() {
9684    if ($(window).outerWidth() <= 992) {
9685        var contentHeight = $('.wrap-hero-content').height();
9686        $('.hero-banner--on-image .wrap-hero-content').parent().find('.hero-banner__img--mobile .hero-banner__img--container').height(contentHeight)
9687    }
9688  }
9689
9690  $(document).ready(function() {
9691    if (document.querySelector('.hero-banner__img picture img')) {
9692      var objectFit = new ObjectFitPolyfill('.hero-banner__img picture img');
9693      $('.hero-banner__img picture img').css('opacity', '1');
9694      $('.hero-banner__img--mobile picture img').css('width', '100%');
9695      $('.hero-banner__img--mobile picture img').css('object-fit', 'cover');
9696
9697    }
9698    resizeHeroBannerOnMobile();
9699    $(window).on('resize', function() {
9700        resizeHeroBannerOnMobile();
9701    });
9702  });
9703});
9704
9705$(function () {
9706  var mobileNavBreakpoint = 991;
9707
9708  function updateHeaderNavOffset() {
9709    var $header = $('.header--version-2.site-header').first();
9710    var headerHeight = Math.ceil($header.outerHeight() || 48);
9711
9712    document.documentElement.style.setProperty(
9713      '--header-mobile-offset',
9714      headerHeight + 'px'
9715    );
9716  }
9717
9718  function updateNavContainer() {
9719    updateHeaderNavOffset();
9720
9721    $('.header--version-2 .header__nav').each(function () {
9722      var $nav = $(this);
9723      var $navContainer = $nav.children('.header__nav-container');
9724      var $buttonContainer = $nav.children('.header__nav-button-container');
9725      var $mainNav = $nav.children('.header__main-nav');
9726      var $auxNav = $nav.children('.header__aux-nav');
9727
9728      if (!$mainNav.length && $navContainer.length) {
9729        $mainNav = $navContainer.children('.header__main-nav');
9730      }
9731
9732      if (!$auxNav.length && $navContainer.length) {
9733        $auxNav = $navContainer.children('.header__aux-nav');
9734      }
9735
9736      function restoreAuxButton() {
9737        if ($buttonContainer.length) {
9738          $auxNav.find('.aux-nav__list').first().append(
9739            $buttonContainer.children('.aux-nav__item--button')
9740          );
9741          $buttonContainer.remove();
9742        }
9743      }
9744
9745      if (!$mainNav.length || !$auxNav.length) {
9746        restoreAuxButton();
9747        if ($navContainer.length) {
9748          $navContainer.children('.header__main-nav, .header__aux-nav').insertBefore(
9749            $navContainer
9750          );
9751          $navContainer.remove();
9752        }
9753        return;
9754      }
9755
9756      if (window.innerWidth <= mobileNavBreakpoint) {
9757        if (!$navContainer.length) {
9758          $navContainer = $('<div class="header__nav-container"></div>');
9759          $mainNav.before($navContainer);
9760          $navContainer.append($mainNav, $auxNav);
9761        }
9762        if (!$buttonContainer.length) {
9763          $buttonContainer = $('<div class="header__nav-button-container"></div>');
9764          $navContainer.after($buttonContainer);
9765        }
9766        $buttonContainer.append($auxNav.find('.aux-nav__item--button'));
9767      } else if ($navContainer.length) {
9768        restoreAuxButton();
9769        $navContainer.children('.header__main-nav, .header__aux-nav').insertBefore(
9770          $navContainer
9771        );
9772        $navContainer.remove();
9773      }
9774    });
9775  }
9776
9777  updateNavContainer();
9778  $(window).on('resize', function () {
9779    updateNavContainer();
9780    updateHeaderNavOffset();
9781  });
9782
9783  var parser = new UAParser();
9784  /********************
9785		Main Navigation
9786	*********************/
9787  var desktop = true;
9788  var parser = new UAParser();
9789  if (
9790    parser.getDevice().type == 'mobile' ||
9791    parser.getDevice().type == 'tablet'
9792  ) {
9793    desktop = false;
9794  }
9795
9796  //nav
9797  var $trigger = $('.header--version-2 .header__menu-trigger');
9798  var $nav = $('.header--version-2 .header__nav');
9799
9800  //sub nav
9801  var $triggerSubNav = $('.level-one__item .main-nav__link .main-nav__icon');
9802  var $subNav = $('.header--version-2 .sub-nav__wrapper');
9803
9804  //search
9805  var $triggerSearch = $('.header--version-2 .header__search-trigger');
9806  var $searchPopup = $('.header__search--global.header__search-popup');
9807  var $closePopUp = $('.header__search--global .header__search-trigger--close');
9808
9809  //Blackout
9810  var $headerBlackout = $('.header-blackout');
9811
9812  var $soloPrimaryLinks = $(
9813    '.main-nav__item.level-one__item:not(.main-nav__item--has-children'
9814  );
9815
9816  function setHeaderNavState(isOpen) {
9817    $('.site-header').css('position', 'fixed');
9818    $('.site-header').css('top', 0);
9819    $('.site-header').css('width', '100%');
9820    $('.site-header').css('height', 'auto');
9821    updateHeaderNavOffset();
9822  }
9823
9824  $trigger.on('click', function (e) {
9825    e.preventDefault();
9826    $this = $(this);
9827    $trigger.toggleClass('is-active');
9828    $nav.toggleClass('is-active');
9829
9830    $('html, body').toggleClass('nav-open', $trigger.hasClass('is-active'));
9831
9832    $subNav.slideUp();
9833    $headerBlackout.toggleClass('is-active');
9834    //hide search popup
9835    $searchPopup.removeClass('is-active');
9836
9837    if ($trigger.hasClass('is-active')) {
9838      setHeaderNavState(true);
9839    } else {
9840      setHeaderNavState(false);
9841      cleanNav();
9842    }
9843  });
9844
9845  $('.level-one__icon').on('click', function (e) {
9846    e.preventDefault();
9847    $this = $(this);
9848    if ($this.hasClass('is-active')) {
9849      returnOne($this);
9850    } else {
9851      $triggerSubNav.removeClass('is-active');
9852      $subNav.hide();
9853      $this.addClass('is-active').parent().next().show();
9854      var $otherTriggerSubNav = $(
9855        '.level-one__icon.main-nav__icon:not(".is-active")'
9856      );
9857      $otherTriggerSubNav.parent().hide();
9858      $this.parent().next().addClass('active');
9859      $this.parent().addClass('active-link');
9860      $('.aux-nav__item:not(.aux-nav__item--button)').hide();
9861      $soloPrimaryLinks.hide();
9862    }
9863  });
9864
9865  $('.level-two__icon').on('click', function (e) {
9866    e.preventDefault();
9867    $this = $(this);
9868    if ($this.hasClass('is-active')) {
9869      returnTwo($this);
9870    } else {
9871      $this.addClass('is-active');
9872      $this.parent().parent().addClass('level-two--active');
9873      $('.level-two--item:not(.level-two--active)').hide();
9874      var $otherTriggerSubNav = $(
9875        '.level-one__item .main-nav__link .main-nav__icon:not(".is-active")'
9876      );
9877      $otherTriggerSubNav.parent().hide();
9878      $('.active-link').hide();
9879      $this.parent().addClass('active-link-two');
9880      $this.parent().next().addClass('active');
9881      $this.parent().next().addClass('active-two');
9882      $('.header__nav').addClass('grey-one');
9883      $this.parent().addClass('active-link-style');
9884    }
9885  });
9886
9887  $('.level-three__icon').on('click', function (e) {
9888    e.preventDefault();
9889    $this = $(this);
9890
9891    if ($this.hasClass('is-active')) {
9892      returnThree($this);
9893    } else {
9894      $this.parent().addClass('active-link-style');
9895      $this.addClass('is-active');
9896      $this.parent().parent().addClass('level-three--active');
9897      $('.level-three--item:not(.level-three--active)').hide();
9898      var $otherTriggerSubNav = $(
9899        '.level-three__icon .main-nav__icon:not(".is-active")'
9900      );
9901      $otherTriggerSubNav.parent().hide();
9902      $('.level-two--active .level-two-link').hide();
9903      $this.parent().next().addClass('active');
9904      $('.header__nav').addClass('grey-two');
9905      $('.header__nav').removeClass('grey-one');
9906    }
9907  });
9908  $('.dropdownv2 p.aux-nav__link .aux-nav__text').on('click', function (e) {
9909    if (window.innerWidth > 991 || !desktop) {
9910      return;
9911    }
9912    e.preventDefault();
9913    $this = $(this).parent().find('.aux-nav-js');
9914
9915    if ($this.hasClass('is-active')) {
9916      returnAux($this);
9917    } else {
9918      $('.header__main-nav').hide();
9919      $this.addClass('is-active');
9920      $(this).parent().parent().addClass('aux-active');
9921      $(this).parent().next().show();
9922      $('.aux-nav__item:not(.aux-active, .aux-nav__item--button)').hide();
9923    }
9924  });
9925
9926  $('.aux-nav-js').on('click', function (e) {
9927    if (window.innerWidth > 991 || !desktop) {
9928      return;
9929    }
9930    e.preventDefault();
9931    $this = $(this);
9932
9933    if ($this.hasClass('is-active')) {
9934      returnAux($this);
9935    } else {
9936      $('.header__main-nav').hide();
9937      $this.addClass('is-active');
9938      $(this).parent().parent().addClass('aux-active');
9939      $(this).parent().next().show();
9940      $('.aux-nav__item:not(.aux-active, .aux-nav__item--button)').hide();
9941    }
9942  });
9943
9944  $triggerSearch.on('click', function (e) {
9945    e.preventDefault();
9946
9947    $this = $(this);
9948    $this.toggleClass('is-active');
9949    $searchPopup.toggleClass('is-active');
9950  });
9951
9952  $closePopUp.on('click', function (e) {
9953    e.preventDefault();
9954    $triggerSearch.removeClass('is-active');
9955    $searchPopup.removeClass('is-active');
9956  });
9957
9958  function cleanNav() {
9959    var aux = $('.aux-nav-js');
9960    var three = $('.level-three__icon');
9961    var two = $('.level-two__icon');
9962    var one = $('.level-one__icon');
9963
9964    if (aux.hasClass('is-active')) {
9965      returnAux(aux);
9966      return;
9967    }
9968    returnThree(three);
9969    returnTwo(two);
9970    returnOne(one);
9971  }
9972
9973  function returnAux(aux) {
9974    $('.header__main-nav').show();
9975    aux.removeClass('is-active');
9976    $(aux).parent().parent().removeClass('aux-active');
9977    $('.aux-nav__item').show();
9978    $('.aux-nav__sub-nav').hide();
9979  }
9980
9981  function returnThree(three) {
9982    $('.level-two--active .level-two-link').show();
9983    three.removeClass('is-active');
9984    $('.level-three--item').show();
9985    $('.level-three--active').removeClass('level-three--active');
9986    $('.level-four').removeClass('active');
9987    $('.header__nav').addClass('grey-one');
9988    $('.header__nav').removeClass('grey-two');
9989    three.parent().removeClass('active-link-style');
9990  }
9991  function returnTwo(two) {
9992    $('.active-link').show();
9993    two.removeClass('is-active');
9994    $('.level-two--item').show();
9995    $('.level-two--active').removeClass('level-two--active');
9996    $('.level-three').removeClass('active');
9997    $('.header__nav').removeClass('grey-one');
9998    two.parent().removeClass('active-link-style');
9999  }
10000  function returnOne(one) {
10001    $triggerSubNav.parent().show();
10002    $triggerSubNav.removeClass('is-active');
10003    $subNav.hide();
10004    $triggerSubNav.parent().next().removeClass('active');
10005    $('.aux-nav__item').show();
10006    one.parent().removeClass('active-link');
10007    $soloPrimaryLinks.show();
10008  }
10009});
10010
10011var parser = new UAParser();
10012if (
10013  parser.getDevice().type == 'mobile' ||
10014  parser.getDevice().type == 'tablet'
10015) {
10016  setupNav();
10017}
10018
10019function setupNav() {
10020  $('.dropdownv2 p.aux-nav__link .aux-nav__text').on('click', function (e) {
10021    e.preventDefault();
10022    $this = $(this).parent().find('.aux-nav-js');
10023
10024    if ($this.hasClass('is-active')) {
10025      returnAux($this);
10026    } else {
10027      $('.header__main-nav').hide();
10028      $this.addClass('is-active');
10029      $(this).parent().parent().addClass('aux-active');
10030      $(this).parent().next().show();
10031      $('.aux-nav__item:not(.aux-active, .aux-nav__item--button)').hide();
10032    }
10033  });
10034
10035  $('.aux-nav-js').on('click', function (e) {
10036
10037    e.preventDefault();
10038    $this = $(this);
10039
10040    if ($this.hasClass('is-active')) {
10041      returnAux($this);
10042    } else {
10043      $('.header__main-nav').hide();
10044      $this.addClass('is-active');
10045      $(this).parent().parent().addClass('aux-active');
10046      $(this).parent().next().show();
10047      $('.aux-nav__item:not(.aux-active, .aux-nav__item--button)').hide();
10048    }
10049  });
10050  // Select the element with the class "headerv2"
10051  var headerElement = document.querySelector('.headerv2');
10052  var headerTrigger = document.querySelector(
10053    '.header__menu-trigger.header__menu-trigger--hamburger'
10054  );
10055  var searchTrigger = document.querySelector(
10056    '.header__menu-trigger--search.header__menu-trigger'
10057  );
10058  // Check if the element is found
10059  if (headerElement) {
10060    // Add a new class to the element
10061    headerElement.classList.add('enable-mobile');
10062    headerElement.parentElement.parentElement.parentElement.parentElement.parentElement.classList.add(
10063      'enable-mobile'
10064    );
10065
10066    if (headerTrigger) {
10067      headerTrigger.style.position = 'absolute';
10068      headerTrigger.style.left = '5px';
10069    }
10070    if (searchTrigger) {
10071      searchTrigger.style.position = 'absolute';
10072      searchTrigger.style.right = '5px';
10073    }
10074  }
10075}
10076
10077function returnAux(aux) {
10078  $('.header__main-nav').show();
10079  aux.removeClass('is-active');
10080  $(aux).parent().parent().removeClass('aux-active');
10081  $('.aux-nav__item').show();
10082  $('.aux-nav__sub-nav').hide();
10083}
10084
10085var parser = new UAParser();
10086$(window).on('load', function () {
10087  function handleResizeEvent() {
10088    if (
10089      parser.getDevice().type == 'mobile' ||
10090      parser.getDevice().type == 'tablet'
10091    ) {
10092      return;
10093    }
10094    const windowWidth = window.innerWidth;
10095    const wrapper = document.querySelector('.header--version-2 .sub-nav__wrapper');
10096    let widthAvailable = windowWidth;
10097
10098    // Adjust widthAvailable by subtracting the description width, if available
10099    if (wrapper) {
10100      const description = wrapper.querySelector('.header--version-2 .sub-nav__description');
10101      if (description) {
10102        widthAvailable -= Math.floor(
10103          parseFloat(getComputedStyle(description).width)
10104        );
10105      }
10106    }
10107
10108    // Query level elements once for efficiency
10109    const levelElements = document.querySelectorAll(
10110      '.header--version-2 .sub-nav__list.level-two, .sub-nav__list.level-three, .sub-nav__list.level-four'
10111    );
10112
10113    // Only apply custom widths when window width is between 992 and 1300
10114    const customWidthRequired = windowWidth > 991 && windowWidth < 1400;
10115
10116    levelElements.forEach((levelElement) => {
10117      levelElement.style.width = customWidthRequired
10118        ? `${widthAvailable}px`
10119        : '';
10120      if (levelElement.classList.contains('level-four')) {
10121        const subNavItems = levelElement.querySelectorAll('.sub-nav__item');
10122        subNavItems.forEach((item) => {
10123          item.style.width = customWidthRequired
10124            ? `${widthAvailable * 0.20}px`
10125            : '';
10126            item.style.minWidth = customWidthRequired
10127            ? `${widthAvailable * 0.20}px`
10128            : '';
10129            
10130        });
10131      } else {
10132        const subNavItems = levelElement.querySelectorAll('.sub-nav__item');
10133        subNavItems.forEach((item) => {
10134          item.style.width = customWidthRequired
10135            ? `${widthAvailable * 0.25}px`
10136            : '';
10137        });
10138      }
10139    });
10140  }
10141
10142  // Initial call and binding to resize
10143  handleResizeEvent();
10144  $(window).on('resize', handleResizeEvent);
10145});
10146
10147window.addEventListener('resize', () => {
10148  if (window.innerWidth > 991) {
10149    $('.experiencefragment:not(.enable-mobile) .global-header.site-header.header--version-2').css('position', 'fixed');
10150    $('.experiencefragment:not(.enable-mobile) .global-header.site-header.header--version-2').css('width', '100%');
10151    $('.experiencefragment:not(.enable-mobile) .global-header.site-header.header--version-2').css('height', 'auto');
10152  } else {
10153    if ($('.experiencefragment:not(.enable-mobile) .header--version-2 .header-blackout').hasClass('is-active')) {
10154      $('.experiencefragment:not(.enable-mobile) .global-header.site-header.header--version-2').css('position','relative');
10155      $('.experiencefragment:not(.enable-mobile) .global-header.site-header.header--version-2').css('height', '100%');
10156    }
10157  }
10158});
10159
10160
10161window.addEventListener('load', () => {
10162  if (window.innerWidth > 991) {
10163    $('.experiencefragment:not(.enable-mobile) .global-header.site-header.header--version-2').css('position', 'fixed');
10164    $('.experiencefragment:not(.enable-mobile) .global-header.site-header.header--version-2').css('width', '100%');
10165    $('.experiencefragment:not(.enable-mobile) .global-header.site-header.header--version-2').css('height', 'auto');
10166  } else {
10167    if ($('.experiencefragment:not(.enable-mobile) .header--version-2 .header-blackout').hasClass('is-active')) {
10168      $('.experiencefragment:not(.enable-mobile) .global-header.site-header.header--version-2').css('position','relative');
10169      $('.experiencefragment:not(.enable-mobile) .global-header.site-header.header--version-2').css('height', '100%');
10170    }
10171  }
10172});
10173
10174$(function () {
10175	var mobileNavBreakpoint = 991;
10176
10177	function updateHeaderNavOffset() {
10178		var $header = $('.global-header.site-header:not(.header--version-2)').first();
10179		var headerHeight = Math.ceil($header.outerHeight() || 48);
10180
10181		document.documentElement.style.setProperty('--header-mobile-offset', headerHeight + 'px');
10182	}
10183
10184	function updateNavContainer() {
10185		updateHeaderNavOffset();
10186
10187		$('.global-header:not(.header--version-2) .header__nav').each(function () {
10188			var $nav = $(this);
10189			var $navContainer = $nav.children('.header__nav-container');
10190			var $buttonContainer = $nav.children('.header__nav-button-container');
10191			var $mainNav = $nav.children('.header__main-nav');
10192			var $auxNav = $nav.children('.header__aux-nav');
10193
10194			if (!$mainNav.length && $navContainer.length) {
10195				$mainNav = $navContainer.children('.header__main-nav');
10196			}
10197
10198			if (!$auxNav.length && $navContainer.length) {
10199				$auxNav = $navContainer.children('.header__aux-nav');
10200			}
10201
10202			function restoreAuxButton() {
10203				if ($buttonContainer.length) {
10204					$auxNav.find('.aux-nav__list').first().append($buttonContainer.children('.aux-nav__item--button'));
10205					$buttonContainer.remove();
10206				}
10207			}
10208
10209			if (!$mainNav.length || !$auxNav.length) {
10210				restoreAuxButton();
10211				if ($navContainer.length) {
10212					$navContainer.children('.header__main-nav, .header__aux-nav').insertBefore($navContainer);
10213					$navContainer.remove();
10214				}
10215				return;
10216			}
10217
10218			if (window.innerWidth <= mobileNavBreakpoint) {
10219				if (!$navContainer.length) {
10220					$navContainer = $('<div class="header__nav-container"></div>');
10221					$mainNav.before($navContainer);
10222					$navContainer.append($mainNav, $auxNav);
10223				}
10224				if (!$buttonContainer.length) {
10225					$buttonContainer = $('<div class="header__nav-button-container"></div>');
10226					$navContainer.after($buttonContainer);
10227				}
10228				$buttonContainer.append($auxNav.find('.aux-nav__item--button'));
10229			} else if ($navContainer.length) {
10230				restoreAuxButton();
10231				$navContainer.children('.header__main-nav, .header__aux-nav').insertBefore($navContainer);
10232				$navContainer.remove();
10233			}
10234		});
10235	}
10236
10237	updateNavContainer();
10238	$(window).on('resize', function () {
10239		updateNavContainer();
10240		updateHeaderNavOffset();
10241	});
10242
10243	/********************
10244		Main Navigation
10245	*********************/
10246
10247	//nav
10248	var $trigger = $('.global-header:not(.header--version-2) .header__menu-trigger');
10249	var $nav = $('.global-header:not(.header--version-2) .header__nav');
10250
10251	//sub nav
10252	var $triggerSubNav = $('.global-header:not(.header--version-2) .main-nav__item--has-children .main-nav__icon')
10253	var $subNav = $('.global-header:not(.header--version-2) .sub-nav__wrapper');
10254
10255	//search
10256	var $triggerSearch = $('.global-header:not(.header--version-2) .header__search-trigger');
10257	var $searchPopup = $('.header__search--global.header__search-popup');
10258	var $closePopUp = $('.header__search--global .header__search-trigger--close');
10259
10260	//Blackout
10261	var $headerBlackout = $('.header-blackout');
10262
10263	function setHeaderNavState(isOpen) {
10264		$('.site-header').css("position", 'fixed');
10265		$('.site-header').css("top", 0);
10266		$('.site-header').css("width", '100%');
10267		$('.site-header').css("height", 'auto');
10268		updateHeaderNavOffset();
10269	}
10270
10271	$trigger.on("click", function (e) {
10272		e.preventDefault();
10273		$this = $(this);
10274		$trigger.toggleClass('is-active');
10275		$nav.toggleClass('is-active');
10276
10277		$('html, body').toggleClass('nav-open', $trigger.hasClass('is-active'));
10278
10279		$subNav.slideUp();
10280		$headerBlackout.toggleClass('is-active');
10281		//hide search popup
10282		$searchPopup.removeClass('is-active');
10283
10284		if($trigger.hasClass('is-active'))
10285		{
10286		    setHeaderNavState(true);
10287		}
10288		else {
10289		    setHeaderNavState(false);
10290		}
10291	});
10292
10293	$triggerSubNav.on("click", function (e) {
10294		e.preventDefault();
10295		$this = $(this);
10296
10297		if ($this.hasClass('is-active')) {
10298			$triggerSubNav.removeClass('is-active');
10299			$subNav.slideUp();
10300		} else {
10301			$triggerSubNav.removeClass('is-active');
10302			$subNav.slideUp();
10303			$this.addClass('is-active').parent().next().slideDown();
10304		}
10305	})
10306
10307	$triggerSearch.on("click", function (e) {
10308		e.preventDefault();
10309
10310		$this = $(this);
10311		$this.toggleClass('is-active');
10312		$searchPopup.toggleClass('is-active');
10313	});
10314
10315	$closePopUp.on("click", function (e) {
10316		e.preventDefault();
10317		$triggerSearch.removeClass('is-active');
10318		$searchPopup.removeClass('is-active');
10319	});
10320
10321	$headerBlackout.on("click", function (e) {
10322		e.preventDefault();
10323		$trigger.removeClass('is-active');
10324		$nav.removeClass('is-active');
10325		$('html, body').removeClass('nav-open');
10326		$triggerSubNav.removeClass('is-active')
10327		$subNav.slideUp();
10328	});
10329
10330    var experiencefragment = $('.experiencefragment');
10331    function setHeader() {
10332
10333        if(experiencefragment.length > 0)
10334        {
10335            $(experiencefragment[0]).css("height", '0px');
10336        }
10337        $('.global-header.site-header:not(.header--version-2)').css("position", 'fixed');
10338        $('.global-header.site-header:not(.header--version-2)').css("width", '100%');
10339        $('.global-header.site-header:not(.header--version-2)').css("height", 'auto');
10340        $('.experiencefragment:first-of-type + .responsivegrid').css("margin-top", $('.site-header').height());
10341    }
10342
10343	$(document).ready(function(){
10344	       setHeader();
10345	});
10346
10347	$(window).on('resize', function() {
10348            setHeader();
10349	});
10350
10351	setHeader();
10352
10353
10354
10355
10356});
10357
10358$(function () {
10359	/********************
10360		AUX Navigation
10361	*********************/
10362
10363	//sub nav
10364	var $triggerSubNav = $('.global-header:not(.header--version-2) .aux-nav__item--dropdown .aux-nav__icon')
10365	var $subNav = $('.global-header:not(.header--version-2) .aux-nav__sub-nav');
10366
10367	$triggerSubNav.on("click", function (e) {
10368		e.preventDefault();
10369		$this = $(this);
10370
10371		if ($this.hasClass('is-active')) {
10372			$triggerSubNav.removeClass('is-active');
10373			$subNav.slideUp();
10374		} else {
10375			$triggerSubNav.removeClass('is-active');
10376			$subNav.slideUp();
10377			$this.addClass('is-active').parent().next().slideDown();
10378		}
10379	})
10380
10381
10382
10383
10384});
10385
10386$('input[name="q"]').change(function() {
10387    $('input[name="query"]').val($(this).val());
10388});
10389var mapList = [];
10390var mapById = {}; // Store maps by their ID for reliable lookup
10391var mapMarkersByCategory = {}; // Store markers organized by category for filtering
10392var mapClusterers = {}; // Store clusterers for each map
10393var openInfoWindows = {}; // Store the active info window for each map
10394
10395// Helper function to check if we're on mobile
10396function isMobileDevice() {
10397  return window.innerWidth <= 768;
10398}
10399
10400// Create and show mobile full-screen modal for infowindow content
10401function showMobileInfoModal(content) {
10402  // Remove any existing modal
10403  closeMobileInfoModal();
10404
10405  var modal = document.createElement('div');
10406  modal.className = 'environmental-mobile-modal';
10407  modal.innerHTML =
10408    '<div class="environmental-mobile-modal__overlay"></div>' +
10409    '<div class="environmental-mobile-modal__container">' +
10410      '<button class="environmental-mobile-modal__close" aria-label="Close">' +
10411        '<span aria-hidden="true">&times;</span>' +
10412      '</button>' +
10413      '<div class="environmental-mobile-modal__content">' + content + '</div>' +
10414    '</div>';
10415
10416  document.body.appendChild(modal);
10417  document.body.style.overflow = 'hidden';
10418
10419  // Add close handlers
10420  modal.querySelector('.environmental-mobile-modal__close').addEventListener('click', closeMobileInfoModal);
10421  modal.querySelector('.environmental-mobile-modal__overlay').addEventListener('click', closeMobileInfoModal);
10422
10423  // Re-attach video trigger handlers for the modal content
10424  attachVideoTriggerHandlers();
10425}
10426
10427// Close mobile info modal
10428function closeMobileInfoModal() {
10429  var existingModal = document.querySelector('.environmental-mobile-modal');
10430  if (existingModal) {
10431    existingModal.remove();
10432    document.body.style.overflow = '';
10433  }
10434}
10435
10436function unescapeToHtml(value) {
10437  if (!value || typeof value !== 'string') return '';
10438
10439  return value
10440    .replace(/\\\\/g, '\\')      // double backslashes -> single backslash
10441    .replace(/\\"/g, '"')        // escaped double quote -> actual "
10442    .replace(/\\'/g, "'")        // escaped single quote -> actual '
10443    .replace(/\\n/g, '<br>')     // escaped newline -> <br>
10444    .replace(/\\t/g, '&emsp;')   // escaped tab -> em space
10445}
10446
10447addEventListener('DOMContentLoaded', () => {
10448  $('.google-map-component').each(function (index, mapElement) {
10449    var longitude = $(mapElement).data('central-lng');
10450    var latitude = $(mapElement).data('central-lat');
10451    var zoom = $(mapElement).data('zoom');
10452    var type = $(mapElement).data('map-type');
10453    var hybridTilt = $(mapElement).data('hybrid-tilt');
10454    var satelliteTilt = $(mapElement).data('satellite-tilt');
10455    var formatting = $(mapElement).data('formatting');
10456    var legendPosition = $(mapElement).data('legend-position');
10457    var setTilt = 0;
10458    if (type == 'satellite' && satelliteTilt) {
10459      setTilt = 45;
10460    }
10461    if (type == 'hybrid' && hybridTilt) {
10462      setTilt = 45;
10463    }
10464
10465    // Check if environmental effort format is selected
10466    if (formatting === 'environmentalEffort') {
10467      // Request user's location for cluster maps
10468      if (navigator.geolocation) {
10469        navigator.geolocation.getCurrentPosition(
10470          function(position) {
10471            // Success: Use user's location
10472            var userLat = position.coords.latitude;
10473            var userLng = position.coords.longitude;
10474
10475            var map = new google.maps.Map(mapElement, {
10476              center: { lat: userLat, lng: userLng },
10477              zoom: parseInt(zoom),
10478              mapTypeId: type,
10479              tilt: setTilt,
10480              mapId: $(mapElement).attr('id')
10481            });
10482            mapList.push(map);
10483            mapById[$(mapElement).attr('id')] = map; // Store map by ID
10484
10485            initEnvironmentalEffortMap(index, mapElement, map, legendPosition);
10486          },
10487          function(error) {
10488            // Error or denied: Use configured location
10489
10490            var map = new google.maps.Map(mapElement, {
10491              center: { lat: parseFloat(latitude), lng: parseFloat(longitude) },
10492              zoom: parseInt(zoom),
10493              mapTypeId: type,
10494              tilt: setTilt,
10495              mapId: $(mapElement).attr('id')
10496            });
10497            mapList.push(map);
10498            mapById[$(mapElement).attr('id')] = map; // Store map by ID
10499
10500            initEnvironmentalEffortMap(index, mapElement, map, legendPosition);
10501          },
10502          {
10503            timeout: 5000,
10504            enableHighAccuracy: false
10505          }
10506        );
10507      } else {
10508        // Geolocation not supported: Use configured location
10509        console.warn('Geolocation not supported, using configured location');
10510
10511        var map = new google.maps.Map(mapElement, {
10512          center: { lat: parseFloat(latitude), lng: parseFloat(longitude) },
10513          zoom: parseInt(zoom),
10514          mapTypeId: type,
10515          tilt: setTilt,
10516          mapId: $(mapElement).attr('id')
10517        });
10518        mapList.push(map);
10519        mapById[$(mapElement).attr('id')] = map; // Store map by ID
10520
10521        initEnvironmentalEffortMap(index, mapElement, map, legendPosition);
10522      }
10523    } else {
10524      // Non-environmental effort maps: Use configured location as before
10525      var map = new google.maps.Map(mapElement, {
10526        center: { lat: parseFloat(latitude), lng: parseFloat(longitude) },
10527        zoom: parseInt(zoom),
10528        mapTypeId: type,
10529        tilt: setTilt,
10530        mapId: $(mapElement).attr('id')
10531      });
10532      mapList.push(map);
10533      mapById[$(mapElement).attr('id')] = map; // Store map by ID
10534
10535      createMarkers(index);
10536    }
10537  });
10538
10539});
10540
10541/**
10542 * Initialize environmental effort map with markers and legend
10543 */
10544function initEnvironmentalEffortMap(index, mapElement, map, legendPosition) {
10545  var endpoint = $(mapElement).data('environmental-effort-endpoint');
10546  var cfPath = $(mapElement).data('environmental-effort-path');
10547  var clusterOptions = {
10548    gridSize: parseInt($(mapElement).data('cluster-grid-size')) || 40,
10549    minPoints: parseInt($(mapElement).data('cluster-min-points')) || 2,
10550    maxZoom: parseInt($(mapElement).data('cluster-max-zoom')) || 14,
10551    color: $(mapElement).data('cluster-color') || '#0074D9'
10552  };
10553
10554  // Parse category icon overrides
10555  var categoryIconOverrides = {};
10556  try {
10557    var overridesJson = $(mapElement).data('category-icon-overrides');
10558
10559    var overridesArray;
10560    if (typeof overridesJson === 'string') {
10561      // If it's a string, parse it
10562      overridesArray = JSON.parse(overridesJson);
10563    } else if (Array.isArray(overridesJson)) {
10564      // If jQuery already parsed it, use it directly
10565      overridesArray = overridesJson;
10566    } else {
10567      console.warn('Unexpected overrides data type');
10568      overridesArray = [];
10569    }
10570
10571    overridesArray.forEach(function(override) {
10572      if (override.category && override.icon) {
10573        categoryIconOverrides[override.category.toLowerCase()] = {
10574          icon: override.icon,
10575          label: override.label || null // Store custom label if provided
10576        };
10577      }
10578    });
10579  } catch (e) {
10580    console.error('Failed to parse category icon overrides:', e, overridesJson);
10581  }
10582
10583  createEnvironmentalEffortMarkers(index, endpoint, cfPath, clusterOptions, categoryIconOverrides, mapElement, legendPosition);
10584}
10585
10586/**
10587 * Fetch environmental effort data from GraphQL endpoint and create clustered markers
10588 */
10589function createEnvironmentalEffortMarkers(index, endpoint, cfPath, clusterOptions, categoryIconOverrides, mapElement, legendPosition) {
10590  if (!endpoint) {
10591    console.warn('Environmental effort endpoint not configured');
10592    return;
10593  }
10594
10595  // Build the URL with path parameter if provided
10596  var requestUrl = endpoint;
10597  if (cfPath) {
10598    // Persisted-query variables are part of the path,
10599    requestUrl = endpoint + ';path=' + cfPath;
10600  }
10601
10602  console.log('Fetching environmental effort data from:', requestUrl);
10603
10604  fetch(requestUrl, {
10605    method: 'GET',
10606    headers: {
10607      'Content-Type': 'application/json'
10608    }
10609  })
10610    .then(function(response) {
10611      if (!response.ok) {
10612        throw new Error('Network response was not ok');
10613      }
10614      return response.json();
10615    })
10616    .then(function(responseData) {
10617      var items = responseData.data && responseData.data.environmentalEffortList && responseData.data.environmentalEffortList.items;
10618
10619      if (!items || items.length === 0) {
10620        console.warn('No environmental effort data found');
10621        return;
10622      }
10623
10624      var markers = [];
10625      var map = mapList[index];
10626      var mapId = $(mapElement).attr('id');
10627
10628      // Initialize category tracking for this map
10629      mapMarkersByCategory[mapId] = {};
10630      var categorySet = new Set();
10631      var markerItems = prepareEnvironmentalEffortMarkerItems(items);
10632
10633      markerItems.forEach(function(markerItem) {
10634        var item = markerItem.item;
10635        var lat = markerItem.position.lat;
10636        var lng = markerItem.position.lng;
10637
10638        // Build info window content
10639        var infowindowContent = buildEnvironmentalEffortInfoWindow(item, categoryIconOverrides);
10640
10641        var infowindow = new google.maps.InfoWindow({
10642          content: infowindowContent,
10643          maxWidth: 350
10644        });
10645
10646        var customIcon = getCustomIconForCategory(item.category, categoryIconOverrides);
10647
10648        var markerOptions = {
10649          position: { lat: lat, lng: lng },
10650          title: item.title || '',
10651          map: map
10652        };
10653
10654        // Set custom icon if available
10655        // Use standard Marker class to support icon property
10656        var marker = new google.maps.Marker(markerOptions);
10657
10658        // Render the category artwork into a white circular marker. Rendering
10659        // the image first avoids a nested-SVG image that Google Maps displays
10660        // as an empty white circle.
10661        if (customIcon) {
10662          Promise.all([
10663            createCircularMarkerIcon(customIcon),
10664            createCircularMarkerIcon(customIcon, null, null, true)
10665          ])
10666            .then(function(markerIcons) {
10667              var normalIcon = {
10668                url: markerIcons[0],
10669                scaledSize: new google.maps.Size(48, 48),
10670                anchor: new google.maps.Point(24, 24)
10671              };
10672              var hoverIcon = {
10673                url: markerIcons[1],
10674                // The larger canvas only provides room for the shadow; the
10675                // circular marker itself remains the same 48px diameter.
10676                scaledSize: new google.maps.Size(68, 68),
10677                anchor: new google.maps.Point(34, 34)
10678              };
10679
10680              marker.setIcon(normalIcon);
10681              marker.addListener('mouseover', function() {
10682                marker.setIcon(hoverIcon);
10683              });
10684              marker.addListener('mouseout', function() {
10685                marker.setIcon(normalIcon);
10686              });
10687              marker.addListener('focus', function() {
10688                marker.setIcon(hoverIcon);
10689              });
10690              marker.addListener('blur', function() {
10691                marker.setIcon(normalIcon);
10692              });
10693            })
10694            .catch(function() {
10695              // Keep the category image visible if an authored icon cannot be rendered.
10696              marker.setIcon({
10697                url: customIcon,
10698                scaledSize: new google.maps.Size(40, 40),
10699                anchor: new google.maps.Point(20, 20)
10700              });
10701            });
10702        }
10703
10704        // Add click listener to show info window
10705        marker.addListener('click', function() {
10706          if (isMobileDevice()) {
10707            // On mobile, show custom full-screen modal
10708            showMobileInfoModal(infowindowContent);
10709          } else {
10710            // On desktop, use native Google Maps InfoWindow
10711            if (openInfoWindows[mapId] && openInfoWindows[mapId] !== infowindow) {
10712              openInfoWindows[mapId].close();
10713            }
10714            infowindow.open({
10715              map: map,
10716              anchor: marker,
10717              shouldFocus: false
10718            });
10719            openInfoWindows[mapId] = infowindow;
10720          }
10721        });
10722
10723        infowindow.addListener('closeclick', function() {
10724          if (openInfoWindows[mapId] === infowindow) {
10725            delete openInfoWindows[mapId];
10726          }
10727        });
10728
10729        markers.push(marker);
10730
10731        // Track markers by category for filtering
10732        if (item.category && item.category.length > 0) {
10733          item.category.forEach(function(cat) {
10734            var categoryKey = cat.toLowerCase();
10735            var categoryLabel = cat.split(':').pop().replace(/-/g, ' ') || cat;
10736
10737            categorySet.add(JSON.stringify({ key: categoryKey, label: categoryLabel, original: cat }));
10738
10739            if (!mapMarkersByCategory[mapId][categoryKey]) {
10740              mapMarkersByCategory[mapId][categoryKey] = {
10741                markers: [],
10742                label: categoryLabel,
10743                icon: getCustomIconForCategory([cat], categoryIconOverrides)
10744              };
10745            }
10746            mapMarkersByCategory[mapId][categoryKey].markers.push(marker);
10747          });
10748        }
10749      });
10750
10751      if (markers.length > 0) {
10752        var clusterer = initMarkerClusterer(map, markers, clusterOptions);
10753        mapClusterers[mapId] = clusterer;
10754      }
10755
10756      // Populate the legend if not hidden
10757      if (legendPosition !== 'hidden') {
10758        populateMapLegend(mapId, categorySet, categoryIconOverrides);
10759      }
10760    })
10761    .catch(function(error) {
10762      console.error('Error fetching environmental effort data:', error);
10763    });
10764}
10765
10766/**
10767 * Keep same-coordinate Environmental Effort markers independently clickable.
10768 * The visual positions are offset only in the browser; CF coordinates remain
10769 * unchanged.
10770 */
10771function prepareEnvironmentalEffortMarkerItems(items) {
10772  var markerItems = [];
10773  var coordinateGroups = {};
10774
10775  items.forEach(function(item) {
10776    if (!item.latitude || !item.longitude) {
10777      console.warn('Skipping item without coordinates:', item.title);
10778      return;
10779    }
10780
10781    var lat = parseFloat(item.latitude);
10782    var lng = parseFloat(item.longitude);
10783    if (isNaN(lat) || isNaN(lng)) {
10784      console.warn('Invalid coordinates for item:', item.title);
10785      return;
10786    }
10787
10788    if (!isItemActive(item)) {
10789      return;
10790    }
10791
10792    var markerItem = {
10793      item: item,
10794      lat: lat,
10795      lng: lng,
10796      position: { lat: lat, lng: lng }
10797    };
10798    var coordinateKey = lat + ',' + lng;
10799    if (!coordinateGroups[coordinateKey]) {
10800      coordinateGroups[coordinateKey] = [];
10801    }
10802    coordinateGroups[coordinateKey].push(markerItem);
10803    markerItems.push(markerItem);
10804  });
10805
10806  Object.keys(coordinateGroups).forEach(function(coordinateKey) {
10807    var group = coordinateGroups[coordinateKey];
10808    if (group.length < 2) {
10809      return;
10810    }
10811
10812    // A CF path provides a stable order, so markers do not swap positions
10813    // between page loads when the GraphQL response order changes.
10814    group.sort(function(first, second) {
10815      var firstPath = first.item._path || first.item.path || first.item.title || '';
10816      var secondPath = second.item._path || second.item.path || second.item.title || '';
10817      return firstPath.localeCompare(secondPath);
10818    });
10819
10820    group.forEach(function(markerItem, groupIndex) {
10821      markerItem.position = getRadialOffsetPosition(
10822        markerItem.lat,
10823        markerItem.lng,
10824        groupIndex,
10825        group.length
10826      );
10827    });
10828  });
10829
10830  return markerItems;
10831}
10832
10833/**
10834 * Spread overlapping markers evenly around their shared coordinate by 15m.
10835 */
10836function getRadialOffsetPosition(latitude, longitude, index, count) {
10837  if (count < 2) {
10838    return { lat: latitude, lng: longitude };
10839  }
10840
10841  var offsetMetres = 15;
10842  var metresPerLatitudeDegree = 111320;
10843  var angle = -Math.PI / 2 + ((Math.PI * 2 * index) / count);
10844  var northMetres = Math.cos(angle) * offsetMetres;
10845  var eastMetres = Math.sin(angle) * offsetMetres;
10846  var longitudeScale = metresPerLatitudeDegree * Math.cos(latitude * Math.PI / 180);
10847
10848  return {
10849    lat: latitude + (northMetres / metresPerLatitudeDegree),
10850    lng: longitude + (eastMetres / longitudeScale)
10851  };
10852}
10853
10854/**
10855 * Build info window HTML content for environmental effort item
10856 */
10857function buildEnvironmentalEffortInfoWindow(item, categoryIconOverrides) {
10858  var content = '<div class="environmental-effort-infowindow">';
10859
10860  // Category tags
10861  if (item.category && item.category.length > 0) {
10862    content += '<div class="environmental-effort-infowindow__categories">';
10863    item.category.forEach(function(cat) {
10864      // Use custom label if available, otherwise use default label
10865      var categoryLabel = getCustomLabelForCategory([cat], categoryIconOverrides) || cat.split(':').pop().replace(/-/g, ' ') || cat;
10866      content += '<p class="environmental-effort-infowindow__category-tag">' + categoryLabel + '</p>';
10867    });
10868    content += '</div>';
10869  }
10870
10871  // Generate unique ID for this item
10872  var uniqueId = 'media-' + Math.random().toString(36).substr(2, 9);
10873
10874  if(item.embed) {
10875    // Embed content - make it clickable to open modal
10876    content += '<div class="environmental-effort-infowindow__embed" id="' + uniqueId + '-preview" style="position: relative; pointer-events: none;">';
10877    content += item.embed;
10878    // Add invisible overlay to capture clicks
10879    content += '<div class="embed-trigger" data-embed-id="' + uniqueId + '" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; cursor: pointer; pointer-events: auto; z-index: 10;"></div>';
10880    // Store embed content in a hidden element for modal use
10881    content += '<div id="' + uniqueId + '-embed-content" style="display: none;">' + item.embed + '</div>';
10882    content += '</div>';
10883  }
10884  else if (item.video && item.video._path) {
10885    // Video exists - make it clickable to open modal
10886    var posterAttr = (item.image && item.image._path) ? ' poster="' + item.image._path + '"' : '';
10887
10888    content += '<div class="environmental-effort-infowindow__video video-trigger" data-video-id="' + uniqueId + '" style="cursor: pointer; position: relative;">';
10889    content += '<video id="' + uniqueId + '"' + posterAttr + ' style="display: block; width: 100%;"><source src="' + item.video._path + '" type="video/mp4">Your browser does not support the video tag.</video>';
10890    content += '<div class="video-play-button" id="play-btn-' + uniqueId + '" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); pointer-events: none;">';
10891    content += '<svg xmlns="http://www.w3.org/2000/svg" width="60" height="40" viewBox="0 0 60 40" fill="none">';
10892    content += '<rect width="60" height="40" rx="8" fill="#262C32" fill-opacity="0.9"/>';
10893    content += '<path d="M38.5 20L25.75 27.7942L25.75 12.2058L38.5 20Z" fill="white"/>';
10894    content += '</svg>';
10895    content += '</div>';
10896    content += '</div>';
10897  } else if (item.image && item.image._path) {
10898    // Image only (no video) - just show the image without play button
10899    content += '<div class="environmental-effort-infowindow__image"><img src="' + item.image._path + '" alt="' + (item.title || '') + '" /></div>';
10900  }
10901
10902  // Title
10903  if (item.title) {
10904    content += '<h5 class="environmental-effort-infowindow__title">' + item.title + '</h5>';
10905  }
10906
10907  // Full Description (HTML content)
10908  if (item.description && item.description.html) {
10909    content += '<div class="environmental-effort-infowindow__description">' + item.description.html + '</div>';
10910  }
10911
10912  // CTA Link
10913  if (item.ctaLink && item.ctaLabel) {
10914    content += '<a href="' + item.ctaLink + '" target="_blank" class="btn-arrow btn-quinary environmental-effort-infowindow__cta">' +
10915      '<span>' + item.ctaLabel + '</span>' +
10916    '</a>';
10917  }
10918
10919  content += '</div>';
10920  return content;
10921}
10922
10923/**
10924 * Initialize Google Maps MarkerClusterer
10925 */
10926function initMarkerClusterer(map, markers, clusterOptions) {
10927  var clusterer = null;
10928
10929  // Check if markerclusterer library is available
10930  if (typeof markerClusterer !== 'undefined' && markerClusterer.MarkerClusterer) {
10931    clusterer = new markerClusterer.MarkerClusterer({
10932      map: map,
10933      markers: markers,
10934      renderer: {
10935        render: function({ count, position }) {
10936          return new google.maps.marker.AdvancedMarkerElement({
10937            position: position,
10938            content: createClusterIcon(count, clusterOptions.color),
10939            zIndex: Number(google.maps.Marker.MAX_ZINDEX) + count
10940          });
10941        }
10942      }
10943    });
10944  } else {
10945    // Fallback: Load MarkerClusterer library dynamically
10946    var script = document.createElement('script');
10947    script.src = 'https://unpkg.com/@googlemaps/markerclusterer/dist/index.min.js';
10948    script.onload = function() {
10949      clusterer = new markerClusterer.MarkerClusterer({
10950        map: map,
10951        markers: markers,
10952        renderer: {
10953          render: function({ count, position }) {
10954            return new google.maps.marker.AdvancedMarkerElement({
10955              position: position,
10956              content: createClusterIcon(count, clusterOptions.color),
10957              zIndex: Number(google.maps.Marker.MAX_ZINDEX) + count
10958            });
10959          }
10960        }
10961      });
10962    };
10963    document.head.appendChild(script);
10964  }
10965
10966  return clusterer;
10967}
10968
10969/**
10970 * Create custom cluster icon element
10971 */
10972function createClusterIcon(count, color) {
10973  var div = document.createElement('div');
10974  div.className = 'cluster-marker';
10975  // Apply custom color if provided
10976  if (color && color !== '#0074D9') {
10977    div.style.background = color;
10978  }
10979  div.textContent = count;
10980  return div;
10981}
10982
10983/**
10984 * Populate the map legend with category checkboxes
10985 */
10986function populateMapLegend(mapId, categorySet, categoryIconOverrides) {
10987  var legendContainer = document.getElementById(mapId + '-legend');
10988  if (!legendContainer) {
10989    console.warn('Legend container not found for map:', mapId);
10990    return;
10991  }
10992
10993  var categoriesContainer = legendContainer.querySelector('.google-map-legend__categories');
10994  if (!categoriesContainer) {
10995    console.warn('Categories container not found in legend');
10996    return;
10997  }
10998
10999  // Clear existing content
11000  categoriesContainer.innerHTML = '';
11001
11002  // Convert Set to Array and sort alphabetically by label
11003  var categories = Array.from(categorySet).map(function(item) {
11004    return JSON.parse(item);
11005  }).sort(function(a, b) {
11006    return a.label.localeCompare(b.label);
11007  });
11008
11009  categories.forEach(function(category) {
11010    var categoryItem = document.createElement('div');
11011    categoryItem.className = 'google-map-legend__category-item';
11012
11013    // Add icon first (if available)
11014    var iconUrl = getCustomIconForCategory([category.original], categoryIconOverrides);
11015    if (iconUrl) {
11016      var iconImg = document.createElement('img');
11017      iconImg.src = iconUrl;
11018      iconImg.alt = category.label;
11019      iconImg.className = 'google-map-legend__icon';
11020      categoryItem.appendChild(iconImg);
11021    }
11022
11023    // Add label text second - use custom label if available
11024    var label = document.createElement('label');
11025    label.htmlFor = mapId + '-category-' + category.key.replace(/[^a-z0-9]/g, '-');
11026    label.className = 'google-map-legend__label';
11027
11028    var labelText = document.createElement('span');
11029    labelText.className = 'google-map-legend__label-text';
11030    // Check for custom label override
11031    var displayLabel = getCustomLabelForCategory([category.original], categoryIconOverrides) || category.label;
11032    labelText.textContent = displayLabel;
11033    label.appendChild(labelText);
11034
11035    categoryItem.appendChild(label);
11036
11037    // Add checkbox last
11038    var checkbox = document.createElement('input');
11039    checkbox.type = 'checkbox';
11040    checkbox.checked = true;
11041    checkbox.id = mapId + '-category-' + category.key.replace(/[^a-z0-9]/g, '-');
11042    checkbox.className = 'google-map-legend__checkbox';
11043    checkbox.dataset.mapId = mapId;
11044    checkbox.dataset.categoryKey = category.key;
11045
11046    categoryItem.appendChild(checkbox);
11047    categoriesContainer.appendChild(categoryItem);
11048
11049    // Add event listener for checkbox
11050    checkbox.addEventListener('change', function() {
11051      toggleCategoryMarkers(this.dataset.mapId, this.dataset.categoryKey, this.checked);
11052    });
11053  });
11054}
11055
11056/**
11057 * Toggle visibility of markers for a specific category
11058 */
11059function toggleCategoryMarkers(mapId, categoryKey, visible) {
11060  var categoryData = mapMarkersByCategory[mapId] && mapMarkersByCategory[mapId][categoryKey];
11061  if (!categoryData) {
11062    console.warn('No markers found for category:', categoryKey);
11063    return;
11064  }
11065
11066  updateClusterer(mapId);
11067}
11068
11069/**
11070 * Get map index by ID
11071 */
11072function getMapIndexById(mapId) {
11073  var mapElements = document.querySelectorAll('.google-map-component');
11074  for (var i = 0; i < mapElements.length; i++) {
11075    if (mapElements[i].id === mapId) {
11076      return i;
11077    }
11078  }
11079  return 0;
11080}
11081
11082/**
11083 * Update clusterer with visible markers
11084 */
11085function updateClusterer(mapId) {
11086  var clusterer = mapClusterers[mapId];
11087  if (!clusterer) return;
11088
11089  var visibleMarkers = [];
11090  var categoriesData = mapMarkersByCategory[mapId];
11091  var processedMarkers = new Set();
11092  var map = mapById[mapId]; // Use mapById for reliable lookup
11093
11094  // Get the legend container for this specific map to scope checkbox lookups
11095  var legendContainer = document.getElementById(mapId + '-legend');
11096
11097  if (categoriesData) {
11098    Object.keys(categoriesData).forEach(function(categoryKey) {
11099      // Look up checkbox within the specific map's legend container to avoid cross-map conflicts
11100      var checkboxId = mapId + '-category-' + categoryKey.replace(/[^a-z0-9]/g, '-');
11101      var checkbox = legendContainer
11102        ? legendContainer.querySelector('#' + CSS.escape(checkboxId))
11103        : document.getElementById(checkboxId);
11104
11105      if (checkbox && checkbox.checked) {
11106        categoriesData[categoryKey].markers.forEach(function(marker) {
11107          if (!processedMarkers.has(marker)) {
11108            processedMarkers.add(marker);
11109            visibleMarkers.push(marker);
11110          }
11111        });
11112      }
11113    });
11114  }
11115
11116  // Clear all markers from clusterer and map
11117  clusterer.clearMarkers();
11118
11119  // Hide all markers first
11120  if (categoriesData) {
11121    Object.keys(categoriesData).forEach(function(categoryKey) {
11122      categoriesData[categoryKey].markers.forEach(function(marker) {
11123        marker.setMap(null);
11124      });
11125    });
11126  }
11127
11128  // Show and add only visible markers to the clusterer
11129  visibleMarkers.forEach(function(marker) {
11130    marker.setMap(map);
11131  });
11132
11133  clusterer.addMarkers(visibleMarkers);
11134}
11135
11136function createMarkers(index) {
11137  var data = document.querySelectorAll('.google-map-component')[index].getAttribute('data-json');
11138
11139
11140  if (data == undefined || data == null) {
11141    return;
11142  }
11143
11144
11145  try {
11146    data = JSON.parse(data);
11147    console.log(data)
11148  } catch (e) {
11149    console.warn('Map points could not be parsed:', e);
11150    return;
11151  }
11152
11153  data.forEach(function (point, i) {
11154    console.log(point);
11155
11156    var pointTitle = point.title != 'null' ? unescapeToHtml(point.title) : '';
11157    var pointIcon = point.customIcon != 'null' ? unescapeToHtml(point.customIcon) : '';
11158    var pointContent = point.content != 'null' ? unescapeToHtml(point.content) : '';
11159    console.log(pointTitle)
11160
11161    var infowindowContent = "";
11162    infowindowContent = pointTitle ? infowindowContent + "<h5 class='marker--title mb-3'>" + pointTitle + '</h5>' : infowindowContent;
11163    infowindowContent = pointContent ? infowindowContent + "<p class='marker-content'>" + pointContent + '</p>' : infowindowContent;
11164
11165    var infowindow = new google.maps.InfoWindow({
11166      content: infowindowContent,
11167    });
11168
11169    var markerElement = null;
11170    
11171    // Create the element for the AdvancedMarkerElement
11172    if (pointIcon) {
11173      markerElement = document.createElement('div');
11174      markerElement.classList.add('custom-marker');
11175      markerElement.innerHTML = "<img src='" + pointIcon + "' alt='" + pointTitle + "' />";
11176    }
11177
11178    var marker = new google.maps.marker.AdvancedMarkerElement({
11179      position: {
11180        lat: parseFloat(point.latitude),
11181        lng: parseFloat(point.longitude),
11182      },
11183      title: pointTitle,
11184      content: markerElement, // Set the custom marker content
11185      map: mapList[index], // Add to the map
11186    });
11187
11188    // Add a click listener for each marker to open the info window
11189    marker.addListener('click', function () {
11190      infowindow.open({
11191        map: mapList[index],
11192        anchor: marker,
11193        shouldFocus: false,
11194      });
11195    });
11196  });
11197}
11198
11199/**
11200 * Play environmental effort video - hides play button and shows controls
11201 * On desktop, shows video in a popup modal with grey opaque background
11202 */
11203function playEnvironmentalVideo(videoId) {
11204  var video = document.getElementById(videoId);
11205  var playBtn = document.getElementById('play-btn-' + videoId);
11206
11207  if (!video) return;
11208
11209  // Check if desktop (screen width > 768px)
11210  var isDesktop = window.innerWidth > 768;
11211
11212  if (isDesktop) {
11213    // Create modal overlay for desktop
11214    var modal = document.createElement('div');
11215    modal.id = 'video-modal-' + videoId;
11216    modal.className = 'environmental-video-modal';
11217
11218    // Create modal content container
11219    var modalContent = document.createElement('div');
11220    modalContent.className = 'environmental-video-modal__content';
11221
11222    // Create close button
11223    var closeBtn = document.createElement('button');
11224    closeBtn.className = 'environmental-video-modal__close';
11225    closeBtn.innerHTML = '&times;';
11226    closeBtn.onclick = function() {
11227      closeVideoModal(videoId);
11228    };
11229
11230    // Clone the video for the modal
11231    var modalVideo = document.createElement('video');
11232    modalVideo.id = 'modal-video-' + videoId;
11233    modalVideo.className = 'environmental-video-modal__media-content';
11234    modalVideo.src = video.querySelector('source').src;
11235    modalVideo.controls = true;
11236
11237    // Assemble modal
11238    modalContent.appendChild(closeBtn);
11239    modalContent.appendChild(modalVideo);
11240    modal.appendChild(modalContent);
11241
11242    // Close modal when clicking on background
11243    modal.onclick = function(e) {
11244      if (e.target === modal) {
11245        closeVideoModal(videoId);
11246      }
11247    };
11248
11249    // Close modal on escape key
11250    document.addEventListener('keydown', function escHandler(e) {
11251      if (e.key === 'Escape') {
11252        closeVideoModal(videoId);
11253        document.removeEventListener('keydown', escHandler);
11254      }
11255    });
11256
11257    // Add modal to body
11258    document.body.appendChild(modal);
11259
11260    // Pause original video if playing
11261    video.pause();
11262
11263  } else {
11264    // Mobile behavior - play inline
11265    video.setAttribute('controls', 'controls');
11266    video.play();
11267
11268    // Hide the play button
11269    if (playBtn) {
11270      playBtn.style.display = 'none';
11271    }
11272
11273    // Listen for video pause/end to show play button again
11274    video.addEventListener('pause', function() {
11275      if (playBtn) {
11276        playBtn.style.display = 'block';
11277      }
11278    });
11279
11280    video.addEventListener('ended', function() {
11281      if (playBtn) {
11282        playBtn.style.display = 'block';
11283      }
11284      // Hide controls when video ends
11285      video.removeAttribute('controls');
11286    });
11287
11288    // Hide play button while playing
11289    video.addEventListener('play', function() {
11290      if (playBtn) {
11291        playBtn.style.display = 'none';
11292      }
11293    });
11294  }
11295}
11296
11297/**
11298 * Close the video modal popup
11299 */
11300function closeVideoModal(videoId) {
11301  var modal = document.getElementById('video-modal-' + videoId);
11302  if (modal) {
11303    var modalVideo = document.getElementById('modal-video-' + videoId);
11304    if (modalVideo) {
11305      modalVideo.pause();
11306    }
11307    modal.remove();
11308  }
11309}
11310
11311/**
11312 * Open modal for embed content
11313 */
11314function openEmbedModal(uniqueId, triggerElement) {
11315  // Get the embed content from the hidden element
11316  var embedContentElement = document.getElementById(uniqueId + '-embed-content');
11317  if (!embedContentElement) {
11318    console.warn('Embed content not found for:', uniqueId);
11319    return;
11320  }
11321
11322  // Check if desktop (screen width > 768px)
11323  var isDesktop = window.innerWidth > 768;
11324
11325  if (isDesktop) {
11326    // Create modal overlay for desktop
11327    var modal = document.createElement('div');
11328    modal.id = 'embed-modal-' + uniqueId;
11329    modal.className = 'environmental-video-modal';
11330
11331    // Create modal content container
11332    var modalContent = document.createElement('div');
11333    modalContent.className = 'environmental-video-modal__content';
11334
11335    // Create close button
11336    var closeBtn = document.createElement('button');
11337    closeBtn.className = 'environmental-video-modal__close';
11338    closeBtn.innerHTML = '&times;';
11339    closeBtn.onclick = function() {
11340      closeEmbedModal(uniqueId);
11341    };
11342
11343    // Create wrapper for embed content
11344    var embedWrapper = document.createElement('div');
11345    embedWrapper.className = 'environmental-video-modal__embed';
11346    embedWrapper.innerHTML = embedContentElement.innerHTML;
11347
11348    // Apply shared media-content class to iframes for consistent styling
11349    var iframes = embedWrapper.querySelectorAll('iframe');
11350    iframes.forEach(function(iframe) {
11351      iframe.classList.add('environmental-video-modal__media-content');
11352    });
11353
11354    // Assemble modal
11355    modalContent.appendChild(closeBtn);
11356    modalContent.appendChild(embedWrapper);
11357    modal.appendChild(modalContent);
11358
11359    // Close modal when clicking on background
11360    modal.onclick = function(e) {
11361      if (e.target === modal) {
11362        closeEmbedModal(uniqueId);
11363      }
11364    };
11365
11366    // Close modal on escape key
11367    document.addEventListener('keydown', function escHandler(e) {
11368      if (e.key === 'Escape') {
11369        closeEmbedModal(uniqueId);
11370        document.removeEventListener('keydown', escHandler);
11371      }
11372    });
11373
11374    // Add modal to body
11375    document.body.appendChild(modal);
11376  } else {
11377    // Mobile behavior - play inline by enabling pointer events on the embed
11378    var previewElement = document.getElementById(uniqueId + '-preview');
11379    if (previewElement) {
11380      // Remove pointer-events: none to allow interaction with the embed
11381      previewElement.style.pointerEvents = 'auto';
11382
11383      // Hide the overlay trigger
11384      var overlayTrigger = previewElement.querySelector('.embed-trigger');
11385      if (overlayTrigger) {
11386        overlayTrigger.style.display = 'none';
11387      }
11388    }
11389  }
11390}
11391
11392/**
11393 * Close the embed modal popup
11394 */
11395function closeEmbedModal(uniqueId) {
11396  var modal = document.getElementById('embed-modal-' + uniqueId);
11397  if (modal) {
11398    modal.remove();
11399  }
11400}
11401
11402/**
11403 * Check if an environmental effort item is active based on date range
11404 */
11405function isItemActive(item) {
11406  if (!item.startDate && !item.endDate) {
11407    return true; // No date range specified, item is active
11408  }
11409
11410  var now = new Date();
11411  var start = item.startDate ? new Date(item.startDate) : null;
11412  var end = item.endDate ? new Date(item.endDate) : null;
11413
11414  // Check if current date is within the start and end date range
11415  return (!start || now >= start) && (!end || now <= end);
11416}
11417
11418/**
11419 * Get custom icon URL for a specific category
11420 */
11421function getCustomIconForCategory(categories, overrides) {
11422  if (!categories || !overrides || Object.keys(overrides).length === 0) {
11423    return null;
11424  }
11425
11426  // Categories can be an array, so check each one
11427  var categoryArray = Array.isArray(categories) ? categories : [categories];
11428
11429
11430  for (const element of categoryArray) {
11431    var cat = element;
11432    if (!cat) continue;
11433
11434    // Try exact match first (case-insensitive)
11435    var lowerCat = cat.toLowerCase();
11436
11437    if (overrides[lowerCat]) {
11438      return overrides[lowerCat].icon;
11439    }
11440
11441    // If not found, try matching just the suffix after colon (e.g., "business_faq" from "APC:business_faq")
11442    if (cat.indexOf(':') > -1) {
11443      var suffix = cat.split(':').pop().toLowerCase();
11444
11445      if (overrides[suffix]) {
11446        return overrides[suffix].icon;
11447      }
11448    }
11449  }
11450
11451  return null;
11452}
11453
11454/**
11455 * Render a category icon inside a white circular marker background.
11456 */
11457function createCircularMarkerIcon(iconUrl, borderColor, borderWidth, hasShadow) {
11458  return new Promise(function(resolve, reject) {
11459    var image = new Image();
11460
11461    image.onload = function() {
11462      var canvas = document.createElement('canvas');
11463      var size = hasShadow ? 68 : 48;
11464      var center = size / 2;
11465      canvas.width = size;
11466      canvas.height = size;
11467
11468      var context = canvas.getContext('2d');
11469      if (hasShadow) {
11470        // Paint the shadow first, then repaint the badge without a shadow.
11471        // That keeps the white marker face completely white.
11472        context.save();
11473        context.shadowColor = 'rgba(0, 0, 0, 0.32)';
11474        context.shadowBlur = 8;
11475        context.shadowOffsetY = 3;
11476        context.beginPath();
11477        context.arc(center, center, 23, 0, Math.PI * 2);
11478        context.fillStyle = '#ffffff';
11479        context.fill();
11480        context.restore();
11481      }
11482      context.beginPath();
11483      context.arc(center, center, 23, 0, Math.PI * 2);
11484      context.fillStyle = '#ffffff';
11485      context.fill();
11486      context.lineWidth = borderWidth || 1.5;
11487      context.strokeStyle = borderColor || '#d1d5db';
11488      context.stroke();
11489
11490      context.drawImage(image, center - 14, center - 14, 28, 28);
11491      resolve(canvas.toDataURL('image/png'));
11492    };
11493
11494    image.onerror = reject;
11495    image.src = iconUrl;
11496  });
11497}
11498
11499/**
11500 * Get custom label for a specific category
11501 */
11502function getCustomLabelForCategory(categories, overrides) {
11503  if (!categories || !overrides || Object.keys(overrides).length === 0) {
11504    return null;
11505  }
11506
11507  // Categories can be an array, so check each one
11508  var categoryArray = Array.isArray(categories) ? categories : [categories];
11509
11510  for (const element of categoryArray) {
11511    var cat = element;
11512    if (!cat) continue;
11513
11514    // Try exact match first (case-insensitive)
11515    var lowerCat = cat.toLowerCase();
11516
11517    if (overrides[lowerCat] && overrides[lowerCat].label) {
11518      return overrides[lowerCat].label;
11519    }
11520
11521    // If not found, try matching just the suffix after colon (e.g., "business_faq" from "APC:business_faq")
11522    if (cat.indexOf(':') > -1) {
11523      var suffix = cat.split(':').pop().toLowerCase();
11524
11525      if (overrides[suffix] && overrides[suffix].label) {
11526        return overrides[suffix].label;
11527      }
11528    }
11529  }
11530
11531  return null;
11532}
11533
11534// Event delegation for video and embed triggers
11535document.addEventListener('click', function(e) {
11536  // Handle video triggers
11537  if (e.target.closest('.video-trigger')) {
11538    var videoTrigger = e.target.closest('.video-trigger');
11539    var videoId = videoTrigger.dataset.videoId;
11540    if (videoId) {
11541      playEnvironmentalVideo(videoId);
11542    }
11543  }
11544
11545  // Handle embed triggers
11546  if (e.target.closest('.embed-trigger')) {
11547    var embedTrigger = e.target.closest('.embed-trigger');
11548    var embedId = embedTrigger.dataset.embedId;
11549    if (embedId) {
11550      openEmbedModal(embedId);
11551    }
11552  }
11553
11554  // Handle modal close buttons
11555  if (e.target.closest('.environmental-video-modal__close')) {
11556    var modal = e.target.closest('.environmental-video-modal');
11557    if (modal) {
11558      if (modal.id.startsWith('video-modal-')) {
11559        var videoId = modal.id.replace('video-modal-', '');
11560        closeVideoModal(videoId);
11561      } else if (modal.id.startsWith('embed-modal-')) {
11562        var embedId = modal.id.replace('embed-modal-', '');
11563        closeEmbedModal(embedId);
11564      }
11565    }
11566  }
11567
11568  // Handle modal background clicks
11569  if (e.target.classList.contains('environmental-video-modal')) {
11570    var modal = e.target;
11571    if (modal.id.startsWith('video-modal-')) {
11572      var videoId = modal.id.replace('video-modal-', '');
11573      closeVideoModal(videoId);
11574    } else if (modal.id.startsWith('embed-modal-')) {
11575      var embedId = modal.id.replace('embed-modal-', '');
11576      closeEmbedModal(embedId);
11577    }
11578  }
11579});
11580
11581// Event listener for Escape key to close modals
11582document.addEventListener('keydown', function(e) {
11583  if (e.key === 'Escape') {
11584    var videoModals = document.querySelectorAll('[id^="video-modal-"]');
11585    videoModals.forEach(function(modal) {
11586      var videoId = modal.id.replace('video-modal-', '');
11587      closeVideoModal(videoId);
11588    });
11589
11590    var embedModals = document.querySelectorAll('[id^="embed-modal-"]');
11591    embedModals.forEach(function(modal) {
11592      var embedId = modal.id.replace('embed-modal-', '');
11593      closeEmbedModal(embedId);
11594    });
11595  }
11596});
11597
11598// Re-attach video trigger handlers for dynamically created content
11599function attachVideoTriggerHandlers() {
11600  var videoTriggers = document.querySelectorAll('.video-trigger');
11601  videoTriggers.forEach(function(trigger) {
11602    var videoId = trigger.dataset.videoId;
11603    if (videoId) {
11604      trigger.addEventListener('click', function() {
11605        playEnvironmentalVideo(videoId);
11606      });
11607    }
11608  });
11609}
11610
11611// Re-attach embed trigger handlers for dynamically created content
11612function attachEmbedTriggerHandlers() {
11613  var embedTriggers = document.querySelectorAll('.embed-trigger');
11614  embedTriggers.forEach(function(trigger) {
11615    var embedId = trigger.dataset.embedId;
11616    if (embedId) {
11617      trigger.addEventListener('click', function() {
11618        openEmbedModal(embedId);
11619      });
11620    }
11621  });
11622}
11623
11624let isModalOpen = false;
11625let modalOpenedFirst = null;
11626let disableBackgroundClick = false;
11627let savedScrollY = 0;
11628
11629$(document).ready(() => {
11630  const modals = $('.generalmodal');
11631
11632  initializeModalSystem();
11633
11634  // Last line of defence: anything that moves the window while a modal is open
11635  // gets snapped back. Modal content scrolls inside .modal-container, so the
11636  // window position is never meant to change here.
11637  $(window).on('scroll.generalmodal', () => {
11638    if (!isModalOpen) return;
11639    if (window.scrollY === savedScrollY) return;
11640    stopScrollAnimations();
11641    window.scrollTo(0, savedScrollY);
11642  });
11643
11644  function initializeModalSystem() {
11645    checkInitialHash();
11646    setupHashChangeListener();
11647    setupModalExitButtons();
11648    setupBackgroundClick();
11649    setupModalLinkSwitching();
11650    trackMouseOverModalContainer();
11651  }
11652
11653  function checkInitialHash() {
11654    const hash = window.location.hash;
11655    if (hash && hash !== '#') {
11656      const targetModal = findModalByHash(hash);
11657      if (targetModal) openModal(targetModal);
11658    }
11659  }
11660
11661  function setupHashChangeListener() {
11662    $(window).on('hashchange', () => {
11663      handleModalSwitch(window.location.hash);
11664    });
11665  }
11666
11667  function setupModalExitButtons() {
11668    $(document).on('click', '.modal-exit-button.icon-x', function (e) {
11669      e.preventDefault();
11670      const backgroundEl = $(this).closest('.background');
11671      closeModal(backgroundEl);
11672    });
11673  }
11674
11675  function setupBackgroundClick() {
11676    $(document).on('click', '.background', function () {
11677      if (disableBackgroundClick) return;
11678      if ($(this).hasClass('restricted')) return;
11679      closeModal($(this));
11680    });
11681  }
11682
11683  function trackMouseOverModalContainer() {
11684    $(document).on('mouseenter', '.modal-container', () => {
11685      disableBackgroundClick = true;
11686    });
11687    $(document).on('mouseleave', '.modal-container', () => {
11688      disableBackgroundClick = false;
11689    });
11690  }
11691
11692  function setupModalLinkSwitching() {
11693    $(document).on('click', '.modal-container a[href^="#"]', function (e) {
11694      const href = $(this).attr('href');
11695      if (!href || href.length <= 1) return;
11696
11697      e.preventDefault();
11698
11699      // pushState rather than assigning location.hash: assigning the hash is a
11700      // real fragment navigation, and .modal-container carries id="anchorId",
11701      // so the browser would scroll that target into view and snap the page.
11702      if (window.location.hash !== href) {
11703        history.pushState(null, null, href);
11704      }
11705      handleModalSwitch(href);
11706    });
11707  }
11708
11709  function handleModalSwitch(newHash) {
11710  if (!newHash || newHash === '#') {
11711    if (isModalOpen) {
11712      closeModal(modalOpenedFirst);
11713    }
11714    return;
11715  }
11716
11717  const targetModal = findModalByHash(newHash);
11718  if (!targetModal) {
11719    if (isModalOpen) {
11720      closeModal(modalOpenedFirst, { preserveHash: true });
11721    }
11722    return;
11723  }
11724
11725  const switchingModals = isModalOpen && modalOpenedFirst !== targetModal;
11726
11727  // Callers own the URL: hashchange arrives with it already updated, and click
11728  // handlers pushState before calling. preserveScrollLock keeps the page pinned
11729  // and the hash intact across the close/open pair.
11730  if (switchingModals) {
11731    closeModal(modalOpenedFirst, { preserveScrollLock: true });
11732  }
11733
11734  if (!isModalOpen || switchingModals) {
11735    openModal(targetModal);
11736  }
11737}
11738  function findModalByHash(hash) {
11739    for (const modal of modals) {
11740      const background = $(modal).find('.background')[0];
11741      if ('#' + $(background).attr('anchorId') === hash) {
11742        return background;
11743      }
11744    }
11745    return null;
11746  }
11747
11748  // The site-wide scroll script in global.min.js binds its own click handler to
11749  // every in-page anchor and queues $('html,body').animate({scrollTop: ...})
11750  // before our delegated handlers run, so preventDefault cannot cancel it.
11751  function stopScrollAnimations() {
11752    $('html, body').stop(true, false);
11753  }
11754
11755  function lockPageScroll() {
11756    // Guard so switching modals (which re-locks without unlocking) can't
11757    // overwrite the position captured when the first modal opened.
11758    if ($('html').hasClass('generalmodal-scroll-lock')) return;
11759    savedScrollY = window.scrollY;
11760    $('html').addClass('generalmodal-scroll-lock');
11761    stopScrollAnimations();
11762    // Both handlers run inside the same click, so a single stop() can lose the
11763    // race against the animate() call that follows it.
11764    requestAnimationFrame(() => {
11765      if (!$('html').hasClass('generalmodal-scroll-lock')) return;
11766      stopScrollAnimations();
11767      window.scrollTo(0, savedScrollY);
11768    });
11769  }
11770
11771  function unlockPageScroll() {
11772    stopScrollAnimations();
11773    $('html').removeClass('generalmodal-scroll-lock');
11774    window.scrollTo(0, savedScrollY);
11775  }
11776
11777  function openModal(target) {
11778    $(target).css('display', 'flex');
11779    lockPageScroll();
11780    isModalOpen = true;
11781    modalOpenedFirst = target;
11782  }
11783
11784  function closeModal(backgroundEl, { preserveScrollLock = false, preserveHash = false } = {}) {
11785    resetEmbeddedVideos($(backgroundEl));
11786    $(backgroundEl).css('display', 'none');
11787  
11788    isModalOpen = false;
11789    modalOpenedFirst = null;
11790  
11791    if (!preserveScrollLock) {
11792      unlockPageScroll();
11793    }
11794  
11795    if (!preserveHash && !preserveScrollLock && window.location.hash !== '') {
11796      const baseUri = location.origin + location.pathname + location.search;
11797      history.replaceState(null, null, baseUri);
11798    }
11799  }
11800  
11801
11802  function resetEmbeddedVideos(modal) {
11803    try {
11804      modal.find('iframe').each(function () {
11805        $(this).attr('src', $(this).attr('src'));
11806      });
11807    } catch (e) {
11808      // Silent fail
11809    }
11810  }
11811
11812  // Expose API for anchor.js coordination
11813  window.generalModalAPI = {
11814    openModal: function(modalId) {
11815      var target = findModalByHash('#' + modalId);
11816      if (target) openModal(target);
11817      return target;
11818    },
11819    closeModal: function(options) {
11820      if (modalOpenedFirst) closeModal(modalOpenedFirst, options);
11821    },
11822    isOpen: function() { return isModalOpen; },
11823    getCurrentModal: function() { return modalOpenedFirst; },
11824    findModalByHash: findModalByHash,
11825    switchTo: handleModalSwitch
11826  };
11827});
11828
11829$(function(){
11830
11831    var categoryOption = '';
11832    $('.gallerygrid__item img.modal-trigger').on('click touch', function () {
11833        var selectedIndex;
11834        var col = $(this).closest('.gallerygrid__col');
11835        if (categoryOption != '') {
11836            selectedIndex = col.index('div.gallerygrid__col.' + categoryOption);
11837        } else {
11838            selectedIndex = col.index('div.gallerygrid__col');
11839        }
11840
11841        var modalIndex = 0;
11842        var gallerygridComponent = $(this).closest('.gallerygrid');
11843        $('.gallerygrid').each(function (i) {
11844            if ($(this).is(gallerygridComponent)) {
11845                modalIndex = i;
11846            }
11847        });
11848        carousels[modalIndex].data('flexslider').flexAnimate(selectedIndex);
11849
11850        // show modal
11851        $(this).closest('.gallerygrid').find('.gallerygrid__modal').addClass('active');
11852    });
11853
11854    var originalCarouselSlides = [],
11855        originalThumbnailSlides = [];
11856    $('.gallerygrid__carousel').each(function (i) {
11857        var slides = {};
11858        $(this).attr('id', 'flexslider__carousel-' + (i + 1));
11859        slides.id = $(this).attr('id');
11860        slides.markup = $(this).find('.slides').html();
11861        originalCarouselSlides.push(slides);
11862    });
11863    $('.gallerygrid__thumbnails').each(function (i) {
11864        var slides = {};
11865        $(this).attr('id', 'flexslider__thumbnails-' + (i + 1));
11866        slides.id = $(this).attr('id');
11867        slides.markup = $(this).find('.slides').html();
11868        originalThumbnailSlides.push(slides);
11869    });
11870
11871    $('.gallerygrid__select select option').each(function () {
11872        var option = $(this).attr('data-option');
11873        option = option.replace(/ /g, '-');
11874        $(this).attr('data-option', option);
11875    });
11876
11877    $('.gallerygrid__select select').bind({
11878        "create": function (ev, obj) {
11879            var $el = $(this),
11880                val = obj.dropdownOption.data('val'),
11881                option = obj.dropdownOption.data('option');
11882            setCategoryLabel($el, val);
11883            filterGalleryResults($el, option);
11884        },
11885        "changed": function (ev, obj) {
11886            var $el = $(this),
11887                val = obj.dropdownOption.data('val'),
11888                option = obj.dropdownOption.data('option');
11889            setCategoryLabel($el, val);
11890            filterGalleryResults($el, option);
11891        }
11892    });
11893    var categorySelect = $('.gallerygrid__select select').selectBoxIt();
11894
11895    function setCategoryLabel($el, categoryText) {
11896        $el.closest('.gallerygrid__select').find('.gallerygrid__category').text(categoryText);
11897    }
11898
11899    function filterGalleryResults($el, category) {
11900        // track category
11901        categoryOption = category;
11902
11903        var gallerygridComponent = $el.closest('.gallerygrid');
11904
11905        // filter grid
11906        var gridItems = gallerygridComponent.find('.gallerygrid__col');
11907        gridItems.each(function () {
11908            if (category != '') {
11909                if (!$(this).hasClass(category)) {
11910                    $(this).hide();
11911                } else {
11912                    $(this).show();
11913                }
11914            } else {
11915                $(this).show();
11916            }
11917        });
11918
11919        // handle pagination
11920        var $categoryItems = gallerygridComponent.find('.gallerygrid__col');
11921        if (category != '') {
11922            $categoryItems = gallerygridComponent.find('.gallerygrid__col.' + category);
11923        }
11924        var $pagination = gallerygridComponent.find('.gallerygrid__pagination');
11925        initPagination($pagination, $categoryItems, 16);
11926
11927        // filter modal carousels. reset their markup, then detach slides with remove()
11928        var thisCarousel = gallerygridComponent.find('.gallerygrid__carousel');
11929        for (var i = 0; i < originalCarouselSlides.length; i++) {
11930            if (originalCarouselSlides[i].id == thisCarousel.attr('id')) {
11931                thisCarousel.find('.slides').html(originalCarouselSlides[i].markup);
11932            }
11933        }
11934        var thisThumbnails = gallerygridComponent.find('.gallerygrid__thumbnails');
11935        for (var i = 0; i < originalThumbnailSlides.length; i++) {
11936            if (originalThumbnailSlides[i].id == thisThumbnails.attr('id')) {
11937                thisThumbnails.find('.slides').html(originalThumbnailSlides[i].markup);
11938            }
11939        }
11940        var slides = gallerygridComponent.find('.slide');
11941        slides.each(function (i) {
11942            if (category != '') {
11943                if (!$(this).hasClass(category)) {
11944                    $(this).remove();
11945                }
11946            }
11947        });
11948
11949        for (i = 0; i < carousels.length; i++) {
11950            var options = carousels[i].data('flexslider').vars;
11951            carousels[i].removeData('flexslider');
11952            carousels[i] = carousels[i].flexslider(options);
11953
11954            options = thumbnailCarousels[i].data('flexslider').vars;
11955            thumbnailCarousels[i].removeData('flexslider');
11956            thumbnailCarousels[i] = thumbnailCarousels[i].flexslider(options);
11957
11958            carousels[i].data('flexslider').flexAnimate(0);
11959            thumbnailCarousels[i].data('flexslider').flexAnimate(0);
11960        }
11961    }
11962});
11963$(document).ready(function () {
11964    var date = /\d{4}/.exec(Date())[0];
11965    var defaultText = " Southern Company. Use constitutes acceptance of General Website."
11966
11967    //a default copyright if no message is specified in dialogue
11968    var defaultElement = document.getElementById('footer-copyright-default');
11969    defaultElement ? defaultElement.innerHTML = "&copy; " + date + defaultText : null;
11970
11971    //setting year for copyright if a message is specified in dialogue
11972    var customElement = document.getElementById('footer-copyright-custom');
11973    var text = "&copy; " + date + " ";
11974    customElement ? customElement.insertAdjacentHTML("afterbegin", text) : null;
11975})
11976$(function () {
11977  $(".featured--article-rail[data-type='latest'], .featured--article-rail[data-type='latestPublished']").each(function () {
11978    var $this = $(this);
11979    var data = $this.find('.featured-article-data');
11980    var cols = data.attr('data-columns');
11981    var root = $this;
11982    var showAuthor = data.attr('data-show-author') === 'true';
11983
11984    var filter = $this.attr('data-type') === 'latestPublished' ? 'most-recent-published' : 'most-recent';
11985
11986    $.get('/bin/southern/global/latestarticles', {
11987      tags: data.attr('data-tags'),
11988      path: data.attr('data-path'),
11989      articleStart: 0,
11990      limit: 2,
11991      filter: filter,
11992    }).done(function (data) {
11993      var pages = data.pages;
11994      pages = pages.filter(function (s) {
11995        return s.path != window.location.href;
11996      });
11997
11998      if (pages.length == 0) {
11999        return;
12000      }
12001
12002      var page = pages[0];
12003      var redirect = page.customRedirect ? page.customRedirect : page.path;
12004      var imageAlt = escapeHtmlAttr(page.articlePromoImageAlt);
12005      var article = $(
12006        '<div class="featured--article-image-section" > <a href="' +
12007          redirect + 
12008          '" ><img class="featured--article-image sr-only" src="' +
12009          page.articlePromoImage +
12010          '" alt="' +
12011          imageAlt +
12012          '"/><figure class="fixedratio" style="background-image: url(' +
12013          page.articlePromoImage +
12014          ');"></figure></a></div><div class="featured--article-tag">' +
12015          page.primaryTag +
12016          '</div><div class="featured--article-title-section"><a href="' +
12017          redirect + 
12018          '" class="featured--article-title">' +
12019          page.title +
12020          '</a></div>'
12021      );
12022
12023      if (page.tagClasses) {
12024        root.addClass(page.tagClasses);
12025      }
12026
12027      root.append(article);
12028
12029      root.append(
12030        $('<div class="featured--article-desc"></div>').append(
12031          $('<p></p>').text(page.description)
12032        )
12033      );
12034
12035      if (showAuthor && page.author) {
12036        var $author = $('<div class="featured--article-author"></div>');
12037        if (page.authorLink) {
12038          $author.append(
12039            $('<a class="featured--article-author-link"></a>')
12040              .attr('href', page.authorLink)
12041              .attr('aria-label', page.author)
12042              .text(page.author)
12043          );
12044        } else {
12045          $author.text(page.author);
12046        }
12047        root.append($author);
12048      }
12049    });
12050  });
12051
12052  function escapeHtmlAttr(value) {
12053    if (value == null || value === '') {
12054      return '';
12055    }
12056    return String(value)
12057      .replace(/&/g, '&amp;')
12058      .replace(/"/g, '&quot;')
12059      .replace(/'/g, '&#39;')
12060      .replace(/</g, '&lt;')
12061      .replace(/>/g, '&gt;');
12062  }
12063});
12064
12065$(function () {
12066	$('.deep-nav').each(function () {
12067		var $nav = $(this),
12068			$contents = $nav.find('.deep-nav--contents'),
12069			$entries = $nav.find('.deep-nav--entries .deep-nav--entry'),
12070			$remainder = $nav.find('.deep-nav--remainder'),
12071			$menu_trigger = $nav.find('.deep-nav--more-entry, .deep-nav--sections-entry'),
12072			$more_list = $nav.find('.deep-nav--more-entries');
12073
12074		if ($entries.length < 6 && $(window).width() > 991) {
12075			$('.deep-nav--remainder').addClass('hidden');
12076		}
12077		else {
12078		    $('.deep-nav--remainder').removeClass('hidden');
12079		}
12080
12081		/* We'll calculate these in a minute */
12082		var $dropdown_entries;
12083
12084		function component_onready() {
12085			$dropdown_entries = clone_entries_to_more();
12086
12087			/* Check entry visibility now and on resize. */
12088			update_entry_visibility();
12089			$(window).resize(function () {
12090				update_entry_visibility();
12091			});
12092
12093			/* Expose/hide the "More" menu. */
12094			$menu_trigger.click(function () {
12095				$remainder.toggleClass('visible');
12096				return false;
12097			});
12098
12099			/* When a "More" link is clicked, close the menu (in case it's an
12100			 * internal link */
12101			$more_list.find('a').click(function () {
12102				$remainder.removeClass('active');
12103				// and continue with the default handler
12104			});
12105
12106			/* Now that we've set everything up, we can safely set our overflow to
12107			 * visible so that we can see menu contents. Previously the overfslow
12108			 * was hidden to hide unprocessed entries that had wrapped. */
12109			$nav.css('overflow', 'visible');
12110		}
12111
12112		/* Clone all of the entries into the More menu.
12113		 *
12114		 * We'll "move" items between the main list and the More menu by
12115		 * selectively hiding and showing them in the appropriate place. */
12116		function clone_entries_to_more() {
12117			$entries.clone().appendTo($more_list);
12118			return $more_list.find('.deep-nav--entry');
12119		}
12120
12121		/* Make entries visible in the right place.
12122		 *
12123		 * Keep as many visible in the main entries list as we can, but if some
12124		 * won't fit then we hide them in the main list and show them in the
12125		 * "More" list. If no main entries are visible, then put us in Singleton
12126		 * mode, where the More menu takes the name of the current page. */
12127		function update_entry_visibility() {
12128			/* First we loop through the entries, making them visible in the main
12129			 * list if the width allows, or in the More list otherwise. */
12130			var available_width = $contents.innerWidth() - $remainder.outerWidth(),
12131				last_visible = -1,
12132				total_width = 0;
12133			for (var i = 0; i < $entries.length; i++) {
12134				var $entry = $($entries[i]);
12135				total_width += $entry.outerWidth();
12136				if (total_width < available_width) {
12137					$($entries[i]).addClass('visible');
12138					$($dropdown_entries[i]).removeClass('visible');
12139					last_visible = i;
12140				} else {
12141					$($entries[i]).removeClass('visible');
12142					$($dropdown_entries[i]).addClass('visible');
12143				}
12144			}
12145
12146			/* If we end up with a single element in the "More" menu, move an
12147			 * extra one in there. The "More" menu looks weird with only one item. */
12148			if (last_visible == $entries.length - 2) {
12149				$($entries[last_visible]).removeClass('visible');
12150				$($dropdown_entries[last_visible]).addClass('visible');
12151				last_visible--;
12152			}
12153
12154			/* If there are any hidden items, mark the "More" menu as visible */
12155			if (last_visible == $entries.length - 1) {
12156				// -1 because 0-based array index
12157				$remainder.removeClass('visible');
12158			} else {
12159				$remainder.addClass('visible');
12160			}
12161		}
12162
12163		component_onready();
12164
12165	});
12166});
12167/**
12168 * Content Card Text Highlight Animation
12169 * 
12170 * Highlights text elements within content cards based on their proximity
12171 * to the viewport center. Uses GSAP SplitText to split text into lines
12172 * and highlights the N closest lines to center.
12173 * 
12174 * Supports multiple content card instances on a single page, each identified
12175 * by their unique instanceID.
12176 * 
12177 * Works with custom scroll containers (not just window/body scroll).
12178 */
12179document.addEventListener('DOMContentLoaded', function() {
12180    // Find all content cards with highlight animation enabled
12181    const contentCards = document.querySelectorAll('.wrap-content-card[data-enable-highlight-animation="true"]');
12182    
12183    if (contentCards.length === 0) {
12184        return;
12185    }
12186
12187    // Check if GSAP and SplitText are available
12188    if (typeof gsap === 'undefined') {
12189        console.warn('Highlight Animation: GSAP is not loaded');
12190        return;
12191    }
12192
12193    if (typeof SplitText === 'undefined') {
12194        console.warn('Highlight Animation: SplitText plugin is not loaded');
12195        return;
12196    }
12197
12198    // Register SplitText plugin
12199    gsap.registerPlugin(SplitText);
12200
12201    // Store all highlight instances keyed by instanceID
12202    const highlightInstances = {};
12203    
12204    // Store scroll containers to attach listeners
12205    const scrollContainers = new Set();
12206
12207    // Desktop breakpoint - text scaling only applies at this width and above
12208    const DESKTOP_BREAKPOINT = 992; // Matches @screen-md-min from variables.less
12209
12210    /**
12211     * Check if viewport is at desktop breakpoint or above
12212     */
12213    const isDesktopBreakpoint = function() {
12214        return window.innerWidth >= DESKTOP_BREAKPOINT;
12215    };
12216
12217    /**
12218     * Find the scrolling container for an element
12219     * Traverses up the DOM to find the element with overflow: scroll/auto
12220     */
12221    const findScrollContainer = function(element) {
12222        let parent = element.parentElement;
12223        
12224        while (parent && parent !== document.body && parent !== document.documentElement) {
12225            const style = window.getComputedStyle(parent);
12226            const overflowY = style.overflowY;
12227            const overflowX = style.overflowX;
12228            
12229            if (overflowY === 'scroll' || overflowY === 'auto' || 
12230                overflowX === 'scroll' || overflowX === 'auto') {
12231                // Check if it actually has scrollable content
12232                if (parent.scrollHeight > parent.clientHeight || parent.scrollWidth > parent.clientWidth) {
12233                    return parent;
12234                }
12235            }
12236            parent = parent.parentElement;
12237        }
12238        
12239        // Default to document/window
12240        return null;
12241    };
12242
12243    // Initialize each content card
12244    contentCards.forEach(function(card, index) {
12245        // Get unique instance ID from the card's id attribute
12246        const instanceId = card.id || 'highlight-instance-' + index;
12247        
12248        const config = {
12249            instanceId: instanceId,
12250            numberOfLines: parseInt(card.getAttribute('data-number-of-lines-highlighted')) || 2,
12251            highlightColor: card.getAttribute('data-highlighted-text-color') || '#FFFFFF', // Default to white
12252            highlightScale: card.getAttribute('data-highlighted-text-scale') || null,
12253            borderLeftColor: card.getAttribute('data-highlighted-border-left-color') || null,
12254            borderRightColor: card.getAttribute('data-highlighted-border-right-color') || null,
12255            borderLeftPadding: card.getAttribute('data-highlighted-border-left-padding') || '0px',
12256            borderRightPadding: card.getAttribute('data-highlighted-border-right-padding') || '0px'
12257        };
12258
12259        // Find the content container within this specific card instance
12260        const contentContainer = card.querySelector('.content-card--content');
12261        if (!contentContainer) {
12262            console.warn('Highlight Animation: No content container found for card', instanceId);
12263            return;
12264        }
12265
12266        // Find the scroll container for this card
12267        const scrollContainer = findScrollContainer(card);
12268        if (scrollContainer) {
12269            scrollContainers.add(scrollContainer);
12270        }
12271
12272        // Find all text elements (headings and paragraphs) within this instance only
12273        const textElements = contentContainer.querySelectorAll('h1, h2, h3, h4, h5, h6, p');
12274        if (textElements.length === 0) {
12275            console.warn('Highlight Animation: No text elements found for card', instanceId);
12276            return;
12277        }
12278
12279        // Store data for each text element
12280        const elementsData = [];
12281
12282        textElements.forEach(function(element, elemIndex) {
12283            // Store original styles
12284            const computedStyle = window.getComputedStyle(element);
12285            const originalColor = computedStyle.color;
12286            const textAlign = computedStyle.textAlign;
12287
12288            // Split text into lines with instance-specific class
12289            const split = new SplitText(element, { 
12290                type: 'lines',
12291                linesClass: 'highlight-line highlight-line--' + instanceId.replace(/[^a-zA-Z0-9-_]/g, '-')
12292            });
12293
12294            // Determine transform origin based on text alignment
12295            // Left-aligned text scales to the right, right-aligned text scales to the left
12296            let transformOrigin = 'left center'; // default for left/start alignment
12297            if (textAlign === 'right' || textAlign === 'end') {
12298                transformOrigin = 'right center';
12299            } else if (textAlign === 'center') {
12300                transformOrigin = 'center center';
12301            }
12302
12303            // Store data for each line
12304            const linesData = split.lines.map(function(line, lineIndex) {
12305                // Add data attribute for debugging
12306                line.setAttribute('data-highlight-instance', instanceId);
12307                line.setAttribute('data-highlight-line-index', lineIndex);
12308                
12309                // Set transition on line (color, transform, borders, and margins)
12310                line.style.transition = 'color 0.6s ease, transform 0.6s ease, border-left-color 0.6s ease, border-right-color 0.6s ease, padding-left 0.6s ease, padding-right 0.6s ease, margin-top 0.6s ease, margin-bottom 0.6s ease';
12311                line.style.transformOrigin = transformOrigin;
12312                line.style.display = 'block'; // Block display prevents horizontal overlap
12313                line.style.whiteSpace = 'nowrap'; // Prevents text from breaking within a line
12314                
12315                return {
12316                    element: line,
12317                    originalColor: originalColor,
12318                    transformOrigin: transformOrigin,
12319                    isHighlighted: false
12320                };
12321            });
12322
12323            elementsData.push({
12324                element: element,
12325                split: split,
12326                lines: linesData,
12327                originalColor: originalColor,
12328                textAlign: textAlign
12329            });
12330        });
12331
12332        // Store instance data keyed by instanceId
12333        const instance = {
12334            instanceId: instanceId,
12335            card: card,
12336            config: config,
12337            contentContainer: contentContainer,
12338            elementsData: elementsData
12339        };
12340
12341        highlightInstances[instanceId] = instance;
12342    });
12343
12344    if (Object.keys(highlightInstances).length === 0) {
12345        return;
12346    }
12347
12348    /**
12349     * Get the vertical center of the viewport
12350     * Uses viewport height directly since getBoundingClientRect is viewport-relative
12351     */
12352    const getViewportCenterY = function() {
12353        return window.innerHeight / 2;
12354    };
12355
12356    /**
12357     * Get the vertical center of an element relative to viewport
12358     */
12359    const getElementCenterY = function(element) {
12360        const rect = element.getBoundingClientRect();
12361        return rect.top + (rect.height / 2);
12362    };
12363
12364    /**
12365     * Calculate distance from element center to viewport center
12366     */
12367    const getDistanceToCenter = function(element) {
12368        const viewportCenterY = getViewportCenterY();
12369        const elementCenterY = getElementCenterY(element);
12370        return Math.abs(elementCenterY - viewportCenterY);
12371    };
12372
12373    /**
12374     * Update highlights for a single instance
12375     */
12376    const updateHighlights = function(instance) {
12377        const config = instance.config;
12378        
12379        // Maximum distance from center to be considered for highlighting
12380        // Lines further than this won't highlight even if they're the closest
12381        const maxDistanceThreshold = window.innerHeight * 0.3; // 30% of viewport height
12382        
12383        // Collect all lines with their distances to center
12384        const allLines = [];
12385        
12386        instance.elementsData.forEach(function(elementData) {
12387            elementData.lines.forEach(function(lineData) {
12388                const distance = getDistanceToCenter(lineData.element);
12389                allLines.push({
12390                    lineData: lineData,
12391                    distance: distance,
12392                    parentElement: elementData.element
12393                });
12394            });
12395        });
12396
12397        // Sort by distance to center
12398        allLines.sort(function(a, b) {
12399            return a.distance - b.distance;
12400        });
12401
12402        // Filter lines that are within the threshold (close enough to viewport center)
12403        const linesWithinThreshold = allLines.filter(function(item) {
12404            return item.distance <= maxDistanceThreshold;
12405        });
12406
12407        // Get the lines to highlight (N closest to center, but only from those within threshold)
12408        const linesToHighlight = linesWithinThreshold.slice(0, config.numberOfLines);
12409        const linesToHighlightSet = new Set(linesToHighlight.map(function(item) {
12410            return item.lineData;
12411        }));
12412
12413        // Update all lines
12414        allLines.forEach(function(item) {
12415            const lineData = item.lineData;
12416            const shouldHighlight = linesToHighlightSet.has(lineData);
12417
12418            if (shouldHighlight && !lineData.isHighlighted) {
12419                // Apply highlight
12420                lineData.element.style.color = config.highlightColor;
12421                // Only apply text scaling at desktop breakpoint and above
12422                if (config.highlightScale && isDesktopBreakpoint()) {
12423                    lineData.element.style.transform = 'scale(' + config.highlightScale + ')';
12424                    // Add margin to compensate for visual scale increase and prevent overlap
12425                    var scaleMargin = ((parseFloat(config.highlightScale) - 1) * 0.5) + 'em';
12426                    lineData.element.style.marginTop = scaleMargin;
12427                    lineData.element.style.marginBottom = scaleMargin;
12428                }
12429                // Apply border colors if set
12430                if (config.borderLeftColor) {
12431                    lineData.element.style.borderLeft = '3px solid ' + config.borderLeftColor;
12432                    lineData.element.style.paddingLeft = config.borderLeftPadding;
12433                }
12434                if (config.borderRightColor) {
12435                    lineData.element.style.borderRight = '3px solid ' + config.borderRightColor;
12436                    lineData.element.style.paddingRight = config.borderRightPadding;
12437                }
12438                lineData.isHighlighted = true;
12439            } else if (!shouldHighlight && lineData.isHighlighted) {
12440                // Remove highlight
12441                lineData.element.style.color = lineData.originalColor;
12442                if (config.highlightScale) {
12443                    lineData.element.style.transform = 'scale(1)';
12444                    // Remove margin compensation
12445                    lineData.element.style.marginTop = '0';
12446                    lineData.element.style.marginBottom = '0';
12447                }
12448                // Remove borders
12449                if (config.borderLeftColor) {
12450                    lineData.element.style.borderLeft = '0px solid transparent';
12451                    lineData.element.style.paddingLeft = '0px';
12452                }
12453                if (config.borderRightColor) {
12454                    lineData.element.style.borderRight = '0px solid transparent';
12455                    lineData.element.style.paddingRight = '0px';
12456                }
12457                lineData.isHighlighted = false;
12458            }
12459        });
12460    };
12461
12462    /**
12463     * Update all highlight instances
12464     */
12465    const updateAllHighlights = function() {
12466        Object.values(highlightInstances).forEach(updateHighlights);
12467    };
12468
12469    // Initial update
12470    updateAllHighlights();
12471
12472    // Update on scroll with throttling using requestAnimationFrame
12473    let isScrolling = false;
12474
12475    const handleScroll = function() {
12476        if (!isScrolling) {
12477            isScrolling = true;
12478            requestAnimationFrame(function() {
12479                updateAllHighlights();
12480                isScrolling = false;
12481            });
12482        }
12483    };
12484
12485    // Attach scroll listener to window
12486    window.addEventListener('scroll', handleScroll, { passive: true });
12487    
12488    // Attach scroll listener to document (for some edge cases)
12489    document.addEventListener('scroll', handleScroll, { passive: true });
12490    
12491    // Attach scroll listener to all detected scroll containers
12492    scrollContainers.forEach(function(container) {
12493        container.addEventListener('scroll', handleScroll, { passive: true });
12494    });
12495
12496    // Update on resize (lines may reflow)
12497    let resizeTimeout;
12498    let isResizing = false;
12499    
12500    window.addEventListener('resize', function() {
12501        clearTimeout(resizeTimeout);
12502        isResizing = true;
12503        
12504        // Use longer debounce (500ms) to ensure resize has truly stopped
12505        resizeTimeout = setTimeout(function() {
12506            // Re-split text on resize as lines may have changed
12507            Object.values(highlightInstances).forEach(function(instance) {
12508                const instanceId = instance.instanceId;
12509                const contentContainer = instance.contentContainer;
12510                
12511                // Step 1: Fade out content container to hide the jarring transition
12512                contentContainer.style.transition = 'opacity 0.2s ease';
12513                contentContainer.style.opacity = '0';
12514                
12515                // Step 2: After fade out, perform the re-split
12516                setTimeout(function() {
12517                    instance.elementsData.forEach(function(elementData) {
12518                        // Revert split
12519                        elementData.split.revert();
12520                        
12521                        // Re-split with instance-specific class
12522                        elementData.split = new SplitText(elementData.element, {
12523                            type: 'lines',
12524                            linesClass: 'highlight-line highlight-line--' + instanceId.replace(/[^a-zA-Z0-9-_]/g, '-')
12525                        });
12526
12527                        // Determine transform origin based on text alignment
12528                        let transformOrigin = 'left center';
12529                        if (elementData.textAlign === 'right' || elementData.textAlign === 'end') {
12530                            transformOrigin = 'right center';
12531                        } else if (elementData.textAlign === 'center') {
12532                            transformOrigin = 'center center';
12533                        }
12534
12535                        // Update lines data with transitions disabled initially
12536                        elementData.lines = elementData.split.lines.map(function(line, lineIndex) {
12537                            // Add data attribute for debugging
12538                            line.setAttribute('data-highlight-instance', instanceId);
12539                            line.setAttribute('data-highlight-line-index', lineIndex);
12540                            
12541                            // Disable transitions initially to prevent animation artifacts
12542                            line.style.transition = 'none';
12543                            line.style.transformOrigin = transformOrigin;
12544                            line.style.display = 'block'; // Block display prevents horizontal overlap
12545                            line.style.whiteSpace = 'nowrap'; // Prevents text from breaking within a line
12546                            
12547                            return {
12548                                element: line,
12549                                originalColor: elementData.originalColor,
12550                                transformOrigin: transformOrigin,
12551                                isHighlighted: false
12552                            };
12553                        });
12554                    });
12555                    
12556                    // Update highlights immediately (without transitions)
12557                    updateHighlights(instance);
12558                    
12559                    // Step 3: Re-enable transitions after a brief delay
12560                    setTimeout(function() {
12561                        instance.elementsData.forEach(function(elementData) {
12562                            elementData.lines.forEach(function(lineData) {
12563                                // Re-enable transitions
12564                                lineData.element.style.transition = 'color 0.6s ease, transform 0.6s ease, border-left-color 0.6s ease, border-right-color 0.6s ease, padding-left 0.6s ease, padding-right 0.6s ease, margin-top 0.6s ease, margin-bottom 0.6s ease';
12565                            });
12566                        });
12567                        
12568                        // Step 4: Fade content back in
12569                        contentContainer.style.opacity = '1';
12570                        
12571                        // Reset transition after fade in completes
12572                        setTimeout(function() {
12573                            contentContainer.style.transition = '';
12574                        }, 200);
12575                        
12576                        isResizing = false;
12577                    }, 50);
12578                }, 200); // Wait for fade out to complete
12579            });
12580        }, 500); // 500ms debounce to ensure resize has stopped
12581    });
12582
12583});
12584
12585$(function(){
12586	$(document).ready(function() {
12587       var phonenumber = $('.js-phone').text().replace(/[^\w\s]/gi, '').replace(' ', '');
12588       if(phonenumber.length == 10)
12589       {
12590        $('.js-phone').attr('href', 'tel:+1'+phonenumber);
12591       }
12592       else {
12593        $('.js-phone').attr('href', 'tel:+'+phonenumber);
12594       }
12595    });
12596});
12597
12598(function () {
12599
12600    function hideEmptyColumnsOnMobile() {
12601        $('.row section > div').each(function () {
12602            if ($(this).children().length === 0) {
12603                var parent = $(this).parent()
12604
12605                if (!$(parent).is(':last-child')) {
12606                    if ($(window).width() <= 750) {
12607                        parent.hide();
12608                    }
12609                    else {
12610                        parent.show();
12611
12612                    }
12613                }
12614                else {
12615                    parent.css("padding-top", 0);
12616                }
12617            }
12618
12619        })
12620    }
12621
12622    $(document).ready(function () {
12623        hideEmptyColumnsOnMobile();
12624        clearTimeout(resizeTimer);
12625        resizeTimer = setTimeout(hideEmptyColumnsOnMobile(), 250);
12626        if($('.parallax').length > 0 && navigator.userAgent.match(/Trident\/7\./))
12627        {
12628                    $('body').on("mousewheel", function () {
12629                        // remove default behavior
12630                        event.preventDefault();
12631
12632                        //scroll without smoothing
12633                        var wheelDelta = event.wheelDelta;
12634                        var currentScrollPosition = window.pageYOffset;
12635                        window.scrollTo(0, currentScrollPosition - wheelDelta);
12636                    });
12637        }
12638
12639    })
12640
12641    var resizeTimer;
12642    $(window).resize(function (e) {
12643        hideEmptyColumnsOnMobile()
12644
12645    });
12646
12647
12648}());
12649(function () {
12650	if( $('.callout-slider').length > 0 ) {
12651		var bindToClass      = 'wrap-callout-slider ',
12652			containerWidth   = 0,
12653			scrollWidth      = 0,
12654			posFromLeft      = 0,    // Stripe position from the left of the screen
12655			stripePos        = 0,    // When relative mouse position inside the thumbs stripe
12656			animated         = null,
12657			$carousel, el, $el, ratio, scrollPos, nextMore, prevMore, pos, padding;
12658
12659		
12660		$.fn.carousel = function(options){
12661			$(document)
12662				.on('mouseenter.carousel', '.' + bindToClass, calc)
12663				.on('mousemove.carousel', '.' + bindToClass, move);
12664		};
12665		// automatic binding to all elements which have the class that is assigned to "bindToClass"
12666		$.fn.carousel();
12667	}
12668	// calculate the thumbs container width
12669	function calc(e){
12670		$el = $(this).find('.callout-slider');
12671		el  = $el[0];
12672		$carousel = $el.parent();
12673
12674		nextMore = prevMore  = false; // reset
12675
12676		containerWidth       = el.clientWidth;
12677		scrollWidth          = el.scrollWidth; // the "<ul>"" width
12678		padding              = 0.2 * containerWidth; // padding in percentage of the area which the mouse movement affects
12679		posFromLeft          = $el.offset().left;
12680		stripePos            = e.pageX - padding - posFromLeft;
12681		pos                  = stripePos / (containerWidth - padding*2);
12682		scrollPos            = (scrollWidth - containerWidth ) * pos;
12683		
12684		if( scrollPos < 0 )
12685		scrollPos = 0;
12686		if( scrollPos > (scrollWidth - containerWidth) )
12687		scrollPos = scrollWidth - containerWidth;
12688	
12689		$el.animate({scrollLeft:scrollPos}, 200, 'swing');
12690		
12691		clearTimeout(animated);
12692		animated = setTimeout(function(){
12693			animated = null;
12694		}, 200);
12695
12696		return this;
12697	}
12698	// move the stripe left or right according to mouse position
12699	function move(e){
12700		// don't move anything until inital movement on 'mouseenter' has finished
12701		if( animated ) return;
12702
12703		ratio     = scrollWidth / containerWidth;
12704		stripePos = e.pageX - padding - posFromLeft; // the mouse X position, "normalized" to the carousel position
12705
12706		if( stripePos < 0)
12707			stripePos = 0;
12708
12709		pos = stripePos / (containerWidth - padding*2); // calculated position between 0 to 1
12710		// calculate the percentage of the mouse position within the carousel
12711		scrollPos = (scrollWidth - containerWidth ) * pos;   
12712
12713		el.scrollLeft = scrollPos;
12714
12715
12716		// check if element has reached an edge
12717		prevMore = el.scrollLeft > 0;
12718		nextMore = el.scrollLeft < (scrollWidth - containerWidth);
12719
12720		$carousel.toggleClass('left', prevMore);
12721		$carousel.toggleClass('right', nextMore);
12722	}
12723
12724}());
12725$(function () {
12726  $numberOfPeople = $('#numberOfPeople');
12727  $grossMonthlyIncome = $('#grossMonthlyIncome');
12728  $county = $('#county');
12729
12730  var titles = [];
12731
12732  var url = new URL(window.location.href);
12733  var c3PathValue = url.searchParams.get('c3path');
12734  var c3Titles = c3PathValue ? c3PathValue.split('|') : [];
12735  if (c3Titles.length > 0) {
12736    $('.eligible-programs__result').hide();
12737    c3Titles.forEach(function (val) {
12738      $('.eligible-programs__result[data-name="' + val + '"]').show();
12739    });
12740    showScreen(4);
12741  }
12742  function getShareUrl() {
12743    values = '';
12744    titles.forEach(function (title) {
12745      values += title + '|';
12746    });
12747
12748    return values;
12749  }
12750
12751  $('.email-button').click(function () {
12752    $('.email-submit--background').show();
12753    $('.email-submit').show();
12754  });
12755
12756  $('.email__close').click(function () {
12757    $('.email-submit--background').hide();
12758    $('.email-submit').hide();
12759  });
12760
12761  function getErrorMessage(that) {
12762    if ($(that).closest('.c3-form').hasClass('.c3-form--spanish')) {
12763      return 'This field is required.';
12764    } else {
12765      return 'This field is required.';
12766    }
12767  }
12768
12769  $('.send-email-js').click(function () {
12770    var email = $('.email--input').val();
12771    var path = $(this).data('path');
12772    resetMessage();
12773    if (!email) {
12774      $(this).addClass('input--error');
12775      $(this)
12776        .prev()
12777        .before(
12778          "<p class='input-message--error'>" + getErrorMessage(this) + '</p>'
12779        );
12780    } else {
12781      $.ajax({
12782        type: 'POST',
12783        url: '/bin/southern/global/c3email',
12784        data: {
12785          email: email,
12786          path: path,
12787          url: window.location.href,
12788          c3: getShareUrl(),
12789        },
12790        success: function (msg) {
12791          $('.email-submit--background').hide();
12792          $('.email-submit').hide();
12793        },
12794        error: function () {
12795          $(this).addClass('input--error');
12796          $(this)
12797            .prev()
12798            .before(
12799              "<p class='input-message--error'>There was an issue sending the email. Please try again later.</p>"
12800            );
12801        },
12802      });
12803    }
12804  });
12805
12806  function resetMessage() {
12807    $('.input-message--error').remove();
12808    $('.input--error').removeClass('input--error');
12809  }
12810  $('.js-screen-1__button').click(function () {
12811    //valid
12812
12813    resetMessage();
12814    var valid = true;
12815    if ($numberOfPeople.val() === 'Select') {
12816      $numberOfPeople.addClass('input--error');
12817      $numberOfPeople
12818        .prev()
12819        .before(
12820          "<p class='input-message--error'>" + getErrorMessage(this) + '</p>'
12821        );
12822      valid = false;
12823    }
12824    if (!$grossMonthlyIncome.val()) {
12825      $grossMonthlyIncome.addClass('input--error');
12826      $grossMonthlyIncome
12827        .prev()
12828        .before(
12829          "<p class='input-message--error'>" + getErrorMessage(this) + '</p>'
12830        );
12831      valid = false;
12832    }
12833    if ($county.val() === 'Select') {
12834      $county.addClass('input--error');
12835      $county
12836        .prev()
12837        .before(
12838          "<p class='input-message--error'>" + getErrorMessage(this) + '</p>'
12839        );
12840      valid = false;
12841    }
12842    if (valid) {
12843      adjustBarForward(2);
12844      showScreen(2);
12845    }
12846  });
12847
12848  $('.js-screen-2__button').click(function () {
12849    //valid
12850    var valid = true;
12851    resetMessage();
12852    if (!$('.assistance').is(':checked')) {
12853      $('.assistance__headline').before(
12854        "<p class='input-message--error'>" + getErrorMessage(this) + '</p>'
12855      );
12856      valid = false;
12857    }
12858    if (valid) {
12859      adjustBarForward(3);
12860      showScreen(3);
12861    }
12862  });
12863
12864  $('.js-screen-3__button').click(function () {
12865    //valid
12866    var valid = true;
12867    resetMessage();
12868    hideStatus();
12869
12870    if (!$('.trouble').is(':checked')) {
12871      $('.trouble__headline').before(
12872        "<p class='input-message--error'>" + getErrorMessage(this) + '</p>'
12873      );
12874      valid = false;
12875    }
12876    if (!$('.military').is(':checked')) {
12877      $('.military__headline').before(
12878        "<p class='input-message--error'>" + getErrorMessage(this) + '</p>'
12879      );
12880      valid = false;
12881    }
12882    if (!$('.situation').is(':checked')) {
12883      $('.situation__headline').before(
12884        "<p class='input-message--error'>" + getErrorMessage(this) + '</p>'
12885      );
12886      valid = false;
12887    }
12888    if (valid) {
12889      titles = getTitlesToShow();
12890
12891      url = getShareUrl();
12892      $('.eligible-programs__result').hide();
12893      titles.forEach(function (val) {
12894        $('.eligible-programs__result[data-name="' + val + '"]').show();
12895      });
12896      showScreen(4);
12897    }
12898  });
12899
12900  $('#troublePayingYes').change(function () {
12901    if ($(this).is(':checked')) {
12902      $('.row--30-trouble').show();
12903    }
12904  });
12905
12906  $('#troublePayingNo').change(function () {
12907    if ($(this).is(':checked')) {
12908      $('.row--30-trouble').hide();
12909    }
12910  });
12911
12912  $('.js-screen').click(function () {
12913    //valid
12914    var screen = $(this).data('screen');
12915    showScreen(screen);
12916    adjustBarBack(screen);
12917  });
12918
12919  function showScreen(screenNumber) {
12920    $('.c3-screen').hide();
12921    $('.c3-screen[data-screen="' + screenNumber + '"]').show();
12922  }
12923
12924  function getValues() {
12925    var vals = [];
12926    vals['numberOfPeople'] = $numberOfPeople.val();
12927    vals['grossMonthlyIncome'] = $grossMonthlyIncome.val();
12928    vals['county'] = $county.val();
12929    $('.assistance').each(function () {
12930      var nameVal = $(this).attr('name');
12931      var value = $(this).is(':checked');
12932      vals[nameVal] = value;
12933    });
12934    vals['troublePaying'] = $('input[name=troublePaying]:checked').val();
12935    vals['troublePaying30'] = $('input[name=troublePaying30]:checked').val();
12936    vals['military'] = $('input[name=military]:checked').val();
12937    $('.situation').each(function () {
12938      var nameVal = $(this).attr('name');
12939      var value = $(this).is(':checked');
12940      vals[nameVal] = value;
12941    });
12942
12943    return vals;
12944  }
12945
12946  function getTitlesToShow() {
12947    var data = getValues();
12948    var titles = ['IE Energy Efficiency Recommendation', 'On-Bill Financing'];
12949    if (qualifyForLIHEAP(data)) {
12950      titles.push('LIHEAP');
12951    }
12952    if (qualifyForSharingProgram(data)) {
12953      titles.push('Sharing Program');
12954    }
12955    if (qualifyForSharingProgramMilitary(data)) {
12956      titles.push('Sharing Program (Military)');
12957    }
12958    if (qualifyForEnergyAideProgram(data)) {
12959      titles.push('Energy Aide Program');
12960    }
12961    if (qualifyForShieldOfCaring(data)) {
12962      titles.push('Shield of Caring');
12963    }
12964    if (incomeAndCountyQualify(data, ['Adams', 'Pike'])) {
12965      titles.push('Two Rivers Regional Council of Public Officials');
12966    }
12967    if (incomeAndCountyQualify(data, ['Boone'])) {
12968      titles.push('Rockford Human Services Department');
12969    }
12970    if (
12971      incomeAndCountyQualify(data, [
12972        'Bureau',
12973        'Carroll',
12974        'LaSalle',
12975        'Lee',
12976        'Ogle',
12977        'Whiteside',
12978      ])
12979    ) {
12980      titles.push('Tri-County Opportunities Council');
12981    }
12982    if (incomeAndCountyQualify(data, ['Champaign'])) {
12983      titles.push('Champaign County Regional Planning Commission');
12984    }
12985    if (incomeAndCountyQualify(data, ['DeKalb'])) {
12986      titles.push('Community Contacts (DeKalb County)');
12987    }
12988    if (incomeAndCountyQualify(data, ['Cook'])) {
12989      titles.push('Community and Economic Development Association');
12990    }
12991    if (incomeAndCountyQualify(data, ['DeWitt', 'Clinton', 'Edgar'])) {
12992      titles.push('Community Action Partnership of Central Illinois');
12993    }
12994    if (incomeAndCountyQualify(data, ['DuPage'])) {
12995      titles.push('DuPage County Department of Human Services');
12996    }
12997    if (incomeAndCountyQualify(data, ['Ford', 'Iroquois', 'Vermillion'])) {
12998      titles.push('East Central Illinois Community Action Agency');
12999    }
13000    if (incomeAndCountyQualify(data, ['Grundy', 'Kendall'])) {
13001      titles.push('Kendall-Grundy Community Action');
13002    }
13003    if (incomeAndCountyQualify(data, ['Hancock', 'Henderson'])) {
13004      titles.push('Western Illinois Regional Council');
13005    }
13006    if (incomeAndCountyQualify(data, ['Henry'])) {
13007      titles.push('Project NOW, Inc');
13008    }
13009    if (incomeAndCountyQualify(data, ['Jo Daviess', 'Stephenson'])) {
13010      titles.push('Northwestern Illinois Community Action Agency');
13011    }
13012    if (incomeAndCountyQualify(data, ['Kane'])) {
13013      titles.push('Community Contacts (Kane County)');
13014    }
13015    if (incomeAndCountyQualify(data, ['Kankakee'])) {
13016      titles.push('Kankakee County Community Services');
13017    }
13018    if (incomeAndCountyQualify(data, ['Lake'])) {
13019      titles.push('Community Action Partnership of Lake County');
13020    }
13021    if (incomeAndCountyQualify(data, ['Winnebago'])) {
13022      titles.push('Rockford Human Service Department');
13023    }
13024    if (incomeAndCountyQualify(data, ['Livingston', 'McLean'])) {
13025      titles.push('Mid Central Community Action, Inc.');
13026    }
13027    if (incomeAndCountyQualify(data, ['McHenry'])) {
13028      titles.push('McHenry County Housing Authority');
13029    }
13030    if (incomeAndCountyQualify(data, ['Tazewell', 'Woodford'])) {
13031      titles.push('Tazwood Community Services, Inc');
13032    }
13033    if (incomeAndCountyQualify(data, ['Will'])) {
13034      titles.push('Will County Center for Community Concerns');
13035    }
13036    if (unitedWayAndSalvationArmyQualify(data)) {
13037      titles.push('United Way');
13038      titles.push('Salvation Army');
13039    }
13040    if (marketrateEnergyEfficiencyQualify(data)) {
13041      titles.push('Marketrate Energy Efficiency');
13042    }
13043    if (qualifyBudgetBill(data)) {
13044      titles.push('Budget Bill');
13045    }
13046    if (qualifyDeferredPaymentArrangement(data)) {
13047      titles.push('Deferred Payment Arrangement');
13048    }
13049
13050    return titles;
13051  }
13052
13053  function qualifyBudgetBill(data) {
13054    var trouble = data['troublePaying'] === 'Yes';
13055    return trouble;
13056  }
13057
13058  function qualifyDeferredPaymentArrangement(data) {
13059    var trouble = data['troublePaying'] === 'Yes';
13060    var trouble30 = data['troublePaying30'] === 'Yes';
13061    return trouble && trouble30;
13062  }
13063
13064  function qualifyForLIHEAP(data) {
13065    var numberOfPeople = data['numberOfPeople'];
13066    var income = parseFloat(data['grossMonthlyIncome']);
13067    var pipp = data['Percentage of Income Payment Plan (PIPP)'];
13068    var liheap = data['Low Income Home Energy Assistance Program (LIHEAP)'];
13069    var trouble = data['troublePaying'] === 'Yes';
13070
13071    if (!liheap && !pipp && trouble) {
13072        if (numberOfPeople === '1' && income <= 2510) {
13073            return true;
13074        } else if (numberOfPeople === '2' && income <= 3407) {
13075            return true;
13076        } else if (numberOfPeople === '3' && income <= 4303) {
13077            return true;
13078        } else if (numberOfPeople === '4' && income <= 5200) {
13079            return true;
13080        } else if (numberOfPeople === '5' && income <= 6097) {
13081            return true;
13082        } else if (numberOfPeople === '6' && income <= 6993) {
13083            return true;
13084        } else if (numberOfPeople === '7' && income <= 7890) {
13085            return true;
13086        } else if (numberOfPeople === '8' && income <= 8427) {
13087            return true;
13088        } else if (numberOfPeople === '9' && income <= 8611) {
13089            return true;
13090        } else if (numberOfPeople === '10+' && income <= 8794) {
13091            return true;
13092        }
13093    }
13094    return false;
13095}
13096
13097
13098function qualifyForSharingProgram(data) {
13099  var trouble = data['troublePaying'] === 'Yes';
13100  var numberOfPeople = data['numberOfPeople'];
13101  var income = parseFloat(data['grossMonthlyIncome']);
13102
13103  if (trouble) {
13104      if (numberOfPeople === '1' && income > 2510 && income <= 3765) {
13105          return true;
13106      } else if (numberOfPeople === '2' && income > 3407 && income <= 5110) {
13107          return true;
13108      } else if (numberOfPeople === '3' && income > 4303 && income <= 6455) {
13109          return true;
13110      } else if (numberOfPeople === '4' && income > 5200 && income <= 7800) {
13111          return true;
13112      } else if (numberOfPeople === '5' && income > 6097 && income <= 9145) {
13113          return true;
13114      } else if (numberOfPeople === '6' && income > 6993 && income <= 10490) {
13115          return true;
13116      } else if (numberOfPeople === '7' && income > 7890 && income <= 11835) {
13117          return true;
13118      } else if (numberOfPeople === '8' && income > 8427 && income <= 13180) {
13119          return true;
13120      } else if (numberOfPeople === '9' && income > 8611 && income <= 14525) {
13121          return true;
13122      } else if (numberOfPeople === '10+' && income > 8794 && income <= 15870) {
13123          return true;
13124      }
13125  }
13126  return false;
13127}
13128function qualifyForSharingProgramMilitary(data) {
13129  var trouble = data['troublePaying'] === 'Yes';
13130  var military = data['military'] === 'Yes';
13131  var numberOfPeople = data['numberOfPeople'];
13132  var income = parseFloat(data['grossMonthlyIncome']);
13133
13134  if (trouble && military) {
13135      if (numberOfPeople === '1' && income < 3765) {
13136          return true;
13137      } else if (numberOfPeople === '2' && income < 5110) {
13138          return true;
13139      } else if (numberOfPeople === '3' && income < 6455) {
13140          return true;
13141      } else if (numberOfPeople === '4' && income < 7800) {
13142          return true;
13143      } else if (numberOfPeople === '5' && income < 9145) {
13144          return true;
13145      } else if (numberOfPeople === '6' && income < 10490) {
13146          return true;
13147      } else if (numberOfPeople === '7' && income < 11835) {
13148          return true;
13149      } else if (numberOfPeople === '8' && income < 13180) {
13150          return true;
13151      } else if (numberOfPeople === '9' && income < 14525) {
13152          return true;
13153      } else if (numberOfPeople === '10+' && income < 15870) {
13154          return true;
13155      }
13156  }
13157  return false;
13158}
13159function qualifyForEnergyAideProgram(data) {
13160  var trouble = data['troublePaying'] === 'Yes';
13161  var numberOfPeople = data['numberOfPeople'];
13162  var income = parseFloat(data['grossMonthlyIncome']);
13163  var trouble30 = data['troublePaying30'] === 'Yes';
13164  var county = data['county'];
13165  var countyIsValid = county !== 'Kankakee' && county !== 'Kane' && county !== 'DeKalb';
13166
13167  if (trouble && trouble30 && countyIsValid) {
13168      if (numberOfPeople === '1' && income <= 4393) {
13169          return true;
13170      } else if (numberOfPeople === '2' && income <= 5962) {
13171          return true;
13172      } else if (numberOfPeople === '3' && income <= 7531) {
13173          return true;
13174      } else if (numberOfPeople === '4' && income <= 9100) {
13175          return true;
13176      } else if (numberOfPeople === '5' && income <= 10669) {
13177          return true;
13178      } else if (numberOfPeople === '6' && income <= 12238) {
13179          return true;
13180      } else if (numberOfPeople === '7' && income <= 13808) {
13181          return true;
13182      } else if (numberOfPeople === '8' && income <= 15377) {
13183          return true;
13184      } else if (numberOfPeople === '9' && income <= 16946) {
13185          return true;
13186      } else if (numberOfPeople === '10+' && income <= 18515) {
13187          return true;
13188      }
13189  }
13190  return false;
13191}
13192
13193
13194function qualifyForShieldOfCaring(data) {
13195  var trouble = data['troublePaying'] === 'Yes';
13196  var trouble30 = data['troublePaying30'] === 'Yes';
13197  var numberOfPeople = data['numberOfPeople'];
13198  var income = parseFloat(data['grossMonthlyIncome']);
13199
13200  if (trouble && trouble30) {
13201      if (numberOfPeople === '1' && income <= 5020) {
13202          return true;
13203      } else if (numberOfPeople === '2' && income <= 6813) {
13204          return true;
13205      } else if (numberOfPeople === '3' && income <= 8607) {
13206          return true;
13207      } else if (numberOfPeople === '4' && income <= 10400) {
13208          return true;
13209      } else if (numberOfPeople === '5' && income <= 12193) {
13210          return true;
13211      } else if (numberOfPeople === '6' && income <= 13987) {
13212          return true;
13213      } else if (numberOfPeople === '7' && income <= 15780) {
13214          return true;
13215      } else if (numberOfPeople === '8' && income <= 17573) {
13216          return true;
13217      } else if (numberOfPeople === '9' && income <= 19367) {
13218          return true;
13219      } else if (numberOfPeople === '10+' && income <= 21160) {
13220          return true;
13221      }
13222  }
13223  return false;
13224}
13225
13226
13227function incomeAndCountyQualify(data, countiesArray) {
13228  var county = data['county'];
13229  var countyQualifies = countiesArray.includes(county);
13230  var numberOfPeople = data['numberOfPeople'];
13231  var income = parseFloat(data['grossMonthlyIncome']);
13232  var incomeQualify = false;
13233
13234  if (numberOfPeople === '1' && income < 2510) {
13235      incomeQualify = true;
13236  } else if (numberOfPeople === '2' && income < 3407) {
13237      incomeQualify = true;
13238  } else if (numberOfPeople === '3' && income < 4303) {
13239      incomeQualify = true;
13240  } else if (numberOfPeople === '4' && income < 5200) {
13241      incomeQualify = true;
13242  } else if (numberOfPeople === '5' && income < 6097) {
13243      incomeQualify = true;
13244  } else if (numberOfPeople === '6' && income < 6993) {
13245      incomeQualify = true;
13246  } else if (numberOfPeople === '7' && income < 7890) {
13247      incomeQualify = true;
13248  } else if (numberOfPeople === '8' && income < 8427) {
13249      incomeQualify = true;
13250  } else if (numberOfPeople === '9' && income < 8611) {
13251      incomeQualify = true;
13252  } else if (numberOfPeople === '10+' && income < 8794) {
13253      incomeQualify = true;
13254  }
13255
13256  return countyQualifies && incomeQualify;
13257}
13258
13259
13260  function unitedWayAndSalvationArmyQualify(data) {
13261    var familyCrisis = data['Family Crisis'];
13262    var lossOfEmployment = data['Loss of Employment'];
13263    var unexpectedOfIncome = data['Unexpected Lost of income'];
13264    var medicalHardship = data['Medical Hardship'];
13265    var seniorCitizen = data['Senior Citizen'];
13266    var disability = data['Disability'];
13267    return (
13268      familyCrisis ||
13269      lossOfEmployment ||
13270      unexpectedOfIncome ||
13271      medicalHardship ||
13272      seniorCitizen ||
13273      disability
13274    );
13275  }
13276  function marketrateEnergyEfficiencyQualify(data) {
13277    var numberOfPeople = data['numberOfPeople'];
13278    var income = parseFloat(data['grossMonthlyIncome']);
13279
13280    if (numberOfPeople === '1' && income > 5020) {
13281        return true;
13282    } else if (numberOfPeople === '2' && income > 6813) {
13283        return true;
13284    } else if (numberOfPeople === '3' && income > 8607) {
13285        return true;
13286    } else if (numberOfPeople === '4' && income > 10400) {
13287        return true;
13288    } else if (numberOfPeople === '5' && income > 12193) {
13289        return true;
13290    } else if (numberOfPeople === '6' && income > 13987) {
13291        return true;
13292    } else if (numberOfPeople === '7' && income > 15780) {
13293        return true;
13294    } else if (numberOfPeople === '8' && income > 17573) {
13295        return true;
13296    } else if (numberOfPeople === '9' && income > 19367) {
13297        return true;
13298    } else if (numberOfPeople === '10+' && income > 21160) {
13299        return true;
13300    }
13301
13302    return false;
13303}
13304
13305
13306  function getMessage(that) {
13307    if ($(that).closest('.c3-form').hasClass('c3-form--spanish')) {
13308      return 'Must include all income received for persons 18 years and older. Gross income is total income before taxes.';
13309    } else {
13310      return 'Debe incluir todos los ingresos recibidos por personas mayores de 18 años. El ingreso bruto es el ingreso total antes de impuestos.';
13311    }
13312  }
13313
13314  var sections = $('.gross-monthly-income__container label');
13315
13316  sections.each(function (i, element) {
13317    var text = getMessage(element);
13318    if (
13319      element.outerText ==
13320        'Must include all income received for persons 18 years and older. Gross income is total income before taxes.' ||
13321      element.outerText ==
13322        'Debe incluir todos los ingresos recibidos por personas mayores de 18 años. El ingreso bruto es el ingreso total antes de impuestos.'
13323    ) {
13324      $(element).addClass('custom-ss');
13325
13326      var parent = $(element).parent()[0];
13327
13328      $(parent).addClass('custom-image');
13329    }
13330  });
13331
13332  var imageContainer = $('.gross-monthly-income__container');
13333
13334  var img = document.createElement('img');
13335
13336  img.src =
13337    '/content/dam/global/images/icons/callout-icons/information-icon-6055.png';
13338
13339  $(imageContainer[0]).append(img);
13340
13341  $('.gross-monthly-income__container img').on('click touch', function () {
13342    if ($(this).parent().hasClass('gross-monthly__image--hover')) {
13343      $(this).parent().removeClass('gross-monthly__image--hover');
13344    } else {
13345      $(this).parent().addClass('gross-monthly__image--hover');
13346    }
13347  });
13348
13349  $('.gross-monthly-income__container img').on('mouseover', function () {
13350    $(this).parent().addClass('gross-monthly__image--hover');
13351
13352    $(this).one('mouseleave', function () {
13353      $(this).parent().removeClass('gross-monthly__image--hover');
13354    });
13355  });
13356});
13357
13358
13359
13360function adjustBarForward(stepTo) {
13361    var lines = $(".c3-status").find(".spacer-line");
13362    var steps = $(".c3-status").find(".number-container");
13363    var tags = $(".c3-status").find(".number-container p");
13364    $(steps[stepTo - 1]).addClass("active");
13365    $(lines[stepTo - 2]).addClass("active");
13366    $(steps[stepTo - 2]).addClass("complete");
13367    $(tags[stepTo - 2]).addClass("checkmark");
13368
13369}
13370
13371function adjustBarBack(stepTo) {
13372    var lines = $(".c3-status").find(".spacer-line");
13373    var steps = $(".c3-status").find(".number-container");
13374    var tags = $(".c3-status").find(".number-container p");
13375    $(steps[stepTo]).removeClass("active");
13376    $(lines[stepTo - 1]).removeClass("active");
13377    $(steps[stepTo - 1]).removeClass("complete");
13378    $(tags[stepTo - 1]).removeClass("checkmark");
13379    $('.c3-status').removeClass("hide");
13380}
13381
13382function hideStatus() {
13383    $('.c3-status').addClass("hide");
13384}
13385let openCard = undefined;
13386
13387document
13388  .querySelectorAll('.business-solutions .expansion-button button')
13389  .forEach((button) => {
13390
13391    // Handle mouse enter (hover)
13392    button.addEventListener('mouseenter', (event) => {
13393      if (event.pointerType != 'mouse') {
13394        return;
13395      }
13396      if (event.currentTarget.dataset.state == "closed") {
13397        button.parentElement.classList.add('expansion-button-hover');
13398        button.parentElement.classList.remove('expansion-button');
13399      } else {
13400        button.parentElement.classList.add('expansion-button-hover');
13401        button.parentElement.classList.remove('expansion-button');
13402      }
13403
13404    });
13405
13406    function handleopenCardPosition(card, expand) {
13407      const container = document.querySelector('.solutions-cards-container');
13408      if (!card) return;
13409      console.log(card.classList)
13410      console.log(expand);
13411
13412      if (!expand) {
13413        console.log(card.classList)
13414        card.classList.remove('force-fit-content');
13415        return;
13416      }
13417    
13418      const className = 'expanded-business-card';
13419      
13420      const cardBottom = card.getBoundingClientRect().bottom + window.scrollY;
13421      const containerBottom = container.getBoundingClientRect().bottom + window.scrollY;
13422      if ((containerBottom - cardBottom) <= 500) {
13423        card.classList.add('force-fit-content');
13424      }    
13425    }
13426
13427
13428    button.addEventListener('click', (event) => {
13429      if (event.pointerType != 'mouse') {
13430        if (event.currentTarget.dataset.state == "closed") {
13431          if (openCard != undefined) {
13432            openCard.classList.remove('expanded-business-card');
13433            const button = openCard.querySelector('button[data-state="open"]');
13434            button.dataset.state = 'closed';
13435            handleopenCardPosition(openCard, false);
13436          }
13437          openCard = $(event.currentTarget).closest('.card')[0];
13438        openCard.classList.add('expanded-business-card');
13439        handleopenCardPosition(openCard, true);
13440        event.currentTarget.dataset.state = "open";
13441        } else {
13442          openCard.classList.remove('expanded-business-card');
13443          event.currentTarget.dataset.state = "closed";
13444          handleopenCardPosition(openCard, false);
13445          openCard = undefined;
13446        }
13447        if (button.parentElement.classList.contains('expansion-button-hover')) {
13448          button.parentElement.classList.remove('expansion-button-hover');
13449          button.parentElement.classList.add('expansion-button');
13450        }
13451        return;
13452      }
13453      if (event.currentTarget.dataset.state == "closed") {
13454        if (openCard != undefined) {
13455          openCard.classList.remove('expanded-business-card');
13456          const button = openCard.querySelector('button[data-state="open"]');
13457          button.dataset.state = 'closed';
13458          handleopenCardPosition(openCard, false);
13459        }
13460        openCard = $(event.currentTarget).closest('.card')[0];
13461        openCard.classList.add('expanded-business-card');
13462        handleopenCardPosition(openCard, true);
13463        event.currentTarget.dataset.state = "open";
13464        
13465      } else {
13466        openCard.classList.remove('expanded-business-card');
13467        event.currentTarget.dataset.state = "closed";
13468        handleopenCardPosition(openCard, false);
13469        openCard = undefined;
13470      }
13471    })
13472  });
13473
13474
13475
13476$(document).ready(function () {
13477  const originalArray = [];
13478  var arrayLength = 0;
13479  const hiddenCards = [];
13480  var dropdownActive = false;
13481
13482  const handler = {
13483    set(target, property, value) {
13484      var array = Reflect.set(target, property, value);
13485      // Updates
13486      onArrayResize(target);
13487      // Reflect the change to the actual array
13488      return array;
13489    },
13490  };
13491  // Wrap the array with a Proxy
13492  const activeFiltersList = new Proxy(originalArray, handler);
13493
13494  // Custom function to be triggered on array modification
13495  function onArrayResize(arr) {
13496    if (arr.length > arrayLength) {
13497      updateFilter();
13498      arrayLength = arr.length;
13499    } else if (arr.length < arrayLength) {
13500      updateFilter();
13501      arrayLength = arr.length;
13502    }
13503  }
13504
13505  function updateFilter() {
13506    const allCards = document.querySelectorAll(
13507      '.business-solutions .solutions-cards-container .card'
13508    );
13509    const activeFilters = activeFiltersList.map(
13510      (filter) => Object.keys(filter)[0]
13511    );
13512
13513    hiddenCards.length = 0;
13514
13515    allCards.forEach((card) => {
13516      const goals = [
13517        card.dataset.goal1,
13518        card.dataset.goal2,
13519        card.dataset.goal3,
13520        card.dataset.businesssize,
13521      ];
13522
13523      const matchesAny = activeFilters.some((f) => goals.includes(f));
13524
13525      if (activeFilters.length === 0 || matchesAny) {
13526        // Show card
13527        card.parentElement.classList.remove('inactive-card');
13528        card.parentElement.style.display = 'block';
13529      } else {
13530        // Hide card
13531        hiddenCards.push(card);
13532        card.parentElement.classList.add('inactive-card');
13533        setTimeout(() => {
13534          card.parentElement.style.display = 'none';
13535        }, 500);
13536      }
13537    });
13538
13539    updateCardCount();
13540  }
13541
13542  const allCards = document.querySelectorAll(
13543    '.business-solutions .goals-filter-container .goals-filter-tags > ul > li'
13544  );
13545
13546  if (allCards) {
13547    allCards.forEach((buttion) => {
13548      buttion.addEventListener('click', handleButtonClick);
13549      buttion.addEventListener('touch', handleButtonClick);
13550    });
13551  }
13552
13553  const businessSizeFilter = document.getElementById('business-size-filter');
13554  if (businessSizeFilter) {
13555    businessSizeFilter.addEventListener('change', handleDropdownChange);
13556  }
13557
13558  function handleDropdownChange(event) {
13559    if (!dropdownActive && this.value != 'empty') {
13560      addToActiveFilters(this, event);
13561      return;
13562    } else {
13563      const filterButton = document.querySelector(
13564        '.filter-button[data-dropdown="true"]'
13565      );
13566      handleDropdownFilterButtonClick(filterButton, event.target.selectedIndex);
13567      if (this.value != 'empty') {
13568        addToActiveFilters(this, event);
13569      }
13570    }
13571  }
13572
13573  function handleButtonClick() {
13574    const dataFilter = this.dataset.filter;
13575    const dataTitle = this.textContent.trim();
13576
13577    if (!this.classList.contains('active-filter')) {
13578      this.classList.add('active-filter');
13579      addToActiveFilters(this);
13580    } else {
13581      handleFilterButtonClick(this);
13582    }
13583  }
13584  function handleFilterButtonClick(originClick) {
13585    activeFiltersList.forEach((filter, index) => {
13586      if (originClick.dataset.filter in filter) {
13587        $(filter[originClick.dataset.filter].target[0]).remove();
13588        console.log(filter[originClick.dataset.filter].origin);
13589        //if the origin is from a button
13590        filter[originClick.dataset.filter].origin.classList.remove(
13591          'active-filter'
13592        );
13593        activeFiltersList.splice(index, 1);
13594        return;
13595      }
13596    });
13597  }
13598
13599  function handleDropdownFilterButtonClick(originSelect, resetIndex) {
13600    activeFiltersList.forEach((filter, index) => {
13601      if (originSelect.dataset.filter in filter) {
13602        $(filter[originSelect.dataset.filter].target[0]).remove();
13603        filter[originSelect.dataset.filter].origin.selectedIndex = resetIndex
13604          ? resetIndex
13605          : 0;
13606        dropdownActive = false;
13607        activeFiltersList.splice(index, 1);
13608        return;
13609      }
13610    });
13611  }
13612
13613  function addToActiveFilters(activatingFilter, dropdownEvent) {
13614    var filterValue;
13615    var methodNeeded;
13616    var newHTML;
13617    if (activatingFilter.classList.contains('active-filter')) {
13618      filterValue = activatingFilter.dataset.filter;
13619      newHTML = createRemoveFilterButton(
13620        filterValue,
13621        activatingFilter.textContent.trim(),
13622        false
13623      );
13624      methodNeeded = handleFilterButtonClick;
13625    } else {
13626      dropdownActive = true;
13627      filterValue = activatingFilter.value;
13628      newHTML = createRemoveFilterButton(
13629        filterValue,
13630        dropdownEvent.target.options[event.target.selectedIndex].text,
13631        true
13632      );
13633      methodNeeded = handleDropdownFilterButtonClick;
13634    }
13635    activeFiltersList.push({
13636      [filterValue]: {
13637        origin: activatingFilter,
13638        target: newHTML,
13639      },
13640    });
13641    const filtersList = $(
13642      '.business-solutions .active-filter-rail .filters-list'
13643    );
13644    filtersList.append(newHTML);
13645    newHTML[0].addEventListener('click', function () {
13646      methodNeeded(this);
13647    });
13648    newHTML[0].addEventListener('touch', function () {
13649      methodNeeded(this);
13650    });
13651  }
13652
13653  function createRemoveFilterButton(filterValue, filterTitle, isDropdown) {
13654    const activeFilterRail = $('.business-solutions .active-filter-rail');
13655    const iconPath = activeFilterRail.data('iconpath');
13656
13657    return $(`
13658      <div class="filter-button" data-filter="${filterValue}" data-dropdown="${isDropdown}">
13659          <button><span><img src="${iconPath}/filter-close-icon.svg"/></span><div>${filterTitle}</div></button>
13660      </div>
13661  `);
13662  }
13663
13664  function updateCardCount() {
13665    console.log('update card count');
13666    const resultsCount = $(
13667      '.business-solutions .active-filter-rail .results-count'
13668    );
13669    const resultsMessage = $('.business-solutions .no-results-container')[0];
13670    // Select cards with data-hidden="false"
13671    var cardCount = document.querySelectorAll(
13672      '.business-solutions .solutions-cards-container .card-wrapper:not(.inactive-card)'
13673    ).length;
13674
13675    // Remove any existing HTML inside resultsCount
13676    resultsCount.empty();
13677
13678    if (hiddenCards.length == 0 && activeFiltersList.length == 0) {
13679      if (!resultsMessage.classList.contains('hidden-container')) {
13680        resultsMessage.classList.add('hidden-container');
13681      }
13682      return;
13683    }
13684    // If no cards are present, exit early
13685    if (cardCount === 0) {
13686      resultsCount.text('0 results');
13687      if (resultsMessage.classList.contains('hidden-container')) {
13688        resultsMessage.classList.remove('hidden-container');
13689      }
13690      return;
13691    }
13692
13693    // Add text displaying the card count, e.g., '3 results'
13694    if (!resultsMessage.classList.contains('hidden-container')) {
13695      resultsMessage.classList.add('hidden-container');
13696    }
13697
13698    resultsCount.text(`${cardCount} result${cardCount > 1 ? 's' : ''}`);
13699  }
13700});
13701
13702(function () {
13703    var offset = 500,
13704	/*duration of the top scrolling animation (in ms)*/
13705			scroll_top_duration = 700;
13706
13707	/*Back to Top*/
13708	$('.backtotop-button:not(.backtotop-button--fixed)').each(function () {
13709		$back_to_top = $(this);
13710		/*browser window scroll (in pixels) after which the "back to top" button is visible*/
13711
13712
13713		/*hide or show the "back to top" link*/
13714		$(window).scroll(function () {
13715			($(this).scrollTop() > offset) ? $back_to_top.addClass('backtotop-button--visible'): $back_to_top.removeClass('backtotop-button--visible');
13716		});
13717		/*smooth scroll to top*/
13718
13719	});
13720
13721	$('.backtotop-button').on('click', function (e) {
13722    			e.preventDefault();
13723    			$('body,html').animate({
13724    				scrollTop: 0
13725    			}, scroll_top_duration);
13726    });
13727
13728}());
13729$(function () {
13730  $('.search-icon--js').click(function () {
13731    var url = $(this).closest('form').submit();
13732  });
13733  $('.categories-dropdown--button').click(function(){
13734        $('.categories-dropdown').toggle();
13735        $(this).toggleClass('categories-dropdown--button-on')
13736  })
13737
13738
13739  $('document').ready(function(){
13740        var list = $('.article-category-quick-links-container ul');
13741        if(list.length > 0)
13742        {
13743            $('.article-category-quick-links-container ul').clone().appendTo('.categories-dropdown')
13744        }
13745  });
13746
13747  $('document').on('resize', function(){
13748        $('.categories-dropdown').hide();
13749        $('.categories-dropdown--button').removeClass('categories-dropdown--button-on');
13750  });
13751});
13752
13753(function() {
13754  var SCREEN_MD = 992,
13755    prevMd,
13756    articleCarouselSwiper = undefined;
13757
13758  //custom function
13759  function initArticleCarouselSwiper() {
13760    var md = window.matchMedia('(min-width: ' + SCREEN_MD + 'px)').matches;
13761    if (md != prevMd || !prevMd) {
13762      if (md && articleCarouselSwiper != undefined) {
13763        // destroy the swiper on desktop (992px and up)
13764        $('.article-carousel').each(function() {
13765          articleCarouselSwiper.destroy(true, true);
13766          articleCarouselSwiper = undefined;
13767        });
13768      } else if (!md && articleCarouselSwiper == undefined) {
13769        // initialize the swiper on mobile
13770        $('.article-carousel').each(function() {
13771          var $this = $(this);
13772          articleCarouselSwiper = new Swiper($this, {
13773            speed: 400,
13774            spaceBetween: 20,
13775            slidesPerView: 3,
13776            pagination: {
13777              el: $this.children('.swiper-pagination'),
13778              type: 'bullets',
13779              clickable: true
13780            },
13781            breakpoints: {
13782              // when window width is <= 991px
13783              991: {
13784                slidesPerView: 1
13785              }
13786            }
13787          });
13788        });
13789      }
13790      prevMd = md;
13791    }
13792  }
13793
13794  // initiate slider
13795  $(window).on('load', function() {
13796    initArticleCarouselSwiper();
13797  });
13798
13799  $(window).on('resize', function() {
13800    initArticleCarouselSwiper();
13801  });
13802})();
13803
13804
13805$(function () {
13806  var components = $('.articleGrid');
13807  $('.article--grid-filters select')
13808    .selectBoxIt()
13809    .change(function () {
13810      var currentCount = $('#article-grid-data .articleblock').length;
13811      var sortLimit = Math.max(currentCount, limit);
13812      articleStart = 0;
13813      $('#article-grid-data').empty();
13814      loadMore(sortLimit);
13815    });
13816
13817  var articleStart = 0;
13818  var articleMax = 0;
13819  var limit = $('.article--grid-container').data('maxarticles');
13820  var lazyload = $('.article--grid-container').data('lazyload');
13821  var alt = $('.article--grid-data').hasClass('alternate');
13822  var gridPath;
13823  var gridTags;
13824  var enableDescription = $('.articlegrid-short-description');
13825  var showAuthor = $('.article--grid-container').attr('data-show-author') === 'true';
13826
13827  var SCREEN_MD = 992,
13828    prevMd,
13829    imgSelector = '.articleblock--image-container img';
13830
13831  if (components.length) {
13832    gridPath = encodeURIComponent(
13833      $(this).find('#article-grid-data').data('path')
13834    );
13835    gridTags = encodeURIComponent(
13836      $(this).find('#article-grid-data').data('tags')
13837    );
13838
13839    $('#loadMore').show();
13840    //Get all the articles
13841    loadMore();
13842  }
13843
13844  function loadMore(overrideLimit) {
13845    var currentLimit = overrideLimit || limit;
13846    var xhttp = new XMLHttpRequest();
13847    xhttp.onreadystatechange = function () {
13848      if (this.readyState == 4 && this.status == 200) {
13849        renderHTML(this.responseText);
13850      }
13851    };
13852
13853    var filter = encodeURIComponent($('.article--grid-filters select').val());
13854
13855    xhttp.open(
13856      'GET',
13857      '/bin/southern/global/latestarticles?tags=' +
13858        gridTags +
13859        '&path=' +
13860        gridPath +
13861        '&start=' +
13862        articleStart +
13863        '&limit=' +
13864        currentLimit +
13865        '&filter=' +
13866        filter,
13867      true
13868    );
13869    xhttp.send();
13870  }
13871
13872  function renderHTML(articles) {
13873    var articlesArr = JSON.parse(articles);
13874    articleMax = articlesArr.size;
13875    articleStart += articlesArr.pages.length;
13876    if (articleStart >= articleMax) {
13877      $('#loadMore').hide();
13878    } else {
13879      $('#loadMore').show();
13880    }
13881    jQuery.each(articlesArr.pages, function (index, obj) {
13882      var redirect = obj.customRedirect ? obj.customRedirect : obj.path;
13883      console.log(lazyload);
13884      var lazystring = lazyload == 'disable' ? '' : ' loading="lazy" ' ;
13885      var shortDescription = enableDescription.length == 1 ? '<a class="articleblock--content--description" href="' + redirect + '"><p>' + obj.description + '</p></a>' : '';
13886      var authorHTML = '';
13887      if (showAuthor && obj.author) {
13888        authorHTML = obj.authorLink
13889          ? '<div class="articleblock--content--author"><a class="articleblock--content--author-link" href="' + obj.authorLink + '" aria-label="' + obj.author + '">' + obj.author + '</a></div>'
13890          : '<div class="articleblock--content--author">' + obj.author + '</div>';
13891      }
13892      var tagClasses = obj.tagClasses ? obj.tagClasses : '';
13893      var imageAlt = escapeHtmlAttr(obj.articlePromoImageAlt);
13894      var articlesHTML =
13895        '<div class="articleblock ' + tagClasses + '"><div class="articleblock--image-container"> <a href="' +
13896        redirect + 
13897        '"> <img  class="articleblock--image"' + lazystring + 'src="' +
13898        obj.articlePromoImage +
13899        '" alt="' +
13900        imageAlt +
13901        '" /></div></a><div class="articleblock--content"><p class="articleblock--content--tag">' +
13902        obj.primaryTag +
13903        '</p><a class="articleblock--content--title" href="' +
13904        redirect +
13905        '">' +
13906        obj.title +
13907        '</a>' +
13908        shortDescription +
13909        authorHTML +
13910        '</div></div>';
13911      $('#article-grid-data').append(articlesHTML);
13912    });
13913    setTitleEllipsis();
13914  }
13915
13916  function escapeHtmlAttr(value) {
13917    if (value == null || value === '') {
13918      return '';
13919    }
13920    return String(value)
13921      .replace(/&/g, '&amp;')
13922      .replace(/"/g, '&quot;')
13923      .replace(/'/g, '&#39;')
13924      .replace(/</g, '&lt;')
13925      .replace(/>/g, '&gt;');
13926  }
13927
13928  function setTitleEllipsis() {
13929    $('.ellip').contents().unwrap();
13930    $('.ellip-line').contents().unwrap();
13931    if (window.innerWidth > 992) {
13932      if (alt) {
13933        $('.articleblock--content--title').ellipsis({ lines: 2 });
13934      } else {
13935        $('.articleblock--content--title').ellipsis({ lines: 4 });
13936      }
13937    }
13938  }
13939
13940  $(window).on('resize', function () {
13941    setTitleEllipsis();
13942  });
13943
13944  $('#loadMore').click(function () {
13945    loadMore();
13946  });
13947});
13948
13949
13950
13951document.addEventListener('DOMContentLoaded', function() {
13952    // Collect all image series containers
13953    const containers = document.querySelectorAll('.animation-container.image-series');
13954    
13955    // Store all instances for coordinated refresh
13956    const instances = [];
13957    
13958    // Initialize all instances first, then do a single global refresh
13959    containers.forEach(function(container, index) {
13960        const instanceId = container.id; // Uses model.instanceId from HTML template
13961        
13962        let imagePathsJson = container.getAttribute('data-image-series-paths');
13963        
13964        if (!imagePathsJson) {
13965            console.warn('No image series paths found for container:', instanceId);
13966            return;
13967        }
13968        
13969        let imagePaths = [];
13970        try {
13971            imagePaths = JSON.parse(imagePathsJson);
13972        } catch (e) {
13973            console.error('Failed to parse image series paths for container:', instanceId, e);
13974            return;
13975        }
13976        
13977        if (imagePaths.length === 0) {
13978            console.warn('No images found in image series folder for container:', instanceId);
13979            return;
13980        }
13981        
13982        // Find canvas element
13983        const canvas = container.querySelector('canvas');
13984        if (!canvas) {
13985            console.warn('Canvas element not found in container:', instanceId);
13986            return;
13987        }
13988        
13989        // Check if content container scroll is enabled
13990        const useContentContainerScroll = container.getAttribute('data-use-content-container-scroll') === 'true';
13991        
13992        // Find content container
13993        const contentContainer = container.querySelector('.content-container');
13994        
13995        // For content-container scroll mode, we need the content container and its scrollable content
13996        let scrollableContent = null;
13997        if (useContentContainerScroll) {
13998            if (!contentContainer) {
13999                console.warn('Content container not found in:', instanceId);
14000                return;
14001            }
14002            scrollableContent = contentContainer.firstElementChild;
14003            if (!scrollableContent) {
14004                console.warn('No scrollable content found in content-container:', instanceId);
14005                return;
14006            }
14007        }
14008        
14009        // Parse overlay items data (will apply to content-container background)
14010        const overlayData = parseOverlayData(container, instanceId);
14011        
14012        // Set canvas to match container's actual rendered size
14013        const setCanvasSize = function() {
14014            const containerStyle = window.getComputedStyle(container);
14015            const containerRect = container.getBoundingClientRect();
14016            
14017            let targetWidth = containerRect.width;
14018            let targetHeight = containerRect.height;
14019            
14020            if (contentContainer) {
14021                const contentRect = contentContainer.getBoundingClientRect();
14022                
14023                const widthIsFitContent = containerStyle.width === 'fit-content' || 
14024                                         containerStyle.width === 'max-content' ||
14025                                         containerStyle.width === 'min-content';
14026                if (widthIsFitContent && contentRect.width > 0) {
14027                    targetWidth = contentRect.width;
14028                }
14029                
14030                const heightIsFitContent = containerStyle.height === 'fit-content' || 
14031                                          containerStyle.height === 'max-content' ||
14032                                          containerStyle.height === 'min-content';
14033                if (heightIsFitContent && contentRect.height > 0) {
14034                    targetHeight = contentRect.height;
14035                }
14036            }
14037            
14038            canvas.width = targetWidth;
14039            canvas.height = targetHeight;
14040        };
14041        
14042        setCanvasSize();
14043        
14044        // Build ScrollTrigger config based on scroll mode
14045        const scrollTriggerConfig = {
14046            id: 'imageSeq-' + instanceId,
14047            scrub: true,
14048            refreshPriority: -index
14049        };
14050        
14051        if (useContentContainerScroll) {
14052            // Content container scroll mode: track scrolling within the content-container
14053            scrollTriggerConfig.scroller = contentContainer;
14054            scrollTriggerConfig.trigger = scrollableContent;
14055            scrollTriggerConfig.start = "top center";    // Frame 0 when content top reaches container center
14056            scrollTriggerConfig.end = "bottom center";   // Last frame when content bottom reaches container center
14057        } else {
14058            // Page scroll mode: track page scrolling relative to the animation container
14059            scrollTriggerConfig.trigger = container;
14060            scrollTriggerConfig.start = "top center";    // Frame 0 when container top reaches viewport center
14061            scrollTriggerConfig.end = "bottom center";   // Last frame when container bottom reaches viewport center
14062        }
14063        
14064        // Initialize image sequence animation with unique ScrollTrigger ID
14065        const animationData = imageSequence({
14066            urls: imagePaths,
14067            canvas: canvas,
14068            overlayData: overlayData,
14069            contentContainer: contentContainer,
14070            scrollTrigger: scrollTriggerConfig
14071        });
14072        
14073        const animation = animationData.animation;
14074        const forceUpdate = animationData.update;
14075        
14076        // Check if pin animation is enabled via data attribute
14077        const pinAnimationEnabled = container.getAttribute('data-pin-animation-enabled') === 'true';
14078        let pinAnimation = null;
14079        
14080        // Add parallax pin effect with unique ID and anticipatePin
14081        if (pinAnimationEnabled) {
14082            pinAnimation = gsap.to(canvas, {
14083                scrollTrigger: {
14084                    id: 'pin-' + instanceId,
14085                    trigger: container,
14086                    start: "top top",
14087                    end: "bottom top",
14088                    pin: canvas,
14089                    pinSpacing: false,
14090                    anticipatePin: 1,
14091                    refreshPriority: -index // Higher on page = higher priority
14092                }
14093            });
14094        }
14095        
14096        // Store instance data for coordinated refresh
14097        instances.push({
14098            instanceId: instanceId,
14099            container: container,
14100            canvas: canvas,
14101            contentContainer: contentContainer,
14102            animation: animation,
14103            pinAnimation: pinAnimation,
14104            forceUpdate: forceUpdate,
14105            forceRedraw: animationData.forceRedraw,
14106            setCanvasSize: setCanvasSize,
14107            overlayData: overlayData
14108        });
14109    });
14110    
14111    // Single global refresh after ALL instances are initialized
14112    if (instances.length > 0) {
14113        setTimeout(function() {
14114            ScrollTrigger.refresh();
14115            instances.forEach(function(inst) {
14116                if (inst.forceRedraw) {
14117                    inst.forceRedraw();
14118                }
14119            });
14120        }, 100);
14121    }
14122    
14123    // Single global resize handler for all instances
14124    let resizeTimeout;
14125    window.addEventListener('resize', function() {
14126        clearTimeout(resizeTimeout);
14127        resizeTimeout = setTimeout(function() {
14128            // Update all canvas sizes
14129            instances.forEach(function(inst) {
14130                inst.setCanvasSize();
14131            });
14132            
14133            // Single coordinated refresh for all ScrollTriggers
14134            ScrollTrigger.refresh();
14135            
14136            // Force redraw at current position for all instances
14137            instances.forEach(function(inst) {
14138                if (inst.forceRedraw) {
14139                    inst.forceRedraw();
14140                }
14141            });
14142        }, 250);
14143    });
14144
14145    /**
14146     * Parse overlay items from data attribute
14147     * @param {HTMLElement} container - The animation container element
14148     * @param {string} instanceId - Unique instance ID for this container
14149     * @returns {Array} Array of overlay data objects with frame ranges and colors
14150     */
14151    function parseOverlayData(container, instanceId) {
14152        const overlayItemsJson = container.getAttribute('data-overlay-items');
14153        const overlayData = [];
14154        
14155        if (!overlayItemsJson) {
14156            return overlayData;
14157        }
14158        
14159        let overlayItems = [];
14160        try {
14161            overlayItems = JSON.parse(overlayItemsJson);
14162        } catch (e) {
14163            console.error('Failed to parse overlay items for container:', instanceId, e);
14164            return overlayData;
14165        }
14166        
14167        if (overlayItems.length === 0) {
14168            return overlayData;
14169        }
14170        
14171        overlayItems.forEach(function(item, i) {
14172            overlayData.push({
14173                beginImageNumber: item.beginImageNumber !== null ? parseInt(item.beginImageNumber, 10) : null,
14174                endImageNumber: item.endImageNumber !== null ? parseInt(item.endImageNumber, 10) : null,
14175                backgroundColor: item.backgroundColor || 'transparent'
14176            });
14177        });
14178        
14179        return overlayData;
14180    }
14181
14182    /**
14183     * Update content-container background based on current frame
14184     * @param {number} currentFrame - The current frame number
14185     * @param {Array} overlayData - Array of overlay data objects with frame ranges and colors
14186     * @param {HTMLElement} contentContainer - The content-container element to apply background to
14187     */
14188    function updateOverlays(currentFrame, overlayData, contentContainer) {
14189        if (!contentContainer || !overlayData || overlayData.length === 0) {
14190            return;
14191        }
14192        
14193        // Find the active overlay for the current frame
14194        let activeBackground = '';
14195        
14196        for (let i = 0; i < overlayData.length; i++) {
14197            const overlay = overlayData[i];
14198            const begin = overlay.beginImageNumber;
14199            const end = overlay.endImageNumber;
14200            
14201            // Skip if no begin number defined
14202            if (begin === null || begin === undefined) {
14203                continue;
14204            }
14205            
14206            let isActive = false;
14207            
14208            if (end !== null && end !== undefined) {
14209                // Range mode: active when frame is between begin and end (inclusive)
14210                isActive = currentFrame >= begin && currentFrame <= end;
14211            } else {
14212                // Single image mode: active only on exact frame
14213                isActive = currentFrame === begin;
14214            }
14215            
14216            if (isActive) {
14217                activeBackground = overlay.backgroundColor;
14218                break; // Use first matching overlay
14219            }
14220        }
14221        
14222        // Apply background to content-container (CSS transition handles fading)
14223        contentContainer.style.background = activeBackground;
14224    }
14225
14226    /**
14227     * Helper function that handles scrubbing through a sequence of images, 
14228     * drawing the appropriate one to the provided canvas.
14229     * 
14230     * Config object properties:
14231     * - urls [Array]: an Array of image URLs
14232     * - canvas [Canvas]: the <canvas> object to draw to
14233     * - scrollTrigger [Object]: an optional ScrollTrigger configuration object
14234     * - clear [Boolean]: if true, it'll clear out the canvas before drawing each frame
14235     * - paused [Boolean]: true if you'd like the returned animation to be paused initially
14236     * - fps [Number]: optional frames per second. Defaults to 30fps.
14237     * - onUpdate [Function]: optional callback for when the Tween updates
14238     * - overlayData [Array]: optional array of overlay data for content-container background
14239     * - contentContainer [HTMLElement]: the content-container element for background overlays
14240     * 
14241     * Returns an object with animation and update function
14242     */
14243    function imageSequence(config) {
14244        const playhead = {frame: 0};
14245        const canvas = config.canvas;
14246        const overlayData = config.overlayData || [];
14247        const contentContainer = config.contentContainer;
14248        
14249        if (!canvas) {
14250            console.warn("canvas not defined");
14251            return { animation: null, update: null, forceRedraw: null };
14252        }
14253        
14254        const ctx = canvas.getContext("2d");
14255        let curFrame = -1;
14256        const onUpdate = config.onUpdate;
14257        const totalFrames = config.urls.length;
14258        
14259        const images = new Array(totalFrames);
14260        for (let i = 0; i < totalFrames; i++) {
14261            images[i] = new Image();
14262        }
14263        
14264        const updateImage = function() {
14265            const frame = Math.round(playhead.frame);
14266            if (frame !== curFrame) {
14267                if (config.clear) {
14268                    ctx.clearRect(0, 0, canvas.width, canvas.height);
14269                }
14270                
14271                const img = images[frame];
14272                if (img && img.complete && img.naturalWidth > 0) {
14273                    const scale = Math.max(
14274                        canvas.width / img.width,
14275                        canvas.height / img.height
14276                    );
14277                    const x = (canvas.width / 2) - (img.width / 2) * scale;
14278                    const y = (canvas.height / 2) - (img.height / 2) * scale;
14279                    ctx.drawImage(img, x, y, img.width * scale, img.height * scale);
14280                }
14281                
14282                curFrame = frame;
14283                
14284                updateOverlays(frame, overlayData, contentContainer);
14285                
14286                if (onUpdate) {
14287                    onUpdate.call(this, frame, images[frame]);
14288                }
14289            }
14290        };
14291        
14292        const forceRedraw = function() {
14293            curFrame = -1;
14294            updateImage();
14295        };
14296        
14297        // --- Viewport-aware priority image loader ---
14298        var loadGeneration = 0;
14299        var loadedCount = 0;
14300        var BATCH_SIZE = 4;
14301        
14302        function buildSpiralOrder(center, total) {
14303            const order = [center];
14304            var offset = 1;
14305            while (order.length < total) {
14306                if (center + offset < total) order.push(center + offset);
14307                if (center - offset >= 0) order.push(center - offset);
14308                offset++;
14309            }
14310            return order;
14311        }
14312        
14313        function loadImagesFromScrollPosition(startFrame) {
14314            var generation = ++loadGeneration;
14315            var order = buildSpiralOrder(startFrame, totalFrames);
14316            var queueIndex = 0;
14317            
14318            function loadNextBatch() {
14319                if (generation !== loadGeneration) return;
14320                
14321                var loading = 0;
14322                while (queueIndex < order.length && loading < BATCH_SIZE) {
14323                    var frameIdx = order[queueIndex];
14324                    queueIndex++;
14325                    
14326                    if (images[frameIdx].src) continue;
14327                    
14328                    (function(idx) {
14329                        images[idx].onload = function() {
14330                            loadedCount++;
14331                            if (idx === Math.round(playhead.frame)) {
14332                                forceRedraw();
14333                            }
14334                            if (generation === loadGeneration) {
14335                                loadNextBatch();
14336                            }
14337                        };
14338                        images[idx].onerror = function() {
14339                            console.warn('Failed to load image at index', idx, config.urls[idx]);
14340                            if (generation === loadGeneration) {
14341                                loadNextBatch();
14342                            }
14343                        };
14344                        images[idx].src = config.urls[idx];
14345                    })(frameIdx);
14346                    
14347                    loading++;
14348                }
14349            }
14350            
14351            loadNextBatch();
14352        }
14353        
14354        // Create GSAP animation with ScrollTrigger
14355        const animation = gsap.to(playhead, {
14356            frame: totalFrames - 1,
14357            ease: "none",
14358            onUpdate: updateImage,
14359            duration: totalFrames / (config.fps || 30),
14360            paused: !!config.paused,
14361            scrollTrigger: config.scrollTrigger
14362        });
14363        
14364        // Determine initial frame from current scroll position, then start loading
14365        var scrollTriggerInstance = animation.scrollTrigger;
14366        var initialProgress = scrollTriggerInstance ? scrollTriggerInstance.progress : 0;
14367        var initialFrame = Math.round(initialProgress * (totalFrames - 1));
14368        loadImagesFromScrollPosition(initialFrame);
14369        
14370        // Re-prioritize loading when user scrolls to unloaded frames (throttled)
14371        var reprioritizeTimeout;
14372        if (scrollTriggerInstance) {
14373            scrollTriggerInstance.vars.onUpdate = function() {
14374                updateImage();
14375                
14376                if (loadedCount >= totalFrames) return;
14377                
14378                clearTimeout(reprioritizeTimeout);
14379                reprioritizeTimeout = setTimeout(function() {
14380                    var currentFrame = Math.round(playhead.frame);
14381                    if (!images[currentFrame].src || !images[currentFrame].complete) {
14382                        loadImagesFromScrollPosition(currentFrame);
14383                    }
14384                }, 150);
14385            };
14386        }
14387        
14388        return {
14389            animation: animation,
14390            update: updateImage,
14391            forceRedraw: forceRedraw
14392        };
14393    }
14394
14395});
14396
14397(function () {
14398    "use strict";
14399
14400    var SELECTOR = '[data-cmp-is="animatedcounter"]';
14401    var NUMBER_SELECTOR = ".cmp-animated-counter__number";
14402    var counters = [];
14403
14404    var initCounter = function (el) {
14405        var numberEl = el.querySelector(NUMBER_SELECTOR);
14406        if (!numberEl) return;
14407
14408        var rawValue = el.dataset.animatedValue || "0";
14409        var endValue = parseFloat(rawValue) || 0;
14410        if (endValue === 0) return;
14411
14412        var enableCommas = el.getAttribute("data-enable-commas") === "true";
14413        var dotIndex = rawValue.indexOf(".");
14414        var decimalPlaces = dotIndex >= 0 ? rawValue.length - dotIndex - 1 : 0;
14415
14416        var format = enableCommas ? "(,ddd)" : "(ddd)";
14417        if (decimalPlaces > 0) {
14418            var decimals = "";
14419            for (var d = 0; d < decimalPlaces; d++) { decimals += "d"; }
14420            format += "." + decimals;
14421        }
14422
14423        var od = new Odometer({
14424            el: numberEl,
14425            value: 0,
14426            format: format
14427        });
14428
14429        counters.push({
14430            container: el,
14431            odometer: od,
14432            target: endValue,
14433            animated: false
14434        });
14435    };
14436
14437    var isInViewport = function (el) {
14438        var rect = el.getBoundingClientRect();
14439        var threshold = window.innerWidth < 768 ? 1150 : 900;
14440        return rect.top <= threshold && rect.top >= -700;
14441    };
14442
14443    var handleScroll = function () {
14444        for (var i = 0; i < counters.length; i++) {
14445            if (counters[i].animated) continue;
14446
14447            if (isInViewport(counters[i].container)) {
14448                counters[i].odometer.update(counters[i].target);
14449                counters[i].animated = true;
14450            }
14451        }
14452    };
14453
14454    var handleDOMReady = function () {
14455        var elements = document.querySelectorAll(SELECTOR);
14456        elements.forEach(initCounter);
14457
14458        if (counters.length > 0) {
14459            window.addEventListener("scroll", handleScroll);
14460            handleScroll();
14461        }
14462    };
14463
14464    if (document.readyState === "loading") {
14465        document.addEventListener("DOMContentLoaded", handleDOMReady);
14466    } else {
14467        handleDOMReady();
14468    }
14469})();
14470
14471(function() {
14472    "use strict";
14473
14474    // Capture and temporarily strip the URL hash to prevent the browser's native
14475    // scroll-to-hash behavior, which causes erratic scrolling in Firefox/Safari
14476    // as layout shifts during page load. The hash is restored after the custom
14477    // GSAP scroll completes, or immediately if it doesn't match an anchor-component.
14478    var initialHash = window.location.hash;
14479    if (initialHash) {
14480        try {
14481            history.replaceState(null, null,
14482                window.location.pathname + window.location.search);
14483        } catch(e) {
14484            initialHash = null;
14485        }
14486    }
14487
14488    // Tracks whether the current scroll was triggered by the initial page-load hash,
14489    // so performScroll can use replaceState (no extra history entry) instead of pushState.
14490    var isInitialScrollInProgress = false;
14491
14492    /**
14493     * Performs the actual scroll animation to an element
14494     * @param {Element} anchorElement - The anchor element to scroll to
14495     * @param {string} hash - The hash to push to URL after scrolling
14496     * @param {number} scrollAdjustment - Pixel adjustment for scroll position
14497     */
14498    var performScroll = function(anchorElement, hash, scrollAdjustment) {
14499        var scrollPosition = anchorElement.getBoundingClientRect().top + window.pageYOffset + scrollAdjustment - 150;
14500        
14501        var currentPosition = window.pageYOffset;
14502        var distance = Math.abs(scrollPosition - currentPosition);
14503        var pixelsPerSecond = 1200;
14504        var duration = distance / pixelsPerSecond;
14505        duration = Math.max(0.3, Math.min(1.5, duration));
14506        
14507        var useReplaceState = isInitialScrollInProgress;
14508        
14509        gsap.to(window, {
14510            duration: duration,
14511            scrollTo: { y: scrollPosition },
14512            ease: "power2.out",
14513            onComplete: function() {
14514                if (hash && window.location.hash !== hash) {
14515                    if (useReplaceState) {
14516                        window.history.replaceState(null, null, hash);
14517                    } else {
14518                        window.history.pushState(null, null, hash);
14519                    }
14520                }
14521                isInitialScrollInProgress = false;
14522            }
14523        });
14524    };
14525
14526    /**
14527     * Handle accordion anchor type - open accordion if needed, then scroll
14528     * @param {Element} anchorElement - The anchor element inside an accordion
14529     * @param {string} hash - The hash to push to URL after scrolling
14530     * @param {number} scrollAdjustment - Pixel adjustment for scroll position
14531     */
14532    var handleAccordionAnchor = function(anchorElement, hash, scrollAdjustment) {
14533        var accordionContent = anchorElement.closest(".accordion__content");
14534        
14535        if (!accordionContent) {
14536            // Not inside an accordion, fall back to default scroll
14537            performScroll(anchorElement, hash, scrollAdjustment);
14538            return;
14539        }
14540        
14541        var accordionItem = accordionContent.closest(".accordion__item");
14542        var accordionTitle = accordionContent.previousElementSibling;
14543        
14544        // Check if accordion is already open
14545        if (accordionItem && accordionItem.classList.contains("is-active")) {
14546            // Already open, just scroll
14547            performScroll(anchorElement, hash, scrollAdjustment);
14548        } else if (accordionTitle) {
14549            // Trigger click to open accordion
14550            accordionTitle.click();
14551            
14552            // Wait for slideDown animation (400ms) + buffer, then scroll
14553            setTimeout(function() {
14554                performScroll(anchorElement, hash, scrollAdjustment);
14555            }, 450);
14556        }
14557    };
14558
14559    /**
14560     * Handle tab anchor type - activate tab if needed, then scroll
14561     * Follows AEM Core Components tabs v1 pattern
14562     * @param {Element} anchorElement - The anchor element inside a tab panel
14563     * @param {string} hash - The hash to push to URL after scrolling
14564     * @param {number} scrollAdjustment - Pixel adjustment for scroll position
14565     */
14566    var handleTabAnchor = function(anchorElement, hash, scrollAdjustment) {
14567        var tabPanel = anchorElement.closest("[data-cmp-hook-tabs='tabpanel']");
14568        
14569        if (!tabPanel) {
14570            // Not inside a tab panel, fall back to default scroll
14571            performScroll(anchorElement, hash, scrollAdjustment);
14572            return;
14573        }
14574        
14575        // Check if tab panel is already active
14576        if (tabPanel.classList.contains("cmp-tabs__tabpanel--active")) {
14577            // Already active, just scroll
14578            performScroll(anchorElement, hash, scrollAdjustment);
14579            return;
14580        }
14581        
14582        // Find the tabs container and determine the index of this panel
14583        // Note: Using .cmp-tabs class instead of [data-cmp-is='tabs'] because
14584        // the data attribute is removed after tabs component initialization
14585        var tabsContainer = tabPanel.closest(".cmp-tabs");
14586        if (!tabsContainer) {
14587            performScroll(anchorElement, hash, scrollAdjustment);
14588            return;
14589        }
14590        
14591        var tabPanels = tabsContainer.querySelectorAll("[data-cmp-hook-tabs='tabpanel']");
14592        var tabs = tabsContainer.querySelectorAll("[data-cmp-hook-tabs='tab']");
14593        var panelIndex = Array.prototype.indexOf.call(tabPanels, tabPanel);
14594        
14595        if (panelIndex > -1 && tabs[panelIndex]) {
14596            // Click the corresponding tab to activate it
14597            tabs[panelIndex].click();
14598            
14599            // Wait for tab switch and any native scroll to complete
14600            // Use longer delay + requestAnimationFrame to ensure layout is settled
14601            setTimeout(function() {
14602                requestAnimationFrame(function() {
14603                    performScroll(anchorElement, hash, scrollAdjustment);
14604                });
14605            }, 800);
14606        } else {
14607            performScroll(anchorElement, hash, scrollAdjustment);
14608        }
14609    };
14610
14611    /**
14612     * Handle carousel anchor type - navigate to carousel slide if needed, then scroll
14613     * Follows AEM Core Components carousel v1 pattern
14614     * @param {Element} anchorElement - The anchor element inside a carousel item
14615     * @param {string} hash - The hash to push to URL after scrolling
14616     * @param {number} scrollAdjustment - Pixel adjustment for scroll position
14617     */
14618    var handleCarouselAnchor = function(anchorElement, hash, scrollAdjustment) {
14619        // Try data attribute selector first, then fallback to class selector
14620        var carouselItem = anchorElement.closest("[data-cmp-hook-carousel='item']");
14621        
14622        if (!carouselItem) {
14623            // Fallback: try finding by class (some implementations may not have data attributes)
14624            carouselItem = anchorElement.closest(".cmp-carousel__item");
14625        }
14626        
14627        if (!carouselItem) {
14628            // Not inside a carousel item, fall back to default scroll
14629            performScroll(anchorElement, hash, scrollAdjustment);
14630            return;
14631        }
14632        
14633        // Check if carousel item is already active
14634        if (carouselItem.classList.contains("cmp-carousel__item--active")) {
14635            // Already active, just scroll
14636            performScroll(anchorElement, hash, scrollAdjustment);
14637            return;
14638        }
14639        
14640        // Find the carousel container
14641        var carouselContainer = carouselItem.closest(".cmp-carousel");
14642        
14643        if (!carouselContainer) {
14644            // Fallback: try data attribute selector
14645            carouselContainer = carouselItem.closest("[data-cmp-is='carousel']");
14646        }
14647        
14648        if (!carouselContainer) {
14649            performScroll(anchorElement, hash, scrollAdjustment);
14650            return;
14651        }
14652        
14653        // Get all carousel items - try data attribute first, then class fallback
14654        var carouselItems = carouselContainer.querySelectorAll("[data-cmp-hook-carousel='item']");
14655        if (!carouselItems.length) {
14656            carouselItems = carouselContainer.querySelectorAll(".cmp-carousel__item");
14657        }
14658        
14659        var itemIndex = Array.prototype.indexOf.call(carouselItems, carouselItem);
14660        
14661        // Get indicators - try data attribute first, then class fallback
14662        var indicators = carouselContainer.querySelectorAll("[data-cmp-hook-carousel='indicator']");
14663        if (!indicators.length) {
14664            indicators = carouselContainer.querySelectorAll(".cmp-carousel__indicator");
14665        }
14666        
14667        if (itemIndex > -1 && indicators[itemIndex]) {
14668            // Click the corresponding indicator to navigate to the slide
14669            indicators[itemIndex].click();
14670            
14671            // Wait for carousel transition and any native scroll to complete
14672            // Use longer delay + requestAnimationFrame to ensure layout is settled
14673            setTimeout(function() {
14674                requestAnimationFrame(function() {
14675                    performScroll(anchorElement, hash, scrollAdjustment);
14676                });
14677            }, 800);
14678        } else {
14679            // No indicators found or invalid index - try direct scroll anyway
14680            performScroll(anchorElement, hash, scrollAdjustment);
14681        }
14682    };
14683
14684    /**
14685     * Check if generalModalAPI is available
14686     * @returns {boolean}
14687     */
14688    var hasModalAPI = function() {
14689        return typeof window.generalModalAPI !== 'undefined';
14690    };
14691
14692    /**
14693     * Performs scroll animation within a modal's .background element
14694     * @param {Element} anchorElement - The anchor element to scroll to
14695     * @param {string} hash - The hash to push to URL after scrolling
14696     * @param {number} scrollAdjustment - Pixel adjustment for scroll position
14697     * @param {Element}
14697 modalBackground - The modal's .background element
14698     */
14699    var performModalScroll = function(anchorElement, hash, scrollAdjustment, modalBackground) {
14700        // Scroll the .background element directly
14701        var scrollableContent = modalBackground;
14702        
14703        var anchorRect = anchorElement.getBoundingClientRect();
14704        var containerRect = scrollableContent.getBoundingClientRect();
14705        var scrollPosition = anchorRect.top - containerRect.top + scrollableContent.scrollTop + scrollAdjustment;
14706        
14707        // Calculate duration based on scroll distance
14708        var currentPosition = scrollableContent.scrollTop;
14709        var distance = Math.abs(scrollPosition - currentPosition);
14710        var pixelsPerSecond = 1200;
14711        var duration = distance / pixelsPerSecond;
14712        duration = Math.max(0.3, Math.min(1.0, duration));
14713        
14714        var useReplaceState = isInitialScrollInProgress;
14715        
14716        gsap.to(scrollableContent, {
14717            duration: duration,
14718            scrollTo: { y: scrollPosition },
14719            ease: "power2.out",
14720            onComplete: function() {
14721                if (hash && window.location.hash !== hash) {
14722                    if (useReplaceState) {
14723                        window.history.replaceState(null, null, hash);
14724                    } else {
14725                        window.history.pushState(null, null, hash);
14726                    }
14727                }
14728                isInitialScrollInProgress = false;
14729            }
14730        });
14731    };
14732
14733    /**
14734     * Handle modal anchor type - open modal if needed, then scroll within it
14735     * Uses window.generalModalAPI for coordination with generalmodal.js
14736     * @param {Element} anchorElement - The anchor element inside a modal
14737     * @param {string} hash - The hash to push to URL after scrolling
14738     * @param {number} scrollAdjustment - Pixel adjustment for scroll position
14739     */
14740    var handleModalAnchor = function(anchorElement, hash, scrollAdjustment) {
14741        var modalId = anchorElement.dataset.modalId;
14742        
14743        if (!modalId) {
14744            // No modal ID, fall back to default scroll
14745            performScroll(anchorElement, hash, scrollAdjustment);
14746            return;
14747        }
14748        
14749        // Check if API is available
14750        if (!hasModalAPI()) {
14751            console.warn("generalModalAPI not available for modal anchor");
14752            return;
14753        }
14754        
14755        // Check if anchor is already in the currently open modal (just scroll, don't close/reopen)
14756        var currentModal = window.generalModalAPI.getCurrentModal();
14757        if (currentModal) {
14758            var currentModalId = currentModal.getAttribute('anchorId');
14759            if (currentModalId === modalId) {
14760                // Same modal - just scroll within it
14761                performModalScroll(anchorElement, hash, scrollAdjustment, currentModal);
14762                return;
14763            }
14764        }
14765        
14766        // Close any open modals first (preserve scroll lock since we're opening another)
14767        if (window.generalModalAPI.isOpen()) {
14768            window.generalModalAPI.closeModal({ preserveScrollLock: true });
14769        }
14770        
14771        // Open the target modal via API
14772        var modalBackground = window.generalModalAPI.openModal(modalId);
14773        if (!modalBackground) {
14774            console.warn("Modal not found:", modalId);
14775            return;
14776        }
14777        
14778        // Wait for modal to be visible, then scroll within modal content
14779        setTimeout(function() {
14780            requestAnimationFrame(function() {
14781                performModalScroll(anchorElement, hash, scrollAdjustment, modalBackground);
14782            });
14783        }, 100);
14784    };
14785
14786    /**
14787     * Routes to the appropriate scroll handler based on anchor type
14788     * @param {Element} anchorElement - The anchor element to scroll to
14789     * @param {string} hash - The hash to push to URL after scrolling
14790     */
14791    var scrollToAnchor = function(anchorElement, hash) {
14792        var anchorType = anchorElement.dataset.anchorType || "default";
14793        var scrollAdjustment = parseInt(anchorElement.dataset.scrollHeightAdjustment, 10) || 0;
14794        
14795        if (anchorType === "default") {
14796            performScroll(anchorElement, hash, scrollAdjustment);
14797        } else if (anchorType === "accordion") {
14798            handleAccordionAnchor(anchorElement, hash, scrollAdjustment);
14799        } else if (anchorType === "tab") {
14800            handleTabAnchor(anchorElement, hash, scrollAdjustment);
14801        } else if (anchorType === "carousel") {
14802            handleCarouselAnchor(anchorElement, hash, scrollAdjustment);
14803        } else if (anchorType === "modal") {
14804            handleModalAnchor(anchorElement, hash, scrollAdjustment);
14805        } else {
14806            console.log("Anchor type:", anchorType, "Element:", anchorElement);
14807        }
14808    };
14809
14810    /**
14811     * Check for stored hash on page load and scroll to anchor.
14812     * Uses the hash captured before it was stripped (to prevent native scroll),
14813     * and waits for window.load so layout is fully stable before scrolling.
14814     */
14815    var handleInitialHash = function() {
14816        var hash = initialHash;
14817        if (!hash || hash === "") return;
14818        
14819        var anchorElement = document.querySelector(".anchor-component" + hash);
14820        
14821        if (!anchorElement) {
14822            // Hash doesn't target an anchor-component — restore it so other scripts
14823            // (generalmodal.js, accordion.js, promocarousel.js) can handle it via
14824            // their existing hashchange listeners.
14825            try {
14826                history.replaceState(null, null, hash);
14827                window.dispatchEvent(new Event('hashchange'));
14828            } catch(e) {}
14829            return;
14830        }
14831        
14832        isInitialScrollInProgress = true;
14833        
14834        var executeScroll = function() {
14835            requestAnimationFrame(function() {
14836                scrollToAnchor(anchorElement, hash);
14837            });
14838        };
14839        
14840        if (document.readyState === 'complete') {
14841            executeScroll();
14842        } else {
14843            window.addEventListener('load', executeScroll);
14844        }
14845    };
14846
14847    /**
14848     * Handle click events on anchor links
14849     */
14850    var handleAnchorClick = function(event) {
14851        var link = event.target.closest("a");
14852        if (!link) return;
14853        
14854        var hash = link.hash;
14855        if (!hash || hash === "") return;
14856        
14857        var anchorElement = document.querySelector(".anchor-component" + hash);
14858        if (anchorElement) {
14859            event.preventDefault();
14860            scrollToAnchor(anchorElement, hash);
14861            return;
14862        }
14863
14864        if (hasModalAPI() && window.generalModalAPI.findModalByHash(hash)) {
14865            event.preventDefault();
14866            // pushState rather than assigning location.hash: the modal's
14867            // .modal-container carries id="anchorId", so a real fragment
14868            // navigation would make the browser scroll that target into view
14869            // and snap the page to the top.
14870            if (window.location.hash !== hash) {
14871                window.history.pushState(null, null, hash);
14872            }
14873            window.generalModalAPI.switchTo(hash);
14874            return;
14875        }
14876    };
14877
14878    // Initialize on DOMContentLoaded
14879    document.addEventListener("DOMContentLoaded", function() {
14880        document.addEventListener("click", handleAnchorClick);
14881        handleInitialHash();
14882    });
14883})();
14884
14885$(function () {
14886  $('.alert-banner__close.icon-x').on('click touchstart', function () {
14887    var ultimateParent = $(this).parent().parent().parent().parent().parent();
14888    createAlertCookie(ultimateParent.attr('data-modified-date'));
14889    if ($(".alert.aem-GridColumn").length > 0) {
14890      $(".alert.aem-GridColumn")[0].innerHTML = "";
14891    } else {
14892      $(".alert.section")[0].innerHTML = "";
14893    }
14894    
14895});
14896
14897  $(document).ready(function () {
14898    var count = 0;
14899    $('.alert-banner-background-image-wrapper').each(function () {
14900      var title = 'alert' + count;
14901      count++;
14902      if (alertCookieExists($(this).attr('data-modified-date'))) {
14903        if ($(".alert.aem-GridColumn").length > 0) {
14904          $(".alert.aem-GridColumn")[0].innerHTML = "";
14905        } else {
14906          $(".alert.section")[0].innerHTML = "";
14907        }
14908      }
14909    });
14910  });
14911});
14912
14913function createAlertCookie(cookieID) {
14914  //expiration set for session and identified and based on alert modified time
14915  document.cookie = cookieID + '=' + cookieID + '; expires=' + 0 + ';';
14916}
14917
14918function alertCookieExists(cookieID) {
14919  return document.cookie.indexOf(cookieID) > -1;
14920}
14921
14922$(function () {
14923  var videoIsLoaded = false;
14924  if ($('.adaptive-carousel').length > 0) {
14925    $('.adaptive-carousel.flexslider').each(function () {
14926      if ($(this).find('li.item').length >= 1) {
14927        var intervalInt = parseInt($(this).attr('data-interval')),
14928          interval = intervalInt ? intervalInt : 3000,
14929          autoplay = $(this).attr('data-autoplay') == 'false' ? false : true;
14930
14931        var parent = $(this).parents('[data-target="true"]');
14932        var that = this;
14933        if (parent.length > 0) {
14934          var targetInitialized = setInterval(
14935            function () {
14936                createSlides(interval, autoplay, that);
14937                clearInterval(targetInitialized);
14938            },
14939            1000,
14940            parent
14941          );
14942        } else {
14943          createSlides(interval, autoplay, this);
14944        }
14945      }
14946    });
14947
14948    $(".adaptive-carousel .item").each(function() {
14949      if ($(this).data("url")) {
14950        var copy = $(this).find(".copy");
14951        if (copy.length) {
14952          copy.wrap("<a href='" + $(this).data("url") + "' class='slide-item--anchor'></a>");
14953        }
14954      }
14955    });
14956
14957    $(window).on('resize load', function () {
14958      // when window loads or resizes, show all items and set their heights/margins
14959      setAdaptiveSlideHeights();
14960    });
14961
14962    $(document).ready(function () {
14963      var copy = $(this).find('.slides .item, .slides .copy');
14964      setTimeout(function () {
14965        copy.addClass('slide--animate');
14966      }, 1000);
14967      loadVideo();
14968      if ($('.slides.cta').length > 0) {
14969        $('.adh-slide--btn').removeClass('btn');
14970        $('.adh-slide--btn').addClass('cta');
14971      }
14972    });
14973  }
14974
14975  function createSlides(interval, autoplay, element) {
14976    $(element).flexslider({
14977      pauseOnHover: true,
14978      controlNav: true,
14979      directionNav: false,
14980      slideshow: autoplay,
14981      slideshowSpeed: interval,
14982
14983      start: function (slider) {
14984        // when slider initializes, show all items
14985        slider.find('.item').addClass('initialized-item');
14986        setAdaptiveSlideHeights();
14987      $(element).data('goToSlide', function(index) {
14988        slider.flexAnimate(index);
14989      });
14990    },
14991    });
14992  }
14993
14994  /**
14995   * Perform scroll animation using GSAP
14996   * @param {number} scrollPosition - Target scroll position
14997   */
14998  function performScroll(scrollPosition) {
14999    console.log('performScroll', scrollPosition);
15000    var currentPosition = window.pageYOffset;
15001    var distance = Math.abs(scrollPosition - currentPosition);
15002    var pixelsPerSecond = 1200;
15003    var duration = distance / pixelsPerSecond;
15004    duration = Math.max(0.3, Math.min(1.5, duration));
15005    var hash = window.location.hash;
15006    
15007    gsap.to(window, {
15008      duration: duration,
15009      scrollTo: { 
15010        y: scrollPosition,
15011        autoKill: false
15012      },
15013      ease: "power2.out",
15014      onComplete: function() {
15015        // Ensure hash is preserved after scroll completes
15016        if (hash && window.location.hash !== hash) {
15017          window.history.replaceState(null, null, hash);
15018        }
15019      }
15020    });
15021  }
15022
15023  /**
15024   * Handle hash-based navigation to slides
15025   * Navigates to a slide with matching anchor ID and scrolls to it
15026   * @param {string} hash - Optional hash to navigate to (defaults to window.location.hash)
15027   */
15028  function handleHashNavigation(hash) {
15029    hash = hash || window.location.hash;
15030    console.log('hash', hash);
15031    if (!hash || hash === "") return;
15032
15033    
15034    // Find slide with matching ID
15035    var targetSlide = document.querySelector('.adaptive-carousel .item' + hash);
15036    if (!targetSlide) return;
15037    
15038    // Find the parent carousel
15039    var carousel = targetSlide.closest('.adaptive-carousel.flexslider');
15040    if (!carousel) return;
15041    
15042    // Calculate scroll position
15043    var scrollAdjustment = parseInt(targetSlide.dataset.scrollAdjustment, 10) || 0;
15044    var scrollPosition = carousel.getBoundingClientRect().top + window.pageYOffset + scrollAdjustment;
15045    
15046    // Check if slide is already active (visible)
15047    var isAlreadyActive = targetSlide.classList.contains('flex-active-slide');
15048    console.log('isAlreadyActive', isAlreadyActive);
15049    
15050    if (isAlreadyActive) {
15051      // Already visible - scroll immediately
15052      performScroll(scrollPosition);
15053    } else {
15054      // Get slide index
15055    var slides = $(carousel).find('.slides > li.item');
15056    var slideIndex = slides.index(targetSlide);
15057
15058    if (slideIndex > -1) {
15059      // Call our custom method
15060      var goToSlide = $(carousel).data('goToSlide');
15061      if (goToSlide) {
15062        goToSlide(slideIndex);
15063      }
15064      
15065      setTimeout(function() {
15066        performScroll(scrollPosition);
15067      }, 400);
15068    }
15069  }
15070}
15071
15072  // Intercept clicks on hash links that match adaptive carousel slide IDs
15073  $(document).on('click', 'a[href^="#"]', function(e) {
15074    var href = $(this).attr('href');
15075    if (!href || href === '#') return;
15076    
15077    // Only intercept if hash matches an adaptive carousel slide ID
15078    var targetSlide = document.querySelector('.adaptive-carousel .item' + href);
15079    if (!targetSlide) return;
15080    
15081    e.preventDefault();
15082    window.history.pushState(null, null, href);
15083    handleHashNavigation(href);
15084  });
15085
15086  // Handle browser back/forward navigation
15087  window.addEventListener('hashchange', function() {
15088    handleHashNavigation();
15089  });
15090  
15091  // Handle initial page load with hash
15092  $(window).on('load', function() {
15093    setTimeout(function() {
15094      handleHashNavigation();
15095    }, 600);
15096  });
15097
15098  function loadVideo() {
15099    if ( !videoIsLoaded) {
15100      var sources = document.querySelectorAll('.slide--video source');
15101      // Define the video object this source is contained inside
15102      var video = document.querySelectorAll('.slide--video');
15103      for (var i = 0; i < sources.length; i++) {
15104        sources[i].setAttribute('src', sources[i].getAttribute('data-src'));
15105      }
15106      // If for some reason we do want to load the video after, for desktop as opposed to mobile (I'd imagine), use videojs API to load
15107      if (video != null) {
15108        for (var i = 0; i < video.length; i++) {
15109          video[i].load();
15110        }
15111      }
15112      videoIsLoaded = true;
15113    }
15114  }
15115
15116  function setAdaptiveSlideHeights() {
15117    $('.adaptive-carousel').removeAttr('style');
15118    if ($(window).outerWidth() >= 992) {
15119      //var objectFit = new ObjectFitPolyfill('.adaptive-carousel video');
15120
15121      // on desktop, remove inline styles that may have been set on mobile
15122      $('.adaptive-carousel.mobile-text-below .item').css(
15123        'margin-bottom',
15124        '0'
15125      );
15126      $('.adaptive-carousel .item, .adaptive-carousel .copy').css(
15127        'height',
15128        'auto'
15129      );
15130      // for each carousel, find the tallest item and update all item heights accordingly
15131      $('.adaptive-carousel').each(function () {
15132        var maxSlideHeight = 0;
15133        $(this)
15134          .find('.item')
15135          .each(function () {
15136            var slideHeight = $(this).outerHeight();
15137            if (maxSlideHeight < slideHeight) {
15138              maxSlideHeight = slideHeight;
15139            }
15140          });
15141        $(this);
15142        var copy = $(this).find('.slides .item, .slides .copy');
15143        copy.css('height', maxSlideHeight);
15144      });
15145    } else {
15146      // on mobile, let aspect ratio (padding-top) control image height
15147      $('.adaptive-carousel .item, .adaptive-carousel .copy').css(
15148        'height',
15149        'auto'
15150      );
15151      $('.adaptive-carousel.mobile-text-below .item').css('height', '0px');
15152
15153      $('.adaptive-carousel').each(function () {
15154        var maxCopyHeight = 0,
15155          maxSlideHeight = 0;
15156
15157        $(this)
15158          .find('.copy')
15159          .each(function () {
15160            var copyHeight = $(this).outerHeight();
15161            if (maxCopyHeight < copyHeight) {
15162              maxCopyHeight = copyHeight;
15163            }
15164          });
15165
15166        $(this)
15167          .find('.item')
15168          .each(function () {
15169            var slideHeight = $(this).outerHeight();
15170            if (maxSlideHeight < slideHeight) {
15171              maxSlideHeight = slideHeight;
15172            }
15173          });
15174        if ($(this).hasClass('mobile-text-below')) {
15175          // update all item margin-bottoms accordingly
15176          var ratio = parseInt(
15177            $(this).find('.slides .item').css('padding-top')
15178          );
15179          ratioHeight = ratio + 16 + 'px';
15180          $(this).find('.slides .item').css('margin-bottom', maxCopyHeight);
15181          $(this).find('.flex-control-nav').css('margin-top', ratioHeight);
15182        } else {
15183          // update all item heights accordingly
15184
15185          var copy = $(this).find('.slides .item, .slides .copy');
15186        }
15187      });
15188    }
15189  }
15190});
15191
15192$(function(){
15193
15194    // A General Modal authored inside accordion content matches the selectors
15195    // below, so without this guard opening the modal would also animate the page.
15196    function targetsGeneralModal() {
15197        return !!(window.generalModalAPI && window.generalModalAPI.findModalByHash(window.location.hash));
15198    }
15199
15200    $('document').ready(function(){
15201        if($(window.location.hash).closest('.accordion__content').length > 0)
15202        {
15203            if (document.querySelector('.anchor-component' + window.location.hash)) return;
15204            if (targetsGeneralModal()) return;
15205            $(window.location.hash).closest('.accordion__content').prev().trigger('click')
15206            $('html,body').animate({scrollTop: $(window.location.hash).closest('.accordion__content').prev().offset().top - 150},'slow');
15207        }
15208        $( window ).on( 'hashchange', function( e ) {
15209            if($(window.location.hash).closest('.accordion__content').length > 0)
15210            {
15211                if (document.querySelector('.anchor-component' + window.location.hash)) return;
15212                if (targetsGeneralModal()) return;
15213                $(window.location.hash).closest('.accordion__content').prev().trigger('click')
15214                $('html,body').animate({scrollTop: $(window.location.hash).closest('.accordion__content').prev().offset().top - 150},'slow');
15215            }
15216        } );
15217    });
15218	$('body').on('click touch', '.accordion__title', function(){
15219		var $this = $(this);
15220		var $item = $this.parent('.accordion__item');
15221
15222		if ( $item.hasClass('is-active') ) {
15223			$this.removeClass('is-active');
15224			$this.next('.accordion__content').slideUp(400, function(){
15225				$item.removeClass('is-active');
15226			});
15227		} else {
15228			$this.addClass('is-active');
15229            var $gallery = $this.parent().find('.gallery');
15230            $gallery.hide();
15231			$this.next().slideDown(400, function(){
15232				$item.addClass('is-active');
15233                $gallery.show();
15234				updateGallery($gallery);
15235
15236
15237			});
15238		}
15239
15240	});
15241	function updateGallery($gallery) {
15242
15243	    $gallery.each(function(){
15244                console.log("test")
15245                var galleryTemplate = $(this).data("galleryTemplate");
15246                var autoPlay = $(this).data("galleryAutoplay");
15247                var id = $(this).attr("id");
15248                console.log(id)
15249                var galleryEle = '#'+id+' .gallery-top';
15250                var thumbnailEle ='#'+id+' .gallery-thumbs';
15251                var paginationEle = '#'+id+ ' .swiper-pagination';
15252                var countSlides = $(galleryEle + ' .swiper-slide').length;
15253                var default_autoplay = 8000;
15254                var params = {
15255                    keyboardControl: true,
15256                    navigation: {
15257                        nextEl: '.gallery-button-next',
15258                        prevEl: '.gallery-button-prev',
15259                    },
15260                    loop: (countSlides >= 6),
15261                    loopedSlides: 7,
15262                    preloadImages: false,
15263                    autoHeight: true,
15264                    lazyLoading: true,
15265                    lazyLoadingInPrevNext: true,
15266                    lazyLoadingOnTransitionStart: true,
15267                    lazyLoadingInPrevNextAmount: 2,
15268                    pagination:{
15269                        el: paginationEle,
15270                        type: 'fraction'
15271                    },
15272                    a11y: true
15273                };
15274                if(autoPlay){
15275                    params.autoplay={
15276                        delay: default_autoplay,
15277                    };
15278                }
15279
15280                var thumbnailConfig={
15281                    spaceBetween: 0,
15282                    centeredSlides: true,
15283                    slidesPerView: (countSlides <= 6) ? 5 : 6,
15284                    touchRatio: 0.2,
15285                    slideToClickedSlide: true,
15286                    loop: (countSlides >= 6), // loop is false when fewer slides than slidesPerView
15287                    loopedSlides: 7
15288                };
15289
15290                // setup the gallery
15291                var galleryTop = new Swiper (galleryEle,params);
15292                if(galleryTemplate!=="nothumbnail"){
15293                   var galleryThumbs = new Swiper(thumbnailEle, thumbnailConfig);
15294                   galleryTop.thumbs = galleryThumbs;
15295                   galleryTop.controller.control =galleryThumbs;
15296                   galleryThumbs.controller.control = galleryTop;
15297
15298
15299                }
15300            });
15301
15302	}
15303
15304
15305});
15306

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.