PageSourceSearch

https://www.amtrak.com/etc.clientlibs/amtrakonebase/clientlibs/clientlib-base.js

js amtrak.com collected 2026-09-24 08:27:05 UTC 125,477 bytes, 3,289 lines download raw bytes

1/*******************************************************************************
2 * Copyright 2019 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 2019 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(function() {
63    "use strict";
64
65    var containerUtils = window.CQ && window.CQ.CoreComponents && window.CQ.CoreComponents.container && window.CQ.CoreComponents.container.utils ? window.CQ.CoreComponents.container.utils : undefined;
66    if (!containerUtils) {
67        // eslint-disable-next-line no-console
68        console.warn("Accordion: 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.");
69    }
70    var dataLayerEnabled;
71    var dataLayer;
72    var delay = 100;
73
74    var NS = "cmp";
75    var IS = "accordion";
76
77    var keyCodes = {
78        ENTER: 13,
79        SPACE: 32,
80        END: 35,
81        HOME: 36,
82        ARROW_LEFT: 37,
83        ARROW_UP: 38,
84        ARROW_RIGHT: 39,
85        ARROW_DOWN: 40
86    };
87
88    var selectors = {
89        self: "[data-" + NS + '-is="' + IS + '"]'
90    };
91
92    var cssClasses = {
93        button: {
94            disabled: "cmp-accordion__button--disabled",
95            expanded: "cmp-accordion__button--expanded"
96        },
97        panel: {
98            hidden: "cmp-accordion__panel--hidden",
99            expanded: "cmp-accordion__panel--expanded"
100        }
101    };
102
103    var dataAttributes = {
104        item: {
105            expanded: "data-cmp-expanded"
106        }
107    };
108
109    var properties = {
110        /**
111         * Determines whether a single accordion item is forced to be expanded at a time.
112         * Expanding one item will collapse all others.
113         *
114         * @memberof Accordion
115         * @type {Boolean}
116         * @default false
117         */
118        "singleExpansion": {
119            "default": false,
120            "transform": function(value) {
121                return !(value === null || typeof value === "undefined");
122            }
123        }
124    };
125
126    /**
127     * Accordion Configuration.
128     *
129     * @typedef {Object} AccordionConfig Represents an Accordion configuration
130     * @property {HTMLElement} element The HTMLElement representing the Accordion
131     * @property {Object} options The Accordion options
132     */
133
134    /**
135     * Accordion.
136     *
137     * @class Accordion
138     * @classdesc An interactive Accordion component for toggling panels of related content
139     * @param {AccordionConfig} config The Accordion configuration
140     */
141    function Accordion(config) {
142        var that = this;
143
144        if (config && config.element) {
145            init(config);
146        }
147
148        /**
149         * Initializes the Accordion.
150         *
151         * @private
152         * @param {AccordionConfig} config The Accordion configuration
153         */
154        function init(config) {
155            that._config = config;
156
157            // prevents multiple initialization
158            config.element.removeAttribute("data-" + NS + "-is");
159
160            setupProperties(config.options);
161            cacheElements(config.element);
162
163            if (that._elements["item"]) {
164                // ensures multiple element types are arrays.
165                that._elements["item"] = Array.isArray(that._elements["item"]) ? that._elements["item"] : [that._elements["item"]];
166                that._elements["button"] = Array.isArray(that._elements["button"]) ? that._elements["button"] : [that._elements["button"]];
167                that._elements["panel"] = Array.isArray(that._elements["panel"]) ? that._elements["panel"] : [that._elements["panel"]];
168
169                if (that._properties.singleExpansion) {
170                    var expandedItems = getExpandedItems();
171                    // multiple expanded items annotated, display the last item open.
172                    if (expandedItems.length > 1) {
173                        toggle(expandedItems.length - 1);
174                    }
175                }
176
177                refreshItems();
178                bindEvents();
179                scrollToDeepLinkIdInAccordion();
180            }
181            if (window.Granite && window.Granite.author && window.Granite.author.MessageChannel) {
182                /*
183                 * Editor message handling:
184                 * - subscribe to "cmp.panelcontainer" message requests sent by the editor frame
185                 * - check that the message data panel container type is correct and that the id (path) matches this specific Accordion component
186                 * - if so, route the "navigate" operation to enact a navigation of the Accordion based on index data
187                 */
188                window.CQ.CoreComponents.MESSAGE_CHANNEL = window.CQ.CoreComponents.MESSAGE_CHANNEL || new window.Granite.author.MessageChannel("cqauthor", window);
189                window.CQ.CoreComponents.MESSAGE_CHANNEL.subscribeRequestMessage("cmp.panelcontainer", function(message) {
190                    if (message.data && message.data.type === "cmp-accordion" && message.data.id === that._elements.self.dataset["cmpPanelcontainerId"]) {
191                        if (message.data.operation === "navigate") {
192                            // switch to single expansion mode when navigating in edit mode.
193                            var singleExpansion = that._properties.singleExpansion;
194                            that._properties.singleExpansion = true;
195                            toggle(message.data.index);
196
197                            // revert to the configured state.
198                            that._properties.singleExpansion = singleExpansion;
199                        }
200                    }
201                });
202            }
203        }
204
205        /**
206         * Displays the panel containing the element that corresponds to the deep link in the URI fragment
207         * and scrolls the browser to this element.
208         */
209        function scrollToDeepLinkIdInAccordion() {
210            if (containerUtils) {
211                var deepLinkItemIdx = containerUtils.getDeepLinkItemIdx(that, "item", "item");
212                if (deepLinkItemIdx > -1) {
213                    var deepLinkItem = that._elements["item"][deepLinkItemIdx];
214                    if (deepLinkItem && !deepLinkItem.hasAttribute(dataAttributes.item.expanded)) {
215                        // if single expansion: close all accordion items
216                        if (that._properties.singleExpansion) {
217                            for (var j = 0; j < that._elements["item"].length; j++) {
218                                if (that._elements["item"][j].hasAttribute(dataAttributes.item.expanded)) {
219                                    setItemExpanded(that._elements["item"][j], false, true);
220                                }
221                            }
222                        }
223                        // expand the accordion item containing the deep link
224                        setItemExpanded(deepLinkItem, true, true);
225                    }
226                    var hashId = window.location.hash.substring(1);
227                    if (hashId) {
228                        var hashItem = document.querySelector("[id='" + hashId + "']");
229                        if (hashItem) {
230                            hashItem.scrollIntoView();
231                        }
232                    }
233                }
234            }
235        }
236
237        /**
238         * Caches the Accordion elements as defined via the {@code data-accordion-hook="ELEMENT_NAME"} markup API.
239         *
240         * @private
241         * @param {HTMLElement} wrapper The Accordion wrapper element
242         */
243        function cacheElements(wrapper) {
244            that._elements = {};
245            that._elements.self = wrapper;
246            var hooks = that._elements.self.querySelectorAll("[data-" + NS + "-hook-" + IS + "]");
247
248            for (var i = 0; i < hooks.length; i++) {
249                var hook = hooks[i];
250                if (hook.closest("." + NS + "-" + IS) === that._elements.self) { // only process own accordion elements
251                    var capitalized = IS;
252                    capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
253                    var key = hook.dataset[NS + "Hook" + capitalized];
254                    if (that._elements[key]) {
255                        if (!Array.isArray(that._elements[key])) {
256                            var tmp = that._elements[key];
257                            that._elements[key] = [tmp];
258                        }
259                        that._elements[key].push(hook);
260                    } else {
261                        that._elements[key] = hook;
262                    }
263                }
264            }
265        }
266
267        /**
268         * Sets up properties for the Accordion based on the passed options.
269         *
270         * @private
271         * @param {Object} options The Accordion options
272         */
273        function setupProperties(options) {
274            that._properties = {};
275
276            for (var key in properties) {
277                if (Object.prototype.hasOwnProperty.call(properties, key)) {
278                    var property = properties[key];
279                    var value = null;
280
281                    if (options && options[key] != null) {
282                        value = options[key];
283
284                        // transform the provided option
285                        if (property && typeof property.transform === "function") {
286                            value = property.transform(value);
287                        }
288                    }
289
290                    if (value === null) {
291                        // value still null, take the property default
292                        value = properties[key]["default"];
293                    }
294
295                    that._properties[key] = value;
296                }
297            }
298        }
299
300        /**
301         * Binds Accordion event handling.
302         *
303         * @private
304         */
305        function bindEvents() {
306            window.addEventListener("hashchange", scrollToDeepLinkIdInAccordion, false);
307            var buttons = that._elements["button"];
308            if (buttons) {
309                for (var i = 0; i < buttons.length; i++) {
310                    (function(index) {
311                        buttons[i].addEventListener("click", function(event) {
312                            toggle(index);
313                            focusButton(index);
314                        });
315                        buttons[i].addEventListener("keydown", function(event) {
316                            onButtonKeyDown(event, index);
317                        });
318                    })(i);
319                }
320            }
321        }
322
323        /**
324         * Handles button keydown events.
325         *
326         * @private
327         * @param {Object} event The keydown event
328         * @param {Number} index The index of the button triggering the event
329         */
330        function onButtonKeyDown(event, index) {
331            var lastIndex = that._elements["button"].length - 1;
332
333            switch (event.keyCode) {
334                case keyCodes.ARROW_LEFT:
335                case keyCodes.ARROW_UP:
336                    event.preventDefault();
337                    if (index > 0) {
338                        focusButton(index - 1);
339                    }
340                    break;
341                case keyCodes.ARROW_RIGHT:
342                case keyCodes.ARROW_DOWN:
343                    event.preventDefault();
344                    if (index < lastIndex) {
345                        focusButton(index + 1);
346                    }
347                    break;
348                case keyCodes.HOME:
349                    event.preventDefault();
350                    focusButton(0);
351                    break;
352                case keyCodes.END:
353                    event.preventDefault();
354                    focusButton(lastIndex);
355                    break;
356                case keyCodes.ENTER:
357                case keyCodes.SPACE:
358                    event.preventDefault();
359                    toggle(index);
360                    focusButton(index);
361                    break;
362                default:
363                    return;
364            }
365        }
366
367        /**
368         * General handler for toggle of an item.
369         *
370         * @private
371         * @param {Number} index The index of the item to toggle
372         */
373        function toggle(index) {
374            var item = that._elements["item"][index];
375            if (item) {
376                if (that._properties.singleExpansion) {
377                    // ensure only a single item is expanded if single expansion is enabled.
378                    for (var i = 0; i < that._elements["item"].length; i++) {
379                        if (that._elements["item"][i] !== item) {
380                            var expanded = getItemExpanded(that._elements["item"][i]);
381                            if (expanded) {
382                                setItemExpanded(that._elements["item"][i], false);
383                            }
384                        }
385                    }
386                }
387                setItemExpanded(item, !getItemExpanded(item));
388
389                if (dataLayerEnabled) {
390                    var accordionId = that._elements.self.id;
391                    var expandedItems = getExpandedItems()
392                        .map(function(item) {
393                            return getDataLayerId(item);
394                        });
395
396                    var uploadPayload = { component: {} };
397                    uploadPayload.component[accordionId] = { shownItems: expandedItems };
398
399                    var removePayload = { component: {} };
400                    removePayload.component[accordionId] = { shownItems: undefined };
401
402                    dataLayer.push(removePayload);
403                    dataLayer.push(uploadPayload);
404                }
405            }
406        }
407
408        /**
409         * Sets an item's expanded state based on the provided flag and refreshes its internals.
410         *
411         * @private
412         * @param {HTMLElement} item The item to mark as expanded, or not expanded
413         * @param {Boolean} expanded true to mark the item expanded, false otherwise
414         * @param {Boolean} keepHash true to keep the hash in the URL, false to update it
415         */
416        function setItemExpanded(item, expanded, keepHash) {
417            if (expanded) {
418                item.setAttribute(dataAttributes.item.expanded, "");
419                var index = that._elements["item"].indexOf(item);
420                if (!keepHash && containerUtils) {
421                    containerUtils.updateUrlHash(that, "item", index);
422                }
423                if (dataLayerEnabled) {
424                    dataLayer.push({
425                        event: "cmp:show",
426                        eventInfo: {
427                            path: "component." + getDataLayerId(item)
428                        }
429                    });
430                }
431
432            } else {
433                item.removeAttribute(dataAttributes.item.expanded);
434                if (!keepHash && containerUtils) {
435                    containerUtils.removeUrlHash();
436                }
437                if (dataLayerEnabled) {
438                    dataLayer.push({
439                        event: "cmp:hide",
440                        eventInfo: {
441                            path: "component." + getDataLayerId(item)
442                        }
443                    });
444                }
445            }
446            refreshItem(item);
447        }
448
449        /**
450         * Gets an item's expanded state.
451         *
452         * @private
453         * @param {HTMLElement} item The item for checking its expanded state
454         * @returns {Boolean} true if the item is expanded, false otherwise
455         */
456        function getItemExpanded(item) {
457            return item && item.dataset && item.dataset["cmpExpanded"] !== undefined;
458        }
459
460        /**
461         * Refreshes an item based on its expanded state.
462         *
463         * @private
464         * @param {HTMLElement} item The item to refresh
465         */
466        function refreshItem(item) {
467            var expanded = getItemExpanded(item);
468            if (expanded) {
469                expandItem(item);
470            } else {
471                collapseItem(item);
472            }
473        }
474
475        /**
476         * Refreshes all items based on their expanded state.
477         *
478         * @private
479         */
480        function refreshItems() {
481            for (var i = 0; i < that._elements["item"].length; i++) {
482                refreshItem(that._elements["item"][i]);
483            }
484        }
485
486        /**
487         * Returns all expanded items.
488         *
489         * @private
490         * @returns {HTMLElement[]} The expanded items
491         */
492        function getExpandedItems() {
493            var expandedItems = [];
494
495            for (var i = 0; i < that._elements["item"].length; i++) {
496                var item = that._elements["item"][i];
497                var expanded = getItemExpanded(item);
498                if (expanded) {
499                    expandedItems.push(item);
500                }
501            }
502
503            return expandedItems;
504        }
505
506        /**
507         * Annotates the item and its internals with
508         * the necessary style and accessibility attributes to indicate it is expanded.
509         *
510         * @private
511         * @param {HTMLElement} item The item to annotate as expanded
512         */
513        function expandItem(item) {
514            var index = that._elements["item"].indexOf(item);
515            if (index > -1) {
516                var button = that._elements["button"][index];
517                var panel = that._elements["panel"][index];
518                button.classList.add(cssClasses.button.expanded);
519                // used to fix some known screen readers issues in reading the correct state of the 'aria-expanded' attribute
520                // e.g. https://bugs.webkit.org/show_bug.cgi?id=210934
521                setTimeout(function() {
522                    button.setAttribute("aria-expanded", true);
523                }, delay);
524                panel.classList.add(cssClasses.panel.expanded);
525                panel.classList.remove(cssClasses.panel.hidden);
526                panel.setAttribute("aria-hidden", false);
527            }
528        }
529
530        /**
531         * Annotates the item and its internals with
532         * the necessary style and accessibility attributes to indicate it is not expanded.
533         *
534         * @private
535         * @param {HTMLElement} item The item to annotate as not expanded
536         */
537        function collapseItem(item) {
538            var index = that._elements["item"].indexOf(item);
539            if (index > -1) {
540                var button = that._elements["button"][index];
541                var panel = that._elements["panel"][index];
542                button.classList.remove(cssClasses.button.expanded);
543                // used to fix some known screen readers issues in reading the correct state of the 'aria-expanded' attribute
544                // e.g. https://bugs.webkit.org/show_bug.cgi?id=210934
545                setTimeout(function() {
546                    button.setAttribute("aria-expanded", false);
547                }, delay);
548                panel.classList.add(cssClasses.panel.hidden);
549                panel.classList.remove(cssClasses.panel.expanded);
550                panel.setAttribute("aria-hidden", true);
551            }
552        }
553
554        /**
555         * Focuses the button at the provided index.
556         *
557         * @private
558         * @param {Number} index The index of the button to focus
559         */
560        function focusButton(index) {
561            var button = that._elements["button"][index];
562            button.focus();
563        }
564    }
565
566    /**
567     * Reads options data from the Accordion wrapper element, defined via {@code data-cmp-*} data attributes.
568     *
569     * @private
570     * @param {HTMLElement} element The Accordion element to read options data from
571     * @returns {Object} The options read from the component data attributes
572     */
573    function readData(element) {
574        var data = element.dataset;
575        var options = [];
576        var capitalized = IS;
577        capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
578        var reserved = ["is", "hook" + capitalized];
579
580        for (var key in data) {
581            if (Object.prototype.hasOwnProperty.call(data, key)) {
582                var value = data[key];
583
584                if (key.indexOf(NS) === 0) {
585                    key = key.slice(NS.length);
586                    key = key.charAt(0).toLowerCase() + key.substring(1);
587
588                    if (reserved.indexOf(key) === -1) {
589                        options[key] = value;
590                    }
591                }
592            }
593        }
594
595        return options;
596    }
597
598    /**
599     * Parses the dataLayer string and returns the ID
600     *
601     * @private
602     * @param {HTMLElement} item the accordion item
603     * @returns {String} dataLayerId or undefined
604     */
605    function getDataLayerId(item) {
606        if (item) {
607            if (item.dataset.cmpDataLayer) {
608                return Object.keys(JSON.parse(item.dataset.cmpDataLayer))[0];
609            } else {
610                return item.id;
611            }
612        }
613        return null;
614    }
615
616    /**
617     * Document ready handler and DOM mutation observers. Initializes Accordion components as necessary.
618     *
619     * @private
620     */
621    function onDocumentReady() {
622        dataLayerEnabled = document.body.hasAttribute("data-cmp-data-layer-enabled");
623        dataLayer = (dataLayerEnabled) ? window.adobeDataLayer = window.adobeDataLayer || [] : undefined;
624
625        var elements = document.querySelectorAll(selectors.self);
626        for (var i = 0; i < elements.length; i++) {
627            new Accordion({ element: elements[i], options: readData(elements[i]) });
628        }
629
630        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
631        var body = document.querySelector("body");
632        var observer = new MutationObserver(function(mutations) {
633            mutations.forEach(function(mutation) {
634                // needed for IE
635                var nodesArray = [].slice.call(mutation.addedNodes);
636                if (nodesArray.length > 0) {
637                    nodesArray.forEach(function(addedNode) {
638                        if (addedNode.querySelectorAll) {
639                            var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self));
640                            elementsArray.forEach(function(element) {
641                                new Accordion({ element: element, options: readData(element) });
642                            });
643                        }
644                    });
645                }
646            });
647        });
648
649        observer.observe(body, {
650            subtree: true,
651            childList: true,
652            characterData: true
653        });
654    }
655
656    if (document.readyState !== "loading") {
657        onDocumentReady();
658    } else {
659        document.addEventListener("DOMContentLoaded", onDocumentReady);
660    }
661
662    if (containerUtils) {
663        window.addEventListener("load", containerUtils.scrollToAnchor, false);
664    }
665
666}());
667
668/*******************************************************************************
669 * Copyright 2018 Adobe
670 *
671 * Licensed under the Apache License, Version 2.0 (the "License");
672 * you may not use this file except in compliance with the License.
673 * You may obtain a copy of the License at
674 *
675 *     http://www.apache.org/licenses/LICENSE2.0
676 *
677 * Unless required by applicable law or agreed to in writing, software
678 * distributed under the License is distributed on an "AS IS" BASIS,
679 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
680 * See the License for the specific language governing permissions and
681 * limitations under the License.
682 ******************************************************************************/
683
684/**
685 * Element.matches()
686 * https://developer.mozilla.org/enUS/docs/Web/API/Element/matches#Polyfill
687 */
688if (!Element.prototype.matches) {
689    Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
690}
691
692// eslint-disable-next-line valid-jsdoc
693/**
694 * Element.closest()
695 * https://developer.mozilla.org/enUS/docs/Web/API/Element/closest#Polyfill
696 */
697if (!Element.prototype.closest) {
698    Element.prototype.closest = function(s) {
699        "use strict";
700        var el = this;
701        if (!document.documentElement.contains(el)) {
702            return null;
703        }
704        do {
705            if (el.matches(s)) {
706                return el;
707            }
708            el = el.parentElement || el.parentNode;
709        } while (el !== null && el.nodeType === 1);
710        return null;
711    };
712}
713
714/*******************************************************************************
715 * Copyright 2018 Adobe
716 *
717 * Licensed under the Apache License, Version 2.0 (the "License");
718 * you may not use this file except in compliance with the License.
719 * You may obtain a copy of the License at
720 *
721 *     http://www.apache.org/licenses/LICENSE-2.0
722 *
723 * Unless required by applicable law or agreed to in writing, software
724 * distributed under the License is distributed on an "AS IS" BASIS,
725 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
726 * See the License for the specific language governing permissions and
727 * limitations under the License.
728 ******************************************************************************/
729/* global
730    CQ
731 */
732(function() {
733    "use strict";
734
735    var containerUtils = window.CQ && window.CQ.CoreComponents && window.CQ.CoreComponents.container && window.CQ.CoreComponents.container.utils ? window.CQ.CoreComponents.container.utils : undefined;
736    if (!containerUtils) {
737        // eslint-disable-next-line no-console
738        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.");
739    }
740    var dataLayerEnabled;
741    var dataLayer;
742
743    var NS = "cmp";
744    var IS = "tabs";
745
746    var keyCodes = {
747        END: 35,
748        HOME: 36,
749        ARROW_LEFT: 37,
750        ARROW_UP: 38,
751        ARROW_RIGHT: 39,
752        ARROW_DOWN: 40
753    };
754
755    var selectors = {
756        self: "[data-" + NS + '-is="' + IS + '"]',
757        active: {
758            tab: "cmp-tabs__tab--active",
759            tabpanel: "cmp-tabs__tabpanel--active"
760        }
761    };
762
763    /**
764     * Tabs Configuration
765     *
766     * @typedef {Object} TabsConfig Represents a Tabs configuration
767     * @property {HTMLElement} element The HTMLElement representing the Tabs
768     * @property {Object} options The Tabs options
769     */
770
771    /**
772     * Tabs
773     *
774     * @class Tabs
775     * @classdesc An interactive Tabs component for navigating a list of tabs
776     * @param {TabsConfig} config The Tabs configuration
777     */
778    function Tabs(config) {
779        var that = this;
780
781        if (config && config.element) {
782            init(config);
783        }
784
785        /**
786         * Initializes the Tabs
787         *
788         * @private
789         * @param {TabsConfig} config The Tabs configuration
790         */
791        function init(config) {
792            that._config = config;
793
794            // prevents multiple initialization
795            config.element.removeAttribute("data-" + NS + "-is");
796
797            cacheElements(config.element);
798            that._active = getActiveIndex(that._elements["tab"]);
799
800            if (that._elements.tabpanel) {
801                refreshActive();
802                bindEvents();
803                scrollToDeepLinkIdInTabs();
804            }
805
806            if (window.Granite && window.Granite.author && window.Granite.author.MessageChannel) {
807                /*
808                 * Editor message handling:
809                 * - subscribe to "cmp.panelcontainer" message requests sent by the editor frame
810                 * - check that the message data panel container type is correct and that the id (path) matches this specific Tabs component
811                 * - if so, route the "navigate" operation to enact a navigation of the Tabs based on index data
812                 */
813                CQ.CoreComponents.MESSAGE_CHANNEL = CQ.CoreComponents.MESSAGE_CHANNEL || new window.Granite.author.MessageChannel("cqauthor", window);
814                CQ.CoreComponents.MESSAGE_CHANNEL.subscribeRequestMessage("cmp.panelcontainer", function(message) {
815                    if (message.data && message.data.type === "cmp-tabs" && message.data.id === that._elements.self.dataset["cmpPanelcontainerId"]) {
816                        if (message.data.operation === "navigate") {
817                            navigate(message.data.index);
818                        }
819                    }
820                });
821            }
822        }
823
824        /**
825         * Displays the panel containing the element that corresponds to the deep link in the URI fragment
826         * and scrolls the browser to this element.
827         */
828        function scrollToDeepLinkIdInTabs() {
829            if (containerUtils) {
830                var deepLinkItemIdx = containerUtils.getDeepLinkItemIdx(that, "tab", "tabpanel");
831                if (deepLinkItemIdx > -1) {
832                    var deepLinkItem = that._elements["tab"][deepLinkItemIdx];
833                    if (deepLinkItem && that._elements["tab"][that._active].id !== deepLinkItem.id) {
834                        navigateAndFocusTab(deepLinkItemIdx, true);
835                    }
836                    var hashId = window.location.hash.substring(1);
837                    if (hashId) {
838                        var hashItem = document.querySelector("[id='" + hashId + "']");
839                        if (hashItem) {
840                            hashItem.scrollIntoView();
841                        }
842                    }
843                }
844            }
845        }
846
847        /**
848         * Returns the index of the active tab, if no tab is active returns 0
849         *
850         * @param {Array} tabs Tab elements
851         * @returns {Number} Index of the active tab, 0 if none is active
852         */
853        function getActiveIndex(tabs) {
854            if (tabs) {
855                for (var i = 0; i < tabs.length; i++) {
856                    if (tabs[i].classList.contains(selectors.active.tab)) {
857                        return i;
858                    }
859                }
860            }
861            return 0;
862        }
863
864        /**
865         * Caches the Tabs elements as defined via the {@code data-tabs-hook="ELEMENT_NAME"} markup API
866         *
867         * @private
868         * @param {HTMLElement} wrapper The Tabs wrapper element
869         */
870        function cacheElements(wrapper) {
871            that._elements = {};
872            that._elements.self = wrapper;
873            var hooks = that._elements.self.querySelectorAll("[data-" + NS + "-hook-" + IS + "]");
874
875            for (var i = 0; i < hooks.length; i++) {
876                var hook = hooks[i];
877                if (hook.closest("." + NS + "-" + IS) === that._elements.self) { // only process own tab elements
878                    var capitalized = IS;
879                    capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
880                    var key = hook.dataset[NS + "Hook" + capitalized];
881                    if (that._elements[key]) {
882                        if (!Array.isArray(that._elements[key])) {
883                            var tmp = that._elements[key];
884                            that._elements[key] = [tmp];
885                        }
886                        that._elements[key].push(hook);
887                    } else {
888                        that._elements[key] = hook;
889                    }
890                }
891            }
892        }
893
894        /**
895         * Binds Tabs event handling
896         *
897         * @private
898         */
899        function bindEvents() {
900            window.addEventListener("hashchange", scrollToDeepLinkIdInTabs, false);
901            var tabs = that._elements["tab"];
902            if (tabs) {
903                for (var i = 0; i < tabs.length; i++) {
904                    (function(index) {
905                        tabs[i].addEventListener("click", function(event) {
906                            navigateAndFocusTab(index);
907                        });
908                        tabs[i].addEventListener("keydown", function(event) {
909                            onKeyDown(event);
910                        });
911                    })(i);
912                }
913            }
914        }
915
916        /**
917         * Handles tab keydown events
918         *
919         * @private
920         * @param {Object} event The keydown event
921         */
922        function onKeyDown(event) {
923            var index = that._active;
924            var lastIndex = that._elements["tab"].length - 1;
925
926            switch (event.keyCode) {
927                case keyCodes.ARROW_LEFT:
928                case keyCodes.ARROW_UP:
929                    event.preventDefault();
930                    if (index > 0) {
931                        navigateAndFocusTab(index - 1);
932                    }
933                    break;
934                case keyCodes.ARROW_RIGHT:
935                case keyCodes.ARROW_DOWN:
936                    event.preventDefault();
937                    if (index < lastIndex) {
938                        navigateAndFocusTab(index + 1);
939                    }
940                    break;
941                case keyCodes.HOME:
942                    event.preventDefault();
943                    navigateAndFocusTab(0);
944                    break;
945                case keyCodes.END:
946                    event.preventDefault();
947                    navigateAndFocusTab(lastIndex);
948                    break;
949                default:
950                    return;
951            }
952        }
953
954        /**
955         * Refreshes the tab markup based on the current {@code Tabs#_active} index
956         *
957         * @private
958         */
959        function refreshActive() {
960            var tabpanels = that._elements["tabpanel"];
961            var tabs = that._elements["tab"];
962
963            if (tabpanels) {
964                if (Array.isArray(tabpanels)) {
965                    for (var i = 0; i < tabpanels.length; i++) {
966                        if (i === parseInt(that._active)) {
967                            tabpanels[i].classList.add(selectors.active.tabpanel);
968                            tabpanels[i].removeAttribute("aria-hidden");
969                            tabs[i].classList.add(selectors.active.tab);
970                            tabs[i].setAttribute("aria-selected", true);
971                            tabs[i].setAttribute("tabindex", "0");
972                        } else {
973                            tabpanels[i].classList.remove(selectors.active.tabpanel);
974                            tabpanels[i].setAttribute("aria-hidden", true);
975                            tabs[i].classList.remove(selectors.active.tab);
976                            tabs[i].setAttribute("aria-selected", false);
977                            tabs[i].setAttribute("tabindex", "-1");
978                        }
979                    }
980                } else {
981                    // only one tab
982                    tabpanels.classList.add(selectors.active.tabpanel);
983                    tabs.classList.add(selectors.active.tab);
984                }
985            }
986        }
987
988        /**
989         * Focuses the element and prevents scrolling the element into view
990         *
991         * @param {HTMLElement} element Element to focus
992         */
993        function focusWithoutScroll(element) {
994            var x = window.scrollX || window.pageXOffset;
995            var y = window.scrollY || window.pageYOffset;
996            element.focus();
997            window.scrollTo(x, y);
998        }
999
1000        /**
1001         * Navigates to the tab at the provided index
1002         *
1003         * @private
1004         * @param {Number} index The index of the tab to navigate to
1005         */
1006        function navigate(index) {
1007            that._active = index;
1008            refreshActive();
1009        }
1010
1011        /**
1012         * Navigates to the item at the provided index and ensures the active tab gains focus
1013         *
1014         * @private
1015         * @param {Number} index The index of the item to navigate to
1016         * @param {Boolean} keepHash true to keep the hash in the URL, false to update it
1017         */
1018        function navigateAndFocusTab(index, keepHash) {
1019            var exActive = that._active;
1020            if (!keepHash && containerUtils) {
1021                containerUtils.updateUrlHash(that, "tab", index);
1022            }
1023            navigate(index);
1024            focusWithoutScroll(that._elements["tab"][index]);
1025
1026            if (dataLayerEnabled) {
1027
1028                var activeItem = getDataLayerId(that._elements.tabpanel[index]);
1029                var exActiveItem = getDataLayerId(that._elements.tabpanel[exActive]);
1030
1031                dataLayer.push({
1032                    event: "cmp:show",
1033                    eventInfo: {
1034                        path: "component." + activeItem
1035                    }
1036                });
1037
1038                dataLayer.push({
1039                    event: "cmp:hide",
1040                    eventInfo: {
1041                        path: "component." + exActiveItem
1042                    }
1043                });
1044
1045                var tabsId = that._elements.self.id;
1046                var uploadPayload = { component: {} };
1047                uploadPayload.component[tabsId] = { shownItems: [activeItem] };
1048
1049                var removePayload = { component: {} };
1050                removePayload.component[tabsId] = { shownItems: undefined };
1051
1052                dataLayer.push(removePayload);
1053                dataLayer.push(uploadPayload);
1054            }
1055        }
1056    }
1057
1058    /**
1059     * Reads options data from the Tabs wrapper element, defined via {@code data-cmp-*} data attributes
1060     *
1061     * @private
1062     * @param {HTMLElement} element The Tabs element to read options data from
1063     * @returns {Object} The options read from the component data attributes
1064     */
1065    function readData(element) {
1066        var data = element.dataset;
1067        var options = [];
1068        var capitalized = IS;
1069        capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
1070        var reserved = ["is", "hook" + capitalized];
1071
1072        for (var key in data) {
1073            if (Object.prototype.hasOwnProperty.call(data, key)) {
1074                var value = data[key];
1075
1076                if (key.indexOf(NS) === 0) {
1077                    key = key.slice(NS.length);
1078                    key = key.charAt(0).toLowerCase() + key.substring(1);
1079
1080                    if (reserved.indexOf(key) === -1) {
1081                        options[key] = value;
1082                    }
1083                }
1084            }
1085        }
1086
1087        return options;
1088    }
1089
1090    /**
1091     * Parses the dataLayer string and returns the ID
1092     *
1093     * @private
1094     * @param {HTMLElement} item the accordion item
1095     * @returns {String} dataLayerId or undefined
1096     */
1097    function getDataLayerId(item) {
1098        if (item) {
1099            if (item.dataset.cmpDataLayer) {
1100                return Object.keys(JSON.parse(item.dataset.cmpDataLayer))[0];
1101            } else {
1102                return item.id;
1103            }
1104        }
1105        return null;
1106    }
1107
1108    /**
1109     * Document ready handler and DOM mutation observers. Initializes Tabs components as necessary.
1110     *
1111     * @private
1112     */
1113    function onDocumentReady() {
1114        dataLayerEnabled = document.body.hasAttribute("data-cmp-data-layer-enabled");
1115        dataLayer = (dataLayerEnabled) ? window.adobeDataLayer = window.adobeDataLayer || [] : undefined;
1116
1117        var elements = document.querySelectorAll(selectors.self);
1118        for (var i = 0; i < elements.length; i++) {
1119            new Tabs({ element: elements[i], options: readData(elements[i]) });
1120        }
1121
1122        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
1123        var body = document.querySelector("body");
1124        var observer = new MutationObserver(function(mutations) {
1125            mutations.forEach(function(mutation) {
1126                // needed for IE
1127                var nodesArray = [].slice.call(mutation.addedNodes);
1128                if (nodesArray.length > 0) {
1129                    nodesArray.forEach(function(addedNode) {
1130                        if (addedNode.querySelectorAll) {
1131                            var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self));
1132                            elementsArray.forEach(function(element) {
1133                                new Tabs({ element: element, options: readData(element) });
1134                            });
1135                        }
1136                    });
1137                }
1138            });
1139        });
1140
1141        observer.observe(body, {
1142            subtree: true,
1143            childList: true,
1144            characterData: true
1145        });
1146    }
1147
1148    if (document.readyState !== "loading") {
1149        onDocumentReady();
1150    } else {
1151        document.addEventListener("DOMContentLoaded", onDocumentReady);
1152    }
1153
1154    if (containerUtils) {
1155        window.addEventListener("load", containerUtils.scrollToAnchor, false);
1156    }
1157
1158}());
1159
1160/*******************************************************************************
1161 * Copyright 2018 Adobe
1162 *
1163 * Licensed under the Apache License, Version 2.0 (the "License");
1164 * you may not use this file except in compliance with the License.
1165 * You may obtain a copy of the License at
1166 *
1167 *     http://www.apache.org/licenses/LICENSE-2.0
1168 *
1169 * Unless required by applicable law or agreed to in writing, software
1170 * distributed under the License is distributed on an "AS IS" BASIS,
1171 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1172 * See the License for the specific language governing permissions and
1173 * limitations under the License.
1174 ******************************************************************************/
1175(function() {
1176    "use strict";
1177
1178    var containerUtils = window.CQ && window.CQ.CoreComponents && window.CQ.CoreComponents.c
1178ontainer && window.CQ.CoreComponents.container.utils ? window.CQ.CoreComponents.container.utils : undefined;
1179    if (!containerUtils) {
1180        // eslint-disable-next-line no-console
1181        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.");
1182    }
1183    var dataLayerEnabled;
1184    var dataLayer;
1185
1186    var NS = "cmp";
1187    var IS = "carousel";
1188
1189    var keyCodes = {
1190        SPACE: 32,
1191        END: 35,
1192        HOME: 36,
1193        ARROW_LEFT: 37,
1194        ARROW_UP: 38,
1195        ARROW_RIGHT: 39,
1196        ARROW_DOWN: 40
1197    };
1198
1199    var selectors = {
1200        self: "[data-" + NS + '-is="' + IS + '"]'
1201    };
1202
1203    var properties = {
1204        /**
1205         * Determines whether the Carousel will automatically transition between slides
1206         *
1207         * @memberof Carousel
1208         * @type {Boolean}
1209         * @default false
1210         */
1211        "autoplay": {
1212            "default": false,
1213            "transform": function(value) {
1214                return !(value === null || typeof value === "undefined");
1215            }
1216        },
1217        /**
1218         * Duration (in milliseconds) before automatically transitioning to the next slide
1219         *
1220         * @memberof Carousel
1221         * @type {Number}
1222         * @default 5000
1223         */
1224        "delay": {
1225            "default": 5000,
1226            "transform": function(value) {
1227                value = parseFloat(value);
1228                return !isNaN(value) ? value : null;
1229            }
1230        },
1231        /**
1232         * Determines whether automatic pause on hovering the carousel is disabled
1233         *
1234         * @memberof Carousel
1235         * @type {Boolean}
1236         * @default false
1237         */
1238        "autopauseDisabled": {
1239            "default": false,
1240            "transform": function(value) {
1241                return !(value === null || typeof value === "undefined");
1242            }
1243        }
1244    };
1245
1246    /**
1247     * Carousel Configuration
1248     *
1249     * @typedef {Object} CarouselConfig Represents a Carousel configuration
1250     * @property {HTMLElement} element The HTMLElement representing the Carousel
1251     * @property {*[]} options The Carousel options
1252     */
1253
1254    /**
1255     * Carousel
1256     *
1257     * @class Carousel
1258     * @classdesc An interactive Carousel component for navigating a list of generic items
1259     * @param {CarouselConfig} config The Carousel configuration
1260     */
1261    function Carousel(config) {
1262        var that = this;
1263
1264        if (config && config.element) {
1265            init(config);
1266        }
1267
1268        /**
1269         * Initializes the Carousel
1270         *
1271         * @private
1272         * @param {CarouselConfig} config The Carousel configuration
1273         */
1274        function init(config) {
1275            that._config = config;
1276
1277            // prevents multiple initialization
1278            config.element.removeAttribute("data-" + NS + "-is");
1279
1280            setupProperties(config.options);
1281            cacheElements(config.element);
1282
1283            that._active = 0;
1284            that._paused = false;
1285
1286            if (that._elements.item) {
1287                initializeActive();
1288                bindEvents();
1289                resetAutoplayInterval();
1290                refreshPlayPauseActions();
1291                scrollToDeepLinkIdInCarousel();
1292            }
1293
1294            // TODO: This section is only relevant in edit mode and should move to the editor clientLib
1295            if (window.Granite && window.Granite.author && window.Granite.author.MessageChannel) {
1296                /*
1297                 * Editor message handling:
1298                 * - subscribe to "cmp.panelcontainer" message requests sent by the editor frame
1299                 * - check that the message data panel container type is correct and that the id (path) matches this specific Carousel component
1300                 * - if so, route the "navigate" operation to enact a navigation of the Carousel based on index data
1301                 */
1302                window.CQ = window.CQ || {};
1303                window.CQ.CoreComponents = window.CQ.CoreComponents || {};
1304                window.CQ.CoreComponents.MESSAGE_CHANNEL = window.CQ.CoreComponents.MESSAGE_CHANNEL || new window.Granite.author.MessageChannel("cqauthor", window);
1305                window.CQ.CoreComponents.MESSAGE_CHANNEL.subscribeRequestMessage("cmp.panelcontainer", function(message) {
1306                    if (message.data && message.data.type === "cmp-carousel" && message.data.id === that._elements.self.dataset["cmpPanelcontainerId"]) {
1307                        if (message.data.operation === "navigate") {
1308                            navigate(message.data.index);
1309                        }
1310                    }
1311                });
1312            }
1313        }
1314
1315        /**
1316         * Displays the slide containing the element that corresponds to the deep link in the URI fragment
1317         * and scrolls the browser to this element.
1318         */
1319        function scrollToDeepLinkIdInCarousel() {
1320            if (containerUtils) {
1321                var deepLinkItemIdx = containerUtils.getDeepLinkItemIdx(that, "item", "item");
1322                if (deepLinkItemIdx > -1) {
1323                    var deepLinkItem = that._elements["item"][deepLinkItemIdx];
1324                    if (deepLinkItem && that._elements["item"][that._active].id !== deepLinkItem.id) {
1325                        navigateAndFocusIndicator(deepLinkItemIdx, true);
1326                        // pause the carousel auto-rotation
1327                        pause();
1328                    }
1329                    var hashId = window.location.hash.substring(1);
1330                    if (hashId) {
1331                        var hashItem = document.querySelector("[id='" + hashId + "']");
1332                        if (hashItem) {
1333                            hashItem.scrollIntoView();
1334                        }
1335                    }
1336                }
1337            }
1338        }
1339
1340        /**
1341         * Caches the Carousel elements as defined via the {@code data-carousel-hook="ELEMENT_NAME"} markup API
1342         *
1343         * @private
1344         * @param {HTMLElement} wrapper The Carousel wrapper element
1345         */
1346        function cacheElements(wrapper) {
1347            that._elements = {};
1348            that._elements.self = wrapper;
1349            var hooks = that._elements.self.querySelectorAll("[data-" + NS + "-hook-" + IS + "]");
1350
1351            for (var i = 0; i < hooks.length; i++) {
1352                var hook = hooks[i];
1353                var capitalized = IS;
1354                capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
1355                var key = hook.dataset[NS + "Hook" + capitalized];
1356                if (that._elements[key]) {
1357                    if (!Array.isArray(that._elements[key])) {
1358                        var tmp = that._elements[key];
1359                        that._elements[key] = [tmp];
1360                    }
1361                    that._elements[key].push(hook);
1362                } else {
1363                    that._elements[key] = hook;
1364                }
1365            }
1366        }
1367
1368        /**
1369         * Sets up properties for the Carousel based on the passed options.
1370         *
1371         * @private
1372         * @param {Object} options The Carousel options
1373         */
1374        function setupProperties(options) {
1375            that._properties = {};
1376
1377            for (var key in properties) {
1378                if (Object.prototype.hasOwnProperty.call(properties, key)) {
1379                    var property = properties[key];
1380                    var value = null;
1381
1382                    if (options && options[key] != null) {
1383                        value = options[key];
1384
1385                        // transform the provided option
1386                        if (property && typeof property.transform === "function") {
1387                            value = property.transform(value);
1388                        }
1389                    }
1390
1391                    if (value === null) {
1392                        // value still null, take the property default
1393                        value = properties[key]["default"];
1394                    }
1395
1396                    that._properties[key] = value;
1397                }
1398            }
1399        }
1400
1401        /**
1402         * Binds Carousel event handling
1403         *
1404         * @private
1405         */
1406        function bindEvents() {
1407            window.addEventListener("hashchange", scrollToDeepLinkIdInCarousel, false);
1408            if (that._elements["previous"]) {
1409                that._elements["previous"].addEventListener("click", function() {
1410                    var index = getPreviousIndex();
1411                    navigate(index);
1412                    if (dataLayerEnabled) {
1413                        dataLayer.push({
1414                            event: "cmp:show",
1415                            eventInfo: {
1416                                path: "component." + getDataLayerId(that._elements.item[index])
1417                            }
1418                        });
1419                    }
1420                });
1421            }
1422
1423            if (that._elements["next"]) {
1424                that._elements["next"].addEventListener("click", function() {
1425                    var index = getNextIndex();
1426                    navigate(index);
1427                    if (dataLayerEnabled) {
1428                        dataLayer.push({
1429                            event: "cmp:show",
1430                            eventInfo: {
1431                                path: "component." + getDataLayerId(that._elements.item[index])
1432                            }
1433                        });
1434                    }
1435                });
1436            }
1437
1438            var indicators = that._elements["indicator"];
1439            if (indicators) {
1440                for (var i = 0; i < indicators.length; i++) {
1441                    (function(index) {
1442                        indicators[i].addEventListener("click", function(event) {
1443                            navigateAndFocusIndicator(index);
1444                            // pause the carousel auto-rotation
1445                            pause();
1446                        });
1447                    })(i);
1448                }
1449            }
1450
1451            if (that._elements["pause"]) {
1452                if (that._properties.autoplay) {
1453                    that._elements["pause"].addEventListener("click", onPauseClick);
1454                }
1455            }
1456
1457            if (that._elements["play"]) {
1458                if (that._properties.autoplay) {
1459                    that._elements["play"].addEventListener("click", onPlayClick);
1460                }
1461            }
1462
1463            that._elements.self.addEventListener("keydown", onKeyDown);
1464
1465            if (!that._properties.autopauseDisabled) {
1466                that._elements.self.addEventListener("mouseenter", onMouseEnter);
1467                that._elements.self.addEventListener("mouseleave", onMouseLeave);
1468            }
1469
1470            // for accessibility we pause animation when a element get focused
1471            var items = that._elements["item"];
1472            if (items) {
1473                for (var j = 0; j < items.length; j++) {
1474                    items[j].addEventListener("focusin", onMouseEnter);
1475                    items[j].addEventListener("focusout", onMouseLeave);
1476                }
1477            }
1478        }
1479
1480        /**
1481         * Handles carousel keydown events
1482         *
1483         * @private
1484         * @param {Object} event The keydown event
1485         */
1486        function onKeyDown(event) {
1487            var index = that._active;
1488            var lastIndex = that._elements["indicator"].length - 1;
1489
1490            switch (event.keyCode) {
1491                case keyCodes.ARROW_LEFT:
1492                case keyCodes.ARROW_UP:
1493                    event.preventDefault();
1494                    if (index > 0) {
1495                        navigateAndFocusIndicator(index - 1);
1496                    }
1497                    break;
1498                case keyCodes.ARROW_RIGHT:
1499                case keyCodes.ARROW_DOWN:
1500                    event.preventDefault();
1501                    if (index < lastIndex) {
1502                        navigateAndFocusIndicator(index + 1);
1503                    }
1504                    break;
1505                case keyCodes.HOME:
1506                    event.preventDefault();
1507                    navigateAndFocusIndicator(0);
1508                    break;
1509                case keyCodes.END:
1510                    event.preventDefault();
1511                    navigateAndFocusIndicator(lastIndex);
1512                    break;
1513                case keyCodes.SPACE:
1514                    if (that._properties.autoplay && (event.target !== that._elements["previous"] && event.target !== that._elements["next"])) {
1515                        event.preventDefault();
1516                        if (!that._paused) {
1517                            pause();
1518                        } else {
1519                            play();
1520                        }
1521                    }
1522                    if (event.target === that._elements["pause"]) {
1523                        that._elements["play"].focus();
1524                    }
1525                    if (event.target === that._elements["play"]) {
1526                        that._elements["pause"].focus();
1527                    }
1528                    break;
1529                default:
1530                    return;
1531            }
1532        }
1533
1534        /**
1535         * Handles carousel mouseenter events
1536         *
1537         * @private
1538         * @param {Object} event The mouseenter event
1539         */
1540        function onMouseEnter(event) {
1541            clearAutoplayInterval();
1542        }
1543
1544        /**
1545         * Handles carousel mouseleave events
1546         *
1547         * @private
1548         * @param {Object} event The mouseleave event
1549         */
1550        function onMouseLeave(event) {
1551            resetAutoplayInterval();
1552        }
1553
1554        /**
1555         * Handles pause element click events
1556         *
1557         * @private
1558         * @param {Object} event The click event
1559         */
1560        function onPauseClick(event) {
1561            pause();
1562            that._elements["play"].focus();
1563        }
1564
1565        /**
1566         * Handles play element click events
1567         *
1568         * @private
1569         * @param {Object} event The click event
1570         */
1571        function onPlayClick() {
1572            play();
1573            that._elements["pause"].focus();
1574        }
1575
1576        /**
1577         * Pauses the playing of the Carousel. Sets {@code Carousel#_paused} marker.
1578         * Only relevant when autoplay is enabled
1579         *
1580         * @private
1581         */
1582        function pause() {
1583            that._paused = true;
1584            clearAutoplayInterval();
1585            refreshPlayPauseActions();
1586        }
1587
1588        /**
1589         * Enables the playing of the Carousel. Sets {@code Carousel#_paused} marker.
1590         * Only relevant when autoplay is enabled
1591         *
1592         * @private
1593         */
1594        function play() {
1595            that._paused = false;
1596
1597            // If the Carousel is hovered, don't begin auto transitioning until the next mouse leave event
1598            var hovered = false;
1599            if (that._elements.self.parentElement) {
1600                hovered = that._elements.self.parentElement.querySelector(":hover") === that._elements.self;
1601            }
1602            if (that._properties.autopauseDisabled || !hovered) {
1603                resetAutoplayInterval();
1604            }
1605
1606            refreshPlayPauseActions();
1607        }
1608
1609        /**
1610         * Refreshes the play/pause action markup based on the {@code Carousel#_paused} state
1611         *
1612         * @private
1613         */
1614        function refreshPlayPauseActions() {
1615            setActionDisabled(that._elements["pause"], that._paused);
1616            setActionDisabled(that._elements["play"], !that._paused);
1617        }
1618
1619        /**
1620         * Initialize {@code Carousel#_active} based on the active item of the carousel.
1621         */
1622        function initializeActive() {
1623            var items = that._elements["item"];
1624            if (items && Array.isArray(items)) {
1625                for (var i = 0; i < items.length; i++) {
1626                    if (items[i].classList.contains("cmp-carousel__item--active")) {
1627                        that._active = i;
1628                        break;
1629                    }
1630                }
1631            }
1632        }
1633
1634        /**
1635         * Refreshes the item markup based on the current {@code Carousel#_active} index
1636         *
1637         * @private
1638         */
1639        function refreshActive() {
1640            var items = that._elements["item"];
1641            var indicators = that._elements["indicator"];
1642
1643            if (items) {
1644                if (Array.isArray(items)) {
1645                    for (var i = 0; i < items.length; i++) {
1646                        if (i === parseInt(that._active)) {
1647                            items[i].classList.add("cmp-carousel__item--active");
1648                            items[i].removeAttribute("aria-hidden");
1649                            indicators[i].classList.add("cmp-carousel__indicator--active");
1650                            indicators[i].setAttribute("aria-selected", true);
1651                            indicators[i].setAttribute("tabindex", "0");
1652                        } else {
1653                            items[i].classList.remove("cmp-carousel__item--active");
1654                            items[i].setAttribute("aria-hidden", true);
1655                            indicators[i].classList.remove("cmp-carousel__indicator--active");
1656                            indicators[i].setAttribute("aria-selected", false);
1657                            indicators[i].setAttribute("tabindex", "-1");
1658                        }
1659                    }
1660                } else {
1661                    // only one item
1662                    items.classList.add("cmp-carousel__item--active");
1663                    indicators.classList.add("cmp-carousel__indicator--active");
1664                }
1665            }
1666        }
1667
1668        /**
1669         * Focuses the element and prevents scrolling the element into view
1670         *
1671         * @param {HTMLElement} element Element to focus
1672         */
1673        function focusWithoutScroll(element) {
1674            var x = window.scrollX || window.pageXOffset;
1675            var y = window.scrollY || window.pageYOffset;
1676            element.focus();
1677            window.scrollTo(x, y);
1678        }
1679
1680        /**
1681         * Retrieves the next active index, with looping
1682         *
1683         * @private
1684         * @returns {Number} Index of the next carousel item
1685         */
1686        function getNextIndex() {
1687            return that._active === (that._elements["item"].length - 1) ? 0 : that._active + 1;
1688        }
1689
1690        /**
1691         * Retrieves the previous active index, with looping
1692         *
1693         * @private
1694         * @returns {Number} Index of the previous carousel item
1695         */
1696        function getPreviousIndex() {
1697            return that._active === 0 ? (that._elements["item"].length - 1) : that._active - 1;
1698        }
1699
1700        /**
1701         * Navigates to the item at the provided index
1702         *
1703         * @private
1704         * @param {Number} index The index of the item to navigate to
1705         * @param {Boolean} keepHash true to keep the hash in the URL, false to update it
1706         */
1707        function navigate(index, keepHash) {
1708            if (index < 0 || index > (that._elements["item"].length - 1)) {
1709                return;
1710            }
1711
1712            that._active = index;
1713            refreshActive();
1714
1715            if (!keepHash && containerUtils) {
1716                containerUtils.updateUrlHash(that, "item", index);
1717            }
1718
1719            if (dataLayerEnabled) {
1720                var carouselId = that._elements.self.id;
1721                var activeItem = getDataLayerId(that._elements.item[index]);
1722                var updatePayload = { component: {} };
1723                updatePayload.component[carouselId] = { shownItems: [activeItem] };
1724
1725                var removePayload = { component: {} };
1726                removePayload.component[carouselId] = { shownItems: undefined };
1727
1728                dataLayer.push(removePayload);
1729                dataLayer.push(updatePayload);
1730            }
1731
1732            // reset the autoplay transition interval following navigation, if not already hovering the carousel
1733            if (that._elements.self.parentElement) {
1734                if (that._elements.self.parentElement.querySelector(":hover") !== that._elements.self) {
1735                    resetAutoplayInterval();
1736                }
1737            }
1738        }
1739
1740        /**
1741         * Navigates to the item at the provided index and ensures the active indicator gains focus
1742         *
1743         * @private
1744         * @param {Number} index The index of the item to navigate to
1745         * @param {Boolean} keepHash true to keep the hash in the URL, false to update it
1746         */
1747        function navigateAndFocusIndicator(index, keepHash) {
1748            navigate(index, keepHash);
1749            focusWithoutScroll(that._elements["indicator"][index]);
1750
1751            if (dataLayerEnabled) {
1752                dataLayer.push({
1753                    event: "cmp:show",
1754                    eventInfo: {
1755                        path: "component." + getDataLayerId(that._elements.item[index])
1756                    }
1757                });
1758            }
1759        }
1760
1761        /**
1762         * Starts/resets automatic slide transition interval
1763         *
1764         * @private
1765         */
1766        function resetAutoplayInterval() {
1767            if (that._paused || !that._properties.autoplay) {
1768                return;
1769            }
1770            clearAutoplayInterval();
1771            that._autoplayIntervalId = window.setInterval(function() {
1772                if (document.visibilityState && document.hidden) {
1773                    return;
1774                }
1775                var indicators = that._elements["indicators"];
1776                if (indicators !== document.activeElement && indicators.contains(document.activeElement)) {
1777                    // if an indicator has focus, ensure we switch focus following navigation
1778                    navigateAndFocusIndicator(getNextIndex(), true);
1779                } else {
1780                    navigate(getNextIndex(), true);
1781                }
1782            }, that._properties.delay);
1783        }
1784
1785        /**
1786         * Clears/pauses automatic slide transition interval
1787         *
1788         * @private
1789         */
1790        function clearAutoplayInterval() {
1791            window.clearInterval(that._autoplayIntervalId);
1792            that._autoplayIntervalId = null;
1793        }
1794
1795        /**
1796         * Sets the disabled state for an action and toggles the appropriate CSS classes
1797         *
1798         * @private
1799         * @param {HTMLElement} action Action to disable
1800         * @param {Boolean} [disable] {@code true} to disable, {@code false} to enable
1801         */
1802        function setActionDisabled(action, disable) {
1803            if (!action) {
1804                return;
1805            }
1806            if (disable !== false) {
1807                action.disabled = true;
1808                action.classList.add("cmp-carousel__action--disabled");
1809            } else {
1810                action.disabled = false;
1811                action.classList.remove("cmp-carousel__action--disabled");
1812            }
1813        }
1814    }
1815
1816    /**
1817     * Parses the dataLayer string and returns the ID
1818     *
1819     * @private
1820     * @param {HTMLElement} item the accordion item
1821     * @returns {String} dataLayerId or undefined
1822     */
1823    function getDataLayerId(item) {
1824        if (item) {
1825            if (item.dataset.cmpDataLayer) {
1826                return Object.keys(JSON.parse(item.dataset.cmpDataLayer))[0];
1827            } else {
1828                return item.id;
1829            }
1830        }
1831        return null;
1832    }
1833
1834    /**
1835     * Document ready handler and DOM mutation observers. Initializes Carousel components as necessary.
1836     *
1837     * @private
1838     */
1839    function onDocumentReady() {
1840        dataLayerEnabled = document.body.hasAttribute("data-cmp-data-layer-enabled");
1841        dataLayer = (dataLayerEnabled) ? window.adobeDataLayer = window.adobeDataLayer || [] : undefined;
1842
1843        var elements = document.querySelectorAll(selectors.self);
1844        for (var i = 0; i < elements.length; i++) {
1845            new Carousel({ element: elements[i], options: CMP.utils.readData(elements[i], IS) });
1846        }
1847
1848        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
1849        var body = document.querySelector("body");
1850        var observer = new MutationObserver(function(mutations) {
1851            mutations.forEach(function(mutation) {
1852                // needed for IE
1853                var nodesArray = [].slice.call(mutation.addedNodes);
1854                if (nodesArray.length > 0) {
1855                    nodesArray.forEach(function(addedNode) {
1856                        if (addedNode.querySelectorAll) {
1857                            var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self));
1858                            elementsArray.forEach(function(element) {
1859                                new Carousel({ element: element, options: CMP.utils.readData(element, IS) });
1860                            });
1861                        }
1862                    });
1863                }
1864            });
1865        });
1866
1867        observer.observe(body, {
1868            subtree: true,
1869            childList: true,
1870            characterData: true
1871        });
1872    }
1873
1874    var documentReady = document.readyState !== "loading" ? Promise.resolve() : new Promise(function(resolve) {
1875        document.addEventListener("DOMContentLoaded", resolve);
1876    });
1877    var utilsReady = (window.CMP && window.CMP.utils) ? Promise.resolve() : new Promise(function(resolve) {
1878        document.addEventListener("core.wcm.components.commons.site.utils.loaded", resolve);
1879    });
1880    Promise.all([documentReady, utilsReady]).then(onDocumentReady);
1881
1882    if (containerUtils) {
1883        window.addEventListener("load", containerUtils.scrollToAnchor, false);
1884    }
1885
1886}());
1887
1888/*******************************************************************************
1889 * Copyright 2017 Adobe
1890 *
1891 * Licensed under the Apache License, Version 2.0 (the "License");
1892 * you may not use this file except in compliance with the License.
1893 * You may obtain a copy of the License at
1894 *
1895 *     http://www.apache.org/licenses/LICENSE-2.0
1896 *
1897 * Unless required by applicable law or agreed to in writing, software
1898 * distributed under the License is distributed on an "AS IS" BASIS,
1899 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1900 * See the License for the specific language governing permissions and
1901 * limitations under the License.
1902 ******************************************************************************/
1903(function() {
1904    "use strict";
1905
1906    var NS = "cmp";
1907    var IS = "search";
1908
1909    var DELAY = 300; // time before fetching new results when the user is typing a search string
1910    var LOADING_DISPLAY_DELAY = 300; // minimum time during which the loading indicator is displayed
1911    var PARAM_RESULTS_OFFSET = "resultsOffset";
1912
1913    var keyCodes = {
1914        TAB: 9,
1915        ENTER: 13,
1916        ESCAPE: 27,
1917        ARROW_UP: 38,
1918        ARROW_DOWN: 40
1919    };
1920
1921    var selectors = {
1922        self: "[data-" + NS + '-is="' + IS + '"]',
1923        item: {
1924            self: "[data-" + NS + "-hook-" + IS + '="item"]',
1925            title: "[data-" + NS + "-hook-" + IS + '="itemTitle"]',
1926            focused: "." + NS + "-search__item--is-focused"
1927        }
1928    };
1929
1930    var properties = {
1931        /**
1932         * The minimum required length of the search term before results are fetched.
1933         *
1934         * @memberof Search
1935         * @type {Number}
1936         * @default 3
1937         */
1938        minLength: {
1939            "default": 3,
1940            transform: function(value) {
1941                value = parseFloat(value);
1942                return isNaN(value) ? null : value;
1943            }
1944        },
1945        /**
1946         * The maximal number of results fetched by a search request.
1947         *
1948         * @memberof Search
1949         * @type {Number}
1950         * @default 10
1951         */
1952        resultsSize: {
1953            "default": 10,
1954            transform: function(value) {
1955                value = parseFloat(value);
1956                return isNaN(value) ? null : value;
1957            }
1958        }
1959    };
1960
1961    var idCount = 0;
1962
1963    function readData(element) {
1964        var data = element.dataset;
1965        var options = [];
1966        var capitalized = IS;
1967        capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
1968        var reserved = ["is", "hook" + capitalized];
1969
1970        for (var key in data) {
1971            if (Object.prototype.hasOwnProperty.call(data, key)) {
1972                var value = data[key];
1973
1974                if (key.indexOf(NS) === 0) {
1975                    key = key.slice(NS.length);
1976                    key = key.charAt(0).toLowerCase() + key.substring(1);
1977
1978                    if (reserved.indexOf(key) === -1) {
1979                        options[key] = value;
1980                    }
1981                }
1982            }
1983        }
1984
1985        return options;
1986    }
1987
1988    function toggleShow(element, show) {
1989        if (element) {
1990            if (show !== false) {
1991                element.style.display = "block";
1992                element.setAttribute("aria-hidden", false);
1993            } else {
1994                element.style.display = "none";
1995                element.setAttribute("aria-hidden", true);
1996            }
1997        }
1998    }
1999
2000    function serialize(form) {
2001        var query = [];
2002        if (form && form.elements) {
2003            for (var i = 0; i < form.elements.length; i++) {
2004                var node = form.elements[i];
2005                if (!node.disabled && node.name) {
2006                    var param = [node.name, encodeURIComponent(node.value)];
2007                    query.push(param.join("="));
2008                }
2009            }
2010        }
2011        return query.join("&");
2012    }
2013
2014    function mark(node, regex) {
2015        if (!node || !regex) {
2016            return;
2017        }
2018
2019        // text nodes
2020        if (node.nodeType === 3) {
2021            var nodeValue = node.nodeValue;
2022            var match = regex.exec(nodeValue);
2023
2024            if (nodeValue && match) {
2025                var element = document.createElement("mark");
2026                element.className = NS + "-search__item-mark";
2027                element.appendChild(document.createTextNode(match[0]));
2028
2029                var after = node.splitText(match.index);
2030                after.nodeValue = after.nodeValue.substring(match[0].length);
2031                node.parentNode.insertBefore(element, after);
2032            }
2033        } else if (node.hasChildNodes()) {
2034            for (var i = 0; i < node.childNodes.length; i++) {
2035                // recurse
2036                mark(node.childNodes[i], regex);
2037            }
2038        }
2039    }
2040
2041    function Search(config) {
2042        if (config.element) {
2043            // prevents multiple initialization
2044            config.element.removeAttribute("data-" + NS + "-is");
2045        }
2046
2047        this._cacheElements(config.element);
2048        this._setupProperties(config.options);
2049
2050        this._action = this._elements.form.getAttribute("action");
2051        this._resultsOffset = 0;
2052        this._hasMoreResults = true;
2053
2054        this._elements.input.addEventListener("input", this._onInput.bind(this));
2055        this._elements.input.addEventListener("focus", this._onInput.bind(this));
2056        this._elements.input.addEventListener("keydown", this._onKeydown.bind(this));
2057        this._elements.clear.addEventListener("click", this._onClearClick.bind(this));
2058        document.addEventListener("click", this._onDocumentClick.bind(this));
2059        this._elements.results.addEventListener("scroll", this._onScroll.bind(this));
2060
2061        this._makeAccessible();
2062    }
2063
2064    Search.prototype._displayResults = function() {
2065        if (this._elements.input.value.length === 0) {
2066            toggleShow(this._elements.clear, false);
2067            this._cancelResults();
2068        } else if (this._elements.input.value.length < this._properties.minLength) {
2069            toggleShow(this._elements.clear, true);
2070        } else {
2071            this._updateResults();
2072            toggleShow(this._elements.clear, true);
2073        }
2074    };
2075
2076    Search.prototype._onScroll = function(event) {
2077        // fetch new results when the results to be scrolled down are less than the visible results
2078        if (this._elements.results.scrollTop + 2 * this._elements.results.clientHeight >= this._elements.results.scrollHeight) {
2079            this._resultsOffset += this._properties.resultsSize;
2080            this._displayResults();
2081        }
2082    };
2083
2084    Search.prototype._onInput = function(event) {
2085        var self = this;
2086        self._cancelResults();
2087        // start searching when the search term reaches the minimum length
2088        this._timeout = setTimeout(function() {
2089            self._displayResults();
2090        }, DELAY);
2091    };
2092
2093    Search.prototype._onKeydown = function(event) {
2094        var self = this;
2095
2096        switch (event.keyCode) {
2097            case keyCodes.TAB:
2098                if (self._resultsOpen()) {
2099                    toggleShow(self._elements.results, false);
2100                    self._elements.input.setAttribute("aria-expanded", "false");
2101                }
2102                break;
2103            case keyCodes.ENTER:
2104                event.preventDefault();
2105                if (self._resultsOpen()) {
2106                    var focused = self._elements.results.querySelector(selectors.item.focused);
2107                    if (focused) {
2108                        focused.click();
2109                    }
2110                }
2111                break;
2112            case keyCodes.ESCAPE:
2113                self._cancelResults();
2114                break;
2115            case keyCodes.ARROW_UP:
2116                if (self._resultsOpen()) {
2117                    event.preventDefault();
2118                    self._stepResultFocus(true);
2119                }
2120                break;
2121            case keyCodes.ARROW_DOWN:
2122                if (self._resultsOpen()) {
2123                    event.preventDefault();
2124                    self._stepResultFocus();
2125                } else {
2126                    // test the input and if necessary fetch and display the results
2127                    self._onInput();
2128                }
2129                break;
2130            default:
2131                return;
2132        }
2133    };
2134
2135    Search.prototype._onClearClick = function(event) {
2136        event.preventDefault();
2137        this._elements.input.value = "";
2138        toggleShow(this._elements.clear, false);
2139        toggleShow(this._elements.results, false);
2140        this._elements.input.setAttribute("aria-expanded", "false");
2141    };
2142
2143    Search.prototype._onDocumentClick = function(event) {
2144        var inputContainsTarget =  this._elements.input.contains(event.target);
2145        var resultsContainTarget = this._elements.results.contains(event.target);
2146
2147        if (!(inputContainsTarget || resultsContainTarget)) {
2148            toggleShow(this._elements.results, false);
2149            this._elements.input.setAttribute("aria-expanded", "false");
2150        }
2151    };
2152
2153    Search.prototype._resultsOpen = function() {
2154        return this._elements.results.style.display !== "none";
2155    };
2156
2157    Search.prototype._makeAccessible = function() {
2158        var id = NS + "-search-results-" + idCount;
2159        this._elements.input.setAttribute("aria-owns", id);
2160        this._elements.results.id = id;
2161        idCount++;
2162    };
2163
2164    Search.prototype._generateItems = function(data, results) {
2165        var self = this;
2166
2167        data.forEach(function(item) {
2168            var el = document.createElement("span");
2169            el.innerHTML = self._elements.itemTemplate.innerHTML;
2170            el.querySelectorAll(selectors.item.title)[0].appendChild(document.createTextNode(item.title));
2171            el.querySelectorAll(selectors.item.self)[0].setAttribute("href", item.url);
2172            results.innerHTML += el.innerHTML;
2173        });
2174    };
2175
2176    Search.prototype._markResults = function() {
2177        var nodeList = this._elements.results.querySelectorAll(selectors.item.self);
2178        var escapedTerm = this._elements.input.value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
2179        var regex = new RegExp("(" + escapedTerm + ")", "gi");
2180
2181        for (var i = this._resultsOffset - 1; i < nodeList.length; ++i) {
2182            var result = nodeList[i];
2183            mark(result, regex);
2184        }
2185    };
2186
2187    Search.prototype._stepResultFocus = function(reverse) {
2188        var results = this._elements.results.querySelectorAll(selectors.item.self);
2189        var focused = this._elements.results.querySelector(selectors.item.focused);
2190        var newFocused;
2191        var index = Array.prototype.indexOf.call(results, focused);
2192        var focusedCssClass = NS + "-search__item--is-focused";
2193
2194        if (results.length > 0) {
2195
2196            if (!reverse) {
2197                // highlight the next result
2198                if (index < 0) {
2199                    results[0].classList.add(focusedCssClass);
2200                    results[0].setAttribute("aria-selected", "true");
2201                } else if (index + 1 < results.length) {
2202                    results[index].classList.remove(focusedCssClass);
2203                    results[index].setAttribute("aria-selected", "false");
2204                    results[index + 1].classList.add(focusedCssClass);
2205                    results[index + 1].setAttribute("aria-selected", "true");
2206                }
2207
2208                // if the last visible result is partially hidden, scroll up until it's completely visible
2209                newFocused = this._elements.results.querySelector(selectors.item.focused);
2210                if (newFocused) {
2211                    var bottomHiddenHeight = newFocused.offsetTop + newFocused.offsetHeight - this._elements.results.scrollTop - this._elements.results.clientHeight;
2212                    if (bottomHiddenHeight > 0) {
2213                        this._elements.results.scrollTop += bottomHiddenHeight;
2214                    } else {
2215                        this._onScroll();
2216                    }
2217                }
2218
2219            } else {
2220                // highlight the previous result
2221                if (index >= 1) {
2222                    results[index].classList.remove(focusedCssClass);
2223                    results[index].setAttribute("aria-selected", "false");
2224                    results[index - 1].classList.add(focusedCssClass);
2225                    results[index - 1].setAttribute("aria-selected", "true");
2226                }
2227
2228                // if the first visible result is partially hidden, scroll down until it's completely visible
2229                newFocused = this._elements.results.querySelector(selectors.item.focused);
2230                if (newFocused) {
2231                    var topHiddenHeight = this._elements.results.scrollTop - newFocused.offsetTop;
2232                    if (topHiddenHeight > 0) {
2233                        this._elements.results.scrollTop -= topHiddenHeight;
2234                    }
2235                }
2236            }
2237        }
2238    };
2239
2240    Search.prototype._updateResults = function() {
2241        var self = this;
2242        if (self._hasMoreResults) {
2243            var request = new XMLHttpRequest();
2244            var url = self._action + "?" + serialize(self._elements.form) + "&" + PARAM_RESULTS_OFFSET + "=" + self._resultsOffset;
2245
2246            request.open("GET", url, true);
2247            request.onload = function() {
2248                // when the results are loaded: hide the loading indicator and display the search icon after a minimum period
2249                setTimeout(function() {
2250                    toggleShow(self._elements.loadingIndicator, false);
2251                    toggleShow(self._elements.icon, true);
2252                }, LOADING_DISPLAY_DELAY);
2253                if (request.status >= 200 && request.status < 400) {
2254                    // success status
2255                    var data = JSON.parse(request.responseText);
2256                    if (data.length > 0) {
2257                        self._generateItems(data, self._elements.results);
2258                        self._markResults();
2259                        toggleShow(self._elements.results, true);
2260                        self._elements.input.setAttribute("aria-expanded", "true");
2261                    } else {
2262                        self._hasMoreResults = false;
2263                    }
2264                    // the total number of results is not a multiple of the fetched results:
2265                    // -> we reached the end of the query
2266                    if (self._elements.results.querySelectorAll(selectors.item.self).length % self._properties.resultsSize > 0) {
2267                        self._hasMoreResults = false;
2268                    }
2269                } else {
2270                    // error status
2271                }
2272            };
2273            // when the results are loading: display the loading indicator and hide the search icon
2274            toggleShow(self._elements.loadingIndicator, true);
2275            toggleShow(self._elements.icon, false);
2276            request.send();
2277        }
2278    };
2279
2280    Search.prototype._cancelResults = function() {
2281        clearTimeout(this._timeout);
2282        this._elements.results.scrollTop = 0;
2283        this._resultsOffset = 0;
2284        this._hasMoreResults = true;
2285        this._elements.results.innerHTML = "";
2286        this._elements.input.setAttribute("aria-expanded", "false");
2287    };
2288
2289    Search.prototype._cacheElements = function(wrapper) {
2290        this._elements = {};
2291        this._elements.self = wrapper;
2292        var hooks = this._elements.self.querySelectorAll("[data-" + NS + "-hook-" + IS + "]");
2293
2294        for (var i = 0; i < hooks.length; i++) {
2295            var hook = hooks[i];
2296            var capitalized = IS;
2297            capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
2298            var key = hook.dataset[NS + "Hook" + capitalized];
2299            this._elements[key] = hook;
2300        }
2301    };
2302
2303    Search.prototype._setupProperties = function(options) {
2304        this._properties = {};
2305
2306        for (var key in properties) {
2307            if (Object.prototype.hasOwnProperty.call(properties, key)) {
2308                var property = properties[key];
2309                if (options && options[key] != null) {
2310                    if (property && typeof property.transform === "function") {
2311                        this._properties[key] = property.transform(options[key]);
2312                    } else {
2313                        this._properties[key] = options[key];
2314                    }
2315                } else {
2316                    this._properties[key] = properties[key]["default"];
2317                }
2318            }
2319        }
2320    };
2321
2322    function onDocumentReady() {
2323        var elements = document.querySelectorAll(selectors.self);
2324        for (var i = 0; i < elements.length; i++) {
2325            new Search({ element: elements[i], options: readData(elements[i]) });
2326        }
2327
2328        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
2329        var body = document.querySelector("body");
2330        var observer = new MutationObserver(function(mutations) {
2331            mutations.forEach(function(mutation) {
2332                // needed for IE
2333                var nodesArray = [].slice.call(mutation.addedNodes);
2334                if (nodesArray.length > 0) {
2335                    nodesArray.forEach(function(addedNode) {
2336                        if (addedNode.querySelectorAll) {
2337                            var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self));
2338                            elementsArray.forEach(function(element) {
2339                                new Search({ element: element, options: readData(element) });
2340                            });
2341                        }
2342                    });
2343                }
2344            });
2345        });
2346
2347        observer.observe(body, {
2348            subtree: true,
2349            childList: true,
2350            characterData: true
2351        });
2352    }
2353
2354    if (document.readyState !== "loading") {
2355        onDocumentReady();
2356    } else {
2357        document.addEventListener("DOMContentLoaded", onDocumentReady);
2358    }
2359
2360})();
2361
2362/*******************************************************************************
2363 * Copyright 2016 Adobe
2364 *
2365 * Licensed under the Apache License, Version 2.0 (the "License");
2366 * you may not use this file except in compliance with the License.
2367 * You may obtain a copy of the License at
2368 *
2369 *     http://www.apache.org/licenses/LICENSE-2.0
2370 *
2371 * Unless required by applicable law or agreed to in writing, softw
2371are
2372 * distributed under the License is distributed on an "AS IS" BASIS,
2373 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2374 * See the License for the specific language governing permissions and
2375 * limitations under the License.
2376 ******************************************************************************/
2377(function() {
2378    "use strict";
2379
2380    var NS = "cmp";
2381    var IS = "formText";
2382    var IS_DASH = "form-text";
2383
2384    var selectors = {
2385        self: "[data-" + NS + '-is="' + IS + '"]'
2386    };
2387
2388    var properties = {
2389        /**
2390         * A validation message to display if there is a type mismatch between the user input and expected input.
2391         *
2392         * @type {String}
2393         */
2394        constraintMessage: "",
2395        /**
2396         * A validation message to display if no input is supplied, but input is expected for the field.
2397         *
2398         * @type {String}
2399         */
2400        requiredMessage: ""
2401    };
2402
2403    function readData(element) {
2404        var data = element.dataset;
2405        var options = [];
2406        var capitalized = IS;
2407        capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
2408        var reserved = ["is", "hook" + capitalized];
2409
2410        for (var key in data) {
2411            if (Object.prototype.hasOwnProperty.call(data, key)) {
2412                var value = data[key];
2413
2414                if (key.indexOf(NS) === 0) {
2415                    key = key.slice(NS.length);
2416                    key = key.charAt(0).toLowerCase() + key.substring(1);
2417
2418                    if (reserved.indexOf(key) === -1) {
2419                        options[key] = value;
2420                    }
2421                }
2422            }
2423        }
2424
2425        return options;
2426    }
2427
2428    function FormText(config) {
2429        if (config.element) {
2430            // prevents multiple initialization
2431            config.element.removeAttribute("data-" + NS + "-is");
2432        }
2433
2434        this._cacheElements(config.element);
2435        this._setupProperties(config.options);
2436
2437        this._elements.input.addEventListener("invalid", this._onInvalid.bind(this));
2438        this._elements.input.addEventListener("input", this._onInput.bind(this));
2439    }
2440
2441    FormText.prototype._onInvalid = function(event) {
2442        event.target.setCustomValidity("");
2443        if (event.target.validity.typeMismatch) {
2444            if (this._properties.constraintMessage) {
2445                event.target.setCustomValidity(this._properties.constraintMessage);
2446            }
2447        } else if (event.target.validity.valueMissing) {
2448            if (this._properties.requiredMessage) {
2449                event.target.setCustomValidity(this._properties.requiredMessage);
2450            }
2451        }
2452    };
2453
2454    FormText.prototype._onInput = function(event) {
2455        event.target.setCustomValidity("");
2456    };
2457
2458    FormText.prototype._cacheElements = function(wrapper) {
2459        this._elements = {};
2460        this._elements.self = wrapper;
2461        var hooks = this._elements.self.querySelectorAll("[data-" + NS + "-hook-" + IS_DASH + "]");
2462        for (var i = 0; i < hooks.length; i++) {
2463            var hook = hooks[i];
2464            var capitalized = IS;
2465            capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
2466            var key = hook.dataset[NS + "Hook" + capitalized];
2467            this._elements[key] = hook;
2468        }
2469    };
2470
2471    FormText.prototype._setupProperties = function(options) {
2472        this._properties = {};
2473
2474        for (var key in properties) {
2475            if (Object.prototype.hasOwnProperty.call(properties, key)) {
2476                var property = properties[key];
2477                if (options && options[key] != null) {
2478                    if (property && typeof property.transform === "function") {
2479                        this._properties[key] = property.transform(options[key]);
2480                    } else {
2481                        this._properties[key] = options[key];
2482                    }
2483                } else {
2484                    this._properties[key] = properties[key]["default"];
2485                }
2486            }
2487        }
2488    };
2489
2490    function onDocumentReady() {
2491        var elements = document.querySelectorAll(selectors.self);
2492        for (var i = 0; i < elements.length; i++) {
2493            new FormText({ element: elements[i], options: readData(elements[i]) });
2494        }
2495
2496        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
2497        var body = document.querySelector("body");
2498        var observer = new MutationObserver(function(mutations) {
2499            mutations.forEach(function(mutation) {
2500                // needed for IE
2501                var nodesArray = [].slice.call(mutation.addedNodes);
2502                if (nodesArray.length > 0) {
2503                    nodesArray.forEach(function(addedNode) {
2504                        if (addedNode.querySelectorAll) {
2505                            var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self));
2506                            elementsArray.forEach(function(element) {
2507                                new FormText({ element: element, options: readData(element) });
2508                            });
2509                        }
2510                    });
2511                }
2512            });
2513        });
2514
2515        observer.observe(body, {
2516            subtree: true,
2517            childList: true,
2518            characterData: true
2519        });
2520    }
2521
2522    if (document.readyState !== "loading") {
2523        onDocumentReady();
2524    } else {
2525        document.addEventListener("DOMContentLoaded", onDocumentReady);
2526    }
2527
2528})();
2529
2530/*******************************************************************************
2531 * Copyright 2020 Adobe
2532 *
2533 * Licensed under the Apache License, Version 2.0 (the "License");
2534 * you may not use this file except in compliance with the License.
2535 * You may obtain a copy of the License at
2536 *
2537 *     http://www.apache.org/licenses/LICENSE-2.0
2538 *
2539 * Unless required by applicable law or agreed to in writing, software
2540 * distributed under the License is distributed on an "AS IS" BASIS,
2541 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2542 * See the License for the specific language governing permissions and
2543 * limitations under the License.
2544 ******************************************************************************/
2545(function() {
2546    "use strict";
2547
2548    var NS = "cmp";
2549    var IS = "pdfviewer";
2550    var SDK_URL = "https://documentservices.adobe.com/view-sdk/viewer.js";
2551    var SDK_READY_EVENT = "adobe_dc_view_sdk.ready";
2552
2553    var selectors = {
2554        self: "[data-" + NS + '-is="' + IS + '"]',
2555        sdkScript: 'script[src="' + SDK_URL + '"]'
2556    };
2557
2558    function initSDK() {
2559        var sdkIncluded = document.querySelectorAll(selectors.sdkScript).length > 0;
2560        if (!window.adobe_dc_view_sdk && !sdkIncluded) {
2561            var dcv = document.createElement("script");
2562            dcv.type = "text/javascript";
2563            dcv.src = SDK_URL;
2564            document.body.appendChild(dcv);
2565        }
2566    }
2567
2568    function previewPdf(component) {
2569        // prevents multiple initialization
2570        component.removeAttribute("data-" + NS + "-is");
2571
2572        // add the view sdk to the page
2573        initSDK();
2574
2575        // manage the preview
2576        if (component.dataset && component.id) {
2577            if (window.AdobeDC && window.AdobeDC.View) {
2578                dcView(component);
2579            } else {
2580                document.addEventListener(SDK_READY_EVENT, function() {
2581                    dcView(component);
2582                });
2583            }
2584        }
2585    }
2586
2587    function dcView(component) {
2588        var adobeDCView = new window.AdobeDC.View({
2589            clientId: component.dataset.cmpClientId,
2590            divId: component.id + "-content",
2591            reportSuiteId: component.dataset.cmpReportSuiteId
2592        });
2593        adobeDCView.previewFile({
2594            content: { location: { url: component.dataset.cmpDocumentPath } },
2595            metaData: { fileName: component.dataset.cmpDocumentFileName }
2596        }, JSON.parse(component.dataset.cmpViewerConfigJson));
2597    }
2598
2599    /**
2600     * Document ready handler and DOM mutation observers. Initializes Accordion components as necessary.
2601     *
2602     * @private
2603     */
2604    function onDocumentReady() {
2605        var elements = document.querySelectorAll(selectors.self);
2606        for (var i = 0; i < elements.length; i++) {
2607            previewPdf(elements[i]);
2608        }
2609
2610        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
2611        var body = document.querySelector("body");
2612        var observer = new MutationObserver(function(mutations) {
2613            mutations.forEach(function(mutation) {
2614                // needed for IE
2615                var nodesArray = [].slice.call(mutation.addedNodes);
2616                if (nodesArray.length > 0) {
2617                    nodesArray.forEach(function(addedNode) {
2618                        if (addedNode.querySelectorAll) {
2619                            var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self));
2620                            elementsArray.forEach(function(element) {
2621                                previewPdf(element);
2622                            });
2623                        }
2624                    });
2625                }
2626            });
2627        });
2628
2629        observer.observe(body, {
2630            subtree: true,
2631            childList: true,
2632            characterData: true
2633        });
2634
2635    }
2636
2637    if (document.readyState !== "loading") {
2638        onDocumentReady();
2639    } else {
2640        document.addEventListener("DOMContentLoaded", onDocumentReady);
2641    }
2642}());
2643
2644
2645var amtrakProgressBarService = amtrakProgressBarService || {};
2646
2647amtrakProgressBarService.ProgressBar = function() {
2648
2649    "use strict";
2650
2651    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2652                                                Code For AGR Tier Progress Bar 
2653    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2654    
2655    const PROFILE_STORE_ID = "ibsession";
2656    const COMPLETED_BAR_PERCENTAGE = 100;
2657
2658    // tool tip constants
2659    const TOOL_TIP_EL_CLASS = ".am-info-tooltip";
2660    const TOOL_TIP_DATA_ATTR = "data-tooltiptext";
2661
2662    /* This id should match progress bar authoring field */
2663    var PROGRESS_BAR_ID;//= '[id="agr-tier-progress-bar"]';
2664    const PB_MID_TIER_ID = '[id="icon-mid-tier"]';
2665    const PB_TOP_TIER_ID = '[id="icon-top-tier"]';
2666    const PB_RESET_TIER_ID = '[id="icon-reset-tier"]';
2667
2668    const PB_EXEC_TIER_ID = "top-tier";
2669    const CSS_STYLE_HIDE = "hide";
2670
2671    /* Aria Label constants */
2672    const PB_ARIA_ROLE_ATTR = "role";
2673    const PB_ARIA_ROLE = "progressbar";
2674    const PB_ARIA_LABEL_ATTR = "aria-label";
2675    const PB_ARIA_VALUE_ATTR = "aria-valuenow";
2676
2677    const PB_EXEC_TIER_TXT_CLASS = "toptier";
2678    const TOOLTIP_TXT_ATTR = "data-tooltiptext";
2679    const PB_INFO_TOOL_TIP_CLASS = "am-info-tooltip am-tooltip";
2680    const PB_INFO_TOOL_TIP_CMP = "am-tooltip";
2681    const PB_INFO_TOOL_TIP_CONTAINER_CLASS = ".info-text";
2682    const DATA_CMP_IS_ATTR = "data-cmp-is";
2683    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2684        Progress Bar Token Selectors 
2685    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2686    const PB_TKN_COMP_INJT = ".agr-tkn-prgs-bar";
2687    const PB_TKN_USR_TQPS = '.agr-usr-tqps-pts';
2688    const PB_TKN_MAX_PTS = ".max-points";
2689    const PB_TKN_USR_TQP = ".agr-tkn-usr-tqps";
2690    const PB_TKN_MAX_PT = ".agr-tkn-max-pts";
2691    const PB_TKN_REQ_PT = ".agr-tkn-req-pts";
2692    const PB_TKN_FUT_TIER = ".agr-tkn-fut-tier";
2693    const PB_TKN_NXT_YEAR = ".agr-tkn-nxt-year";
2694    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2695        Progress Bar Element Selectors 
2696    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2697    const PB_SEL_TOPTIER_MSG = ".cmp-progressbar_message.toptier";
2698    const PB_SEL_MIDTIER_MSG = ".cmp-progressbar_message.midtier";
2699    const PB_SEL_TOPRSTTIER_MSG = ".cmp-progressbar_message.toptier-reset";
2700    const PB_SEL_BAR = '.cmp-progressbar__bar';
2701    const PB_SEL_WRAPPER = ".cmp-progressbar-wrapper";
2702    const PB_SEL_TQPS_TXT = ".cmp-progressbar__text.tqps_text";
2703    const PB_SEL_BAR_TXT = ".cmp-progressbar__text";
2704    const PB_SEL_BAR_MIN = '.points__min';
2705    const PB_SEL_BAR_MAX = '.points__max';
2706    const PB_SEL_PAR_SE_COMP = '.am-badge';
2707
2708    /* Define Entrace threshold for various AGR tiers*/
2709    const CORE_TIER_ENTRANCE_THRESHOLD = 0;
2710    const PRMR_TIER_ENTRANCE_THRESHOLD = 5000;
2711    const PLUS_TIER_ENTRANCE_THRESHOLD = 10000;
2712    const EXEC_TIER_ENTRANCE_THRESHOLD = 20000;
2713
2714    /* Define values for various AGR tiers data points */
2715    const CORE_TIER_DESCRIPTION = "MEMBER";
2716    const PRMR_TIER_DESCRIPTION = "SELECT";
2717    const PLUS_TIER_DESCRIPTION = "SELECT PLUS";
2718    const EXEC_TIER_DESCRIPTION = "SELECT EXECUTIVE";
2719
2720    const CORE_TIER_CODE = "C";
2721    const CORE_TIER_MIN_BAR_PTS = 0;
2722
2723    /* Progress Bar Elements */
2724    var $topTierMsgEl;
2725    var $midTierMsgEl;
2726    var $resetMsgEl;
2727    var $progressBarContainer;
2728    var $progressBarPlaceholderEl;
2729    var $agrMaxResetTierMsgEl;
2730    var $futureTierTotalPointsEl;
2731    var $progressBarMainEl;
2732    var $tqpsMessageEl;
2733    var $progressBarMainElement;
2734    var $progressBarUIElement;
2735    var $agrMsgFutureTierEl;
2736    var $agrMsgNextYearEl;
2737    var $agrMsgRequiredPointsEl;
2738    var $agrMaxTierPointsEl;
2739    var $tierMinPointsEl;
2740    var $tierMaxPointsEl;
2741    var $topTierCompletedEl;
2742    var $topTierCompletedTqpsEl;
2743    var $progressBarParentContainer;
2744    var currentLocale = "en-us";
2745    var numberFormatObject = new Intl.NumberFormat(currentLocale);
2746
2747    var amtrakProgressBarService = function(PB_ID) {
2748        console.log("setting PB ID");
2749        PROGRESS_BAR_ID = '[id="' + PB_ID + '"]';
2750    };
2751
2752    amtrakProgressBarService.prototype = {
2753        loadProgressBar: function() {
2754            console.log("PB selector set is " + PROGRESS_BAR_ID)
2755            $progressBarContainer = $(PROGRESS_BAR_ID);
2756    
2757            var progressBarHtml = "";
2758    
2759            var accountObj = getAgrAccountInfo();
2760    
2761            if ($progressBarContainer.length > 0) {
2762                setProgressBarElements($progressBarContainer);
2763                buildProgressBar(accountObj);
2764                progressBarHtml = $progressBarContainer.html();
2765                moveContainerToPlaceholder($progressBarPlaceholderEl, $progressBarContainer);
2766            }
2767        }
2768    }
2769     return amtrakProgressBarService;
2770
2771    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2772                                    Code for the AGR Tier Progress Bar 
2773    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2774
2775    /* Function to simulate data points based on user current TQPS */
2776    function updateAccountInfoForTqpsReset(accountInfo) {
2777        var tqps = accountInfo.tierQualifyingPoints;
2778        accountInfo.resetTier = true;
2779        if (tqps < PRMR_TIER_ENTRANCE_THRESHOLD) {
2780            accountInfo.nextTierStatus = PRMR_TIER_DESCRIPTION;
2781            accountInfo.nextTierQualifyingPoints = PRMR_TIER_ENTRANCE_THRESHOLD - tqps;
2782            accountInfo.nextTierTQPEntranceThreshold = CORE_TIER_ENTRANCE_THRESHOLD;
2783            accountInfo.nextTierTQPTriggerValue = PRMR_TIER_ENTRANCE_THRESHOLD;
2784            accountInfo.topTier =  false;
2785        } else if (tqps >= PRMR_TIER_ENTRANCE_THRESHOLD && tqps < PLUS_TIER_ENTRANCE_THRESHOLD) {
2786            accountInfo.nextTierStatus = PLUS_TIER_DESCRIPTION;
2787            accountInfo.nextTierQualifyingPoints = PLUS_TIER_ENTRANCE_THRESHOLD - tqps;
2788            accountInfo.nextTierTQPEntranceThreshold = PRMR_TIER_ENTRANCE_THRESHOLD;
2789            accountInfo.nextTierTQPTriggerValue = PLUS_TIER_ENTRANCE_THRESHOLD;
2790            accountInfo.topTier =  false;
2791        } else if (tqps >= PLUS_TIER_ENTRANCE_THRESHOLD && tqps < EXEC_TIER_ENTRANCE_THRESHOLD) {
2792            accountInfo.nextTierStatus = EXEC_TIER_DESCRIPTION;
2793            accountInfo.nextTierQualifyingPoints = EXEC_TIER_ENTRANCE_THRESHOLD - tqps;
2794            accountInfo.nextTierTQPEntranceThreshold = PLUS_TIER_ENTRANCE_THRESHOLD;
2795            accountInfo.nextTierTQPTriggerValue = EXEC_TIER_ENTRANCE_THRESHOLD;
2796            accountInfo.topTier =  false;
2797        } else {
2798            //used to display Exec Points Req
2799            accountInfo.nextTierTQPEntranceThreshold = EXEC_TIER_ENTRANCE_THRESHOLD;
2800            accountInfo.nextTierStatus = "";
2801            accountInfo.nextTierQualifyingPoints =  "";
2802            accountInfo.nextTierTQPTriggerValue = "";
2803            accountInfo.topTier =  true;
2804        }
2805    }
2806    /* 
2807        Calculate Perentage Completion towards next tier
2808    */
2809    function calculateProgressPercentage(accountInfo) {
2810        var completionPercentage;
2811        if (accountInfo.topTier && accountInfo.tierQualifyingPoints >= EXEC_TIER_ENTRANCE_THRESHOLD) {
2812            completionPercentage = COMPLETED_BAR_PERCENTAGE;
2813        } else {
2814            completionPercentage = (accountInfo.tierQualifyingPoints - accountInfo.nextTierTQPEntranceThreshold) / (accountInfo.nextTierTQPTriggerValue - accountInfo.nextTierTQPEntranceThreshold) * 100
2815        }
2816        return completionPercentage;
2817    }
2818
2819
2820    function tqpsIsReset(userAccountInfoObj) {
2821
2822        var tqps = userAccountInfoObj.tierQualifyingPoints;
2823        var entrance = userAccountInfoObj.nextTierTQPEntranceThreshold;
2824        var trigger = userAccountInfoObj.nextTierTQPTriggerValue;
2825        var topTier = userAccountInfoObj.topTier;
2826
2827        if( userAccountInfoObj !== null && !topTier && (tqps < entrance || tqps >=  trigger) ){
2828            return true;
2829        } else if (topTier && tqps < entrance) {
2830            return true;
2831        } else {
2832            return false;
2833        }
2834
2835    }
2836
2837    /* Construct  Account Info object for progress bar based on user's Tier and TQPS */
2838    function getAgrAccountInfo() {
2839
2840        var agrAccountInfoObj = {};
2841        var userAccountInfoObj = JSON.parse(sessionStorage.getItem(PROFILE_STORE_ID)).profile.data.consumer.myProfile.myAGRAccountInfo;
2842
2843        /* set values applicable to both tiers */
2844        agrAccountInfoObj.tierQualifyingPoints = userAccountInfoObj.tierQualifyingPoints;
2845        agrAccountInfoObj.userTierName = userAccountInfoObj.memberTierDescription;
2846        agrAccountInfoObj.memberTierCode = userAccountInfoObj.memberTierCode;
2847
2848        if (!tqpsIsReset(userAccountInfoObj)) {
2849            agrAccountInfoObj.nextTierStatus = userAccountInfoObj.nextTierStatus;
2850            agrAccountInfoObj.nextTierQualifyingPoints = userAccountInfoObj.nextTierQualifyingPoints;
2851            agrAccountInfoObj.nextTierTQPTriggerValue = userAccountInfoObj.nextTierTQPTriggerValue;
2852            agrAccountInfoObj.nextTierTQPEntranceThreshold = userAccountInfoObj.nextTierTQPEntranceThreshold;
2853            agrAccountInfoObj.topTier = userAccountInfoObj.topTier;
2854            agrAccountInfoObj.resetTier = false;
2855            if ( agrAccountInfoObj.nextTierQualifyingPoints !== (agrAccountInfoObj.nextTierTQPTriggerValue - agrAccountInfoObj.tierQualifyingPoints) ) {
2856                console.log("Updating API next tier qualifying value");
2857                agrAccountInfoObj.nextTierQualifyingPoints = agrAccountInfoObj.nextTierTQPTriggerValue - agrAccountInfoObj.tierQualifyingPoints;
2858            }
2859        } else {
2860            updateAccountInfoForTqpsReset(agrAccountInfoObj);
2861            console.log("API data adjusted for TQPS Reset");
2862        }
2863        console.log("Returning account object")
2864        return agrAccountInfoObj;
2865    }
2866
2867    /* move progress bar component to placehiolder defined inside header placeholder */
2868    function moveContainerToPlaceholder($placeholderElement, $containerElement) {
2869        /* move progress bar to placeholder */
2870        if ($placeholderElement.length > 0 && $containerElement.length > 0) {
2871            $placeholderElement.html($containerElement.html());
2872            /* Remove dulicate progress bar */
2873            $containerElement.remove();
2874        }
2875    }
2876
2877    /* Set all progress bar elements within container context */
2878    function setProgressBarElements($container) {
2879        // progress bar elements
2880        $progressBarParentContainer = $(PROGRESS_BAR_ID);
2881        $progressBarPlaceholderEl = $(PB_TKN_COMP_INJT);
2882        $progressBarMainEl = $(PB_SEL_BAR, $container);
2883        $progressBarMainElement = $(PB_SEL_BAR, $container);
2884        $progressBarUIElement = $(PB_SEL_WRAPPER, $container);
2885        $futureTierTotalPointsEl = $(PB_TKN_MAX_PTS, $container);
2886        $tqpsMessageEl = $(PB_SEL_TQPS_TXT, $container);
2887        $tierMinPointsEl = $(PB_SEL_BAR_MIN, $container);
2888        $tierMaxPointsEl = $(PB_SEL_BAR_MAX, $container);
2889        $topTierCompletedEl = $(PB_SEL_PAR_SE_COMP, $container);
2890        $topTierCompletedTqpsEl = $(PB_TKN_USR_TQPS, $topTierCompletedEl);
2891
2892        // token elements
2893        $agrMsgFutureTierEl = $(PB_TKN_FUT_TIER, $container);
2894         $agrMsgNextYearEl = $(PB_TKN_NXT_YEAR, $container);
2895        $agrMsgRequiredPointsEl = $(PB_TKN_REQ_PT, $container);
2896        $agrMaxTierPointsEl = $(PB_TKN_MAX_PT, $container);
2897        $agrMaxResetTierMsgEl = $(PB_TKN_USR_TQP, $container);
2898        $topTierMsgEl = $(PB_SEL_TOPTIER_MSG, $container);
2899        $resetMsgEl = $(PB_SEL_TOPRSTTIER_MSG, $container);
2900        $midTierMsgEl = $(PB_SEL_MIDTIER_MSG, $container);
2901    }
2902
2903    function completedBoxEligible(accountInfo) {
2904        if (!accountInfo.topTier || accountInfo.tierQualifyingPoints < accountInfo.nextTierTQPEntranceThreshold) {
2905            return false;
2906        } else {
2907            return true;
2908        }
2909    }
2910
2911    function setTierMinMaxValue(userTotalTqps) {
2912        /* Set Empty Min / Max values */
2913        if (userTotalTqps < PRMR_TIER_ENTRANCE_THRESHOLD) {
2914            $tierMinPointsEl.each(function () {
2915                $(this).text(CORE_TIER_ENTRANCE_THRESHOLD);
2916            });
2917            $tierMaxPointsEl.each(function () {
2918                $(this).text(numberFormatObject.format(PRMR_TIER_ENTRANCE_THRESHOLD));
2919            });
2920        }
2921
2922        if (userTotalTqps >= PRMR_TIER_ENTRANCE_THRESHOLD && userTotalTqps < PLUS_TIER_ENTRANCE_THRESHOLD) {
2923            $tierMinPointsEl.each(function () {
2924                $(this).text(PRMR_TIER_ENTRANCE_THRESHOLD);
2925            });
2926            $tierMaxPointsEl.each(function () {
2927                $(this).text(PLUS_TIER_ENTRANCE_THRESHOLD);
2928            });
2929        }
2930        if (userTotalTqps >= PLUS_TIER_ENTRANCE_THRESHOLD && userTotalTqps < EXEC_TIER_ENTRANCE_THRESHOLD) {
2931            $tierMinPointsEl.each(function () {
2932                $(this).text(numberFormatObject.format(PLUS_TIER_ENTRANCE_THRESHOLD));
2933            });
2934            $tierMaxPointsEl.each(function () {
2935                $(this).text(numberFormatObject.format(EXEC_TIER_ENTRANCE_THRESHOLD));
2936            });
2937        }
2938        return;
2939    }
2940
2941    /* Render and Operation for Top TIer */
2942    function setTopTier(accountInfo) {
2943        /* Set Top Tier Thresold Ponits */
2944        $agrMaxTierPointsEl.each(function () {
2945            $(this).text(numberFormatObject.format(accountInfo.nextTierTQPEntranceThreshold));
2946        });
2947        /* Set top tier Class for green color*/
2948        if (!$progressBarParentContainer.hasClass()) {
2949            $progressBarParentContainer.addClass(PB_EXEC_TIER_ID);
2950        }
2951        showTierMessage(accountInfo);
2952
2953        // Set next year in message
2954        var currentYear = new Date().getFullYear();
2955        $agrMsgNextYearEl.text(currentYear+1);
2956
2957        /* Display completed box, if applicable */
2958        if (completedBoxEligible(accountInfo)) {
2959
2960            $topTierCompletedTqpsEl.each(function () {
2961                $(this).text(numberFormatObject.format(accountInfo.tierQualifyingPoints));
2962            });
2963            /* set top tier thresold value */
2964            if ($topTierCompletedEl.hasClass(CSS_STYLE_HIDE)) {
2965                $topTierCompletedEl.removeClass(CSS_STYLE_HIDE);
2966            }
2967        }
2968        
2969        /* Set Empty Min / Max values */
2970        $tierMinPointsEl.each(function () {
2971            $(this).text("");
2972        });
2973
2974        $tierMaxPointsEl.each(function () {
2975            $(this).text("");
2976        });
2977
2978        if (accountInfo.tierQualifyingPoints < accountInfo.nextTierTQPEntranceThreshold) {
2979            var progressPercentage = calculateProgressPercentage(accountInfo);
2980            $progressBarMainEl.css("width", progressPercentage + "%");
2981            setBarElementText($progressBarMainEl, completedPercentage);
2982        } else {
2983            $progressBarMainEl.css("width", "100%");
2984            setBarElementText($progressBarMainEl, "100");
2985        }
2986        return;
2987    }
2988
2989    function setProgressBarAriaLabels($progressBarParentDiv, accountInfo) {
2990        var completedPercentage = calculateProgressPercentage(accountInfo);
2991        $progressBarParentDiv.attr(PB_ARIA_ROLE_ATTR, PB_ARIA_ROLE);
2992        $progressBarParentDiv.attr(PB_ARIA_LABEL_ATTR, completedPercentage + "% completed");
2993        $progressBarParentDiv.attr(PB_ARIA_VALUE_ATTR, completedPercentage);
2994    }
2995
2996    function setCurrentTqpsOnBar($barElement, userAccountInfoObj) {
2997        var barTqpsPointsAttr = $barElement.attr(TOOLTIP_TXT_ATTR);
2998        if (typeof barTqpsPointsAttr !== 'undefined' && barTqpsPointsAttr !== false) {
2999            $barElement.attr(TOOLTIP_TXT_ATTR, numberFormatObject.format(userAccountInfoObj.tierQualifyingPoints));
3000        }
3001        return;
3002    }
3003
3004    function setMidTier(accountInfo) {
3005
3006
3007
3008        /* if Progess Bar is not displayed */
3009        if (!isProgressBarEligible(accountInfo)) {
3010
3011           var nextTier = accountInfo.nextTierStatus;
3012           var nextTierLowerCase = nextTier.toLowerCase();
3013           var hrefLinkTierValue = nextTierLowerCase.replace(/ /g, "-");
3014
3015           var href = $('#'+hrefLinkTierValue+'-benefits-href').text();
3016
3017
3018            // Set tier message 
3019         $agrMsgFutureTierEl.each(function() {
3020            if(href!=''){
3021              var aEl = document.createElement('a');
3022              aEl.href = new URL(href, window.location.href).href;
3023              aEl.textContent = accountInfo.nextTierStatus;
3024              $(this).empty().append(aEl);
3025            }
3026            else{
3027              $(this).text(nextTier);
3028            }
3029         });
3030
3031
3032
3033           // Set next year in message
3034           var currentYear = new Date().getFullYear();
3035           $agrMsgNextYearEl.text(currentYear+1);
3036
3037
3038            //Set Next Tier Qualifying points
3039            $agrMsgRequiredPointsEl.each(function () {
3040                $(this).text(numberFormatObject.format(accountInfo.nextTierQualifyingPoints));
3041            });
3042
3043            removeProgressBarElement($progressBarUIElement);
3044        } else {
3045            // Set Bar Starting Point
3046            $tierMinPointsEl.each(function () {
3047                $(this).text(numberFormatObject.format(accountInfo.nextTierTQPEntranceThreshold));
3048            });
3049
3050            // Set Bar End Point
3051            $tierMaxPointsEl.each(function () {
3052                $(this).text(numberFormatObject.format(accountInfo.nextTierTQPTriggerValue));
3053            });
3054            setCurrentTqpsOnBar($progressBarUIElement, accountInfo);
3055
3056             var nextTier = accountInfo.nextTierStatus;
3057             var nextTierLowerCase = nextTier.toLowerCase();
3058             var hrefLinkTierValue = nextTierLowerCase.replace(/ /g, "-");
3059             var href = $('#'+hrefLinkTierValue+'-benefits-href').text();
3060
3061            // Set Future Tier Token Value
3062            $agrMsgFutureTierEl.each(function() {
3063               if(href!=''){
3064                 var aEl = document.createElement('a');
3065                 aEl.href = new URL(href, window.location.href).href;
3066                 aEl.textContent = accountInfo.nextTierStatus;
3067                 $(this).empty().append(aEl);
3068                }
3069                else{
3070                 $(this).text(nextTier);
3071                }
3072            });
3073
3074            // Set next year in message
3075            var currentYear = new Date().getFullYear();
3076            $agrMsgNextYearEl.text(currentYear+1);
3077
3078            // Set Points Required TOken
3079            $agrMsgRequiredPointsEl.each(function () {
3080                $(this).text(numberFormatObject.format(accountInfo.nextTierQualifyingPoints));
3081            });
3082            // Set TOken for Top Tier
3083            $futureTierTotalPointsEl.each(function () {
3084                $(this).text(numberFormatObject.format(accountInfo.futureTierTotalPoints));
3085            });
3086
3087            var completedPercentage = calculateProgressPercentage(accountInfo);
3088            // Set Progress percentage
3089            $progressBarMainEl.css("width", completedPercentage + "%");
3090            setBarElementText($progressBarMainEl, completedPercentage);
3091        }
3092        showTierMessage(accountInfo);
3093        return;
3094    }
3095
3096    function showTierMessage(accountInfo) {
3097        
3098        /*$topTierMsgEl  $topTierResetMsgEl $midTierMsgEl */
3099
3100        var resetTier = accountInfo.resetTier;
3101
3102        if (accountInfo.topTier) {
3103            $resetMsgEl.addClass(CSS_STYLE_HIDE);
3104            $midTierMsgEl.addClass(CSS_STYLE_HIDE);
3105            $topTierMsgEl.removeClass(CSS_STYLE_HIDE);
3106        } else {
3107            if (isProgressBarEligible(accountInfo)) {
3108                $midTierMsgEl.addClass(CSS_STYLE_HIDE);
3109                $resetMsgEl.removeClass(CSS_STYLE_HIDE);
3110            } else {
3111                $midTierMsgEl.removeClass(CSS_STYLE_HIDE);
3112                $resetMsgEl.addClass(CSS_STYLE_HIDE);
3113            }
3114            if (!$topTierMsgEl.hasClass(CSS_STYLE_HIDE)) {
3115                $topTierMsgEl.addClass(CSS_STYLE_HIDE);
3116            }     
3117        }
3118    }
3119
3120    function setBarElementText($progressBarEl, value) {
3121        $progressBarEl.each(function () {
3122            $(this).text(value)
3123        });
3124        return;
3125    }
3126
3127    /* set all a tag values in the tier message*/
3128    function setInfoToolTipMessage(PB_TIER_ID) {
3129        const $tooltipAnchor = $(PB_TIER_ID);
3130        const $infoText = $(PB_INFO_TOOL_TIP_CONTAINER_CLASS).first();
3131        $tooltipAnchor.each(function () {
3132            $(this).addClass(PB_INFO_TOOL_TIP_CLASS);
3133            $(this).attr(DATA_CMP_IS_ATTR, PB_INFO_TOOL_TIP_CMP);
3134            $(this).attr(TOOLTIP_TXT_ATTR, $infoText.text());
3135        });
3136        // Select the element with class 'am-info-tooltip'
3137        var $tooltipElement = $(TOOL_TIP_EL_CLASS);
3138
3139        // Check if the element exists
3140        if ($tooltipElement.length) {
3141            // Get the data-tooltiptext attribute
3142            var tooltipText = $tooltipElement.attr(TOOLTIP_TXT_ATTR);
3143
3144            // Create a new span element with the class 'tooltiptext tooltip-bottom'
3145            var $tooltipSpan = $('<span>', {
3146                class: 'tooltiptext tooltip-bottom',
3147                html: tooltipText
3148            });
3149
3150            // Append the new span element to the tooltip element
3151            $tooltipElement.html($tooltipSpan);
3152        }
3153
3154        var $progressbarwrapper = $(PB_SEL_WRAPPER);//'.cmp-progressbar-wrapper'
3155        if ($progressbarwrapper.length) {
3156            var tooltipText = $progressbarwrapper.attr(TOOL_TIP_DATA_ATTR);
3157            var $progressbarscore = $('.tooltip-top')
3158            $progressbarscore.html(tooltipText);
3159        }
3160
3161    }
3162
3163    /* set all values in the progress bar */
3164    function buildProgressBar(accountInfo) {
3165        /* Set tier level data*/
3166
3167        var resetTier = accountInfo.resetTier;
3168        
3169        if (accountInfo.topTier) {
3170            setTopTier(accountInfo);
3171            setInfoToolTipMessage(PB_TOP_TIER_ID);
3172        } else {
3173            setMidTier(accountInfo);
3174            if (isProgressBarEligible(accountInfo)) {
3175                setInfoToolTipMessage(PB_RESET_TIER_ID);
3176            } else {
3177                setInfoToolTipMessage(PB_MID_TIER_ID);
3178            }
3179            
3180        }
3181
3182        setProgressBarAriaLabels($progressBarMainEl, accountInfo);
3183    }
3184
3185    function removeProgressBarElement($parentElement) {
3186        $parentElement.remove();
3187    }
3188
3189    /* Determines whether to show Tier Bar */
3190    function isProgressBarEligible(accountInfo) {
3191        if (accountInfo.memberTierCode === CORE_TIER_CODE && accountInfo.tierQualifyingPoints === CORE_TIER_MIN_BAR_PTS) {
3192            return false;
3193        } else {
3194            return true;
3195        }
3196    }
3197
3198}();
3199/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3200                                            End AGR Tier Progress Bar 
3201~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
3202/*******************************************************************************
3203 * Copyright 2022 Adobe
3204 *
3205 * Licensed under the Apache License, Version 2.0 (the "License");
3206 * you may not use this file except in compliance with the License.
3207 * You may obtain a copy of the License at
3208 *
3209 *     http://www.apache.org/licenses/LICENSE-2.0
3210 *
3211 * Unless required by applicable law or agreed to in writing, softw
3211are
3212 * distributed under the License is distributed on an "AS IS" BASIS,
3213 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3214 * See the License for the specific language governing permissions and
3215 * limitations under the License.
3216 ******************************************************************************/
3217(function() {
3218    "use strict";
3219
3220    var linkAccessibilityClass = "cmp-link__screen-reader-only";
3221    var selectors = {
3222        linkAccessibility: "." + linkAccessibilityClass,
3223        linkAccessibilityEnabled: "[data-cmp-link-accessibility-enabled]",
3224        linkAccessibilityText: "[data-cmp-link-accessibility-text]"
3225    };
3226
3227    function getLinkAccessibilityText() {
3228        var linkAccessibilityEnabled = document.querySelectorAll(selectors.linkAccessibilityEnabled);
3229        if (!linkAccessibilityEnabled[0]) {
3230            return;
3231        }
3232        var linkAccessibilityTextElements = document.querySelectorAll(selectors.linkAccessibilityText);
3233        if (!linkAccessibilityTextElements[0]) {
3234            return;
3235        }
3236        return linkAccessibilityTextElements[0].dataset.cmpLinkAccessibilityText;
3237    }
3238
3239    function onDocumentReady() {
3240        var linkAccessibilityText = getLinkAccessibilityText();
3241        if (linkAccessibilityText) {
3242            var linkAccessibilityHtml = "<span class='" + linkAccessibilityClass + "'>(" + linkAccessibilityText + ")</span>";
3243            document.querySelectorAll("a[target='_blank']").forEach(function(link) {
3244                if (!link.querySelector(selectors.linkAccessibility)) {
3245                    link.insertAdjacentHTML("beforeend",  linkAccessibilityHtml);
3246                }
3247            });
3248        }
3249    }
3250
3251    if (document.readyState !== "loading") {
3252        onDocumentReady();
3253    } else {
3254        document.addEventListener("DOMContentLoaded", onDocumentReady);
3255    }
3256
3257}());
3258
3259"use strict";
3260$(document).ready(function() {
3261 
3262         const onTimeElement = document.getElementById('onTimeValue');
3263        const offTimeElement = document.getElementById('offTimeValue');
3264        if (onTimeElement && offTimeElement) {
3265            const onTimeStringValue =  onTimeElement.getAttribute('data-attribute');
3266            const offTimeStringValue = offTimeElement.getAttribute('data-attribute');
3267            const onTimeDate = new Date(onTimeStringValue);
3268            const offTimeDate = new Date(offTimeStringValue);
3269            const currentDate = new Date();
3270            const ScheduleContent = document.getElementById('schedule-content');
3271            const DefaultContent = document.getElementById('default-content');
3272            ScheduleContent.style.display = 'none';
3273            DefaultContent.style.display = 'none';
3274            if (onTimeDate <= currentDate && currentDate <= offTimeDate) {
3275                ScheduleContent.style.display = 'block';
3276                DefaultContent.style.display ='none';
3277            } else {
3278                ScheduleContent.style.display = 'none';
3279                DefaultContent.style.display ='block';
3280            }
3281    }
3282           
3283});
3284$(document).ready(function() {
3285    // Apply event handler to the <a> and <span> elements directly.
3286    $(document).on('contextmenu', '.header-sign-in a, .header-sign-in, .header-sign-up a, .header-sign-up', function(event) {
3287        event.preventDefault();  // Prevent the default right-click menu
3288    });
3289});

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.