PageSourceSearch

https://www.roche4med.co.il/etc.clientlibs/nextgen/core/clientlibs…ase.min.18e27fbb855b3118e90a9550d2097be5.js

js roche4med.co.il collected 2026-10-07 16:54:42 UTC 158,952 bytes, 4,179 lines download raw bytes

1window.isInEditor = function () {
2	try {
3		return window.self !== window.top && window.top.location.href.indexOf('/editor.html') ? true : false;
4	} catch (e) {
5		return false;
6	}
7};
8
9/*******************************************************************************
10 * Copyright 2020 Adobe
11 *
12 * Licensed under the Apache License, Version 2.0 (the "License");
13 * you may not use this file except in compliance with the License.
14 * You may obtain a copy of the License at
15 *
16 *     http://www.apache.org/licenses/LICENSE-2.0
17 *
18 * Unless required by applicable law or agreed to in writing, software
19 * distributed under the License is distributed on an "AS IS" BASIS,
20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 * See the License for the specific language governing permissions and
22 * limitations under the License.
23 ******************************************************************************/
24(function() {
25    "use strict";
26
27    window.CQ = window.CQ || {};
28    window.CQ.CoreComponents = window.CQ.CoreComponents || {};
29    window.CQ.CoreComponents.container = window.CQ.CoreComponents.container || {};
30    window.CQ.CoreComponents.container.utils = {};
31
32    /**
33     * Utilities for Container Components (accordion, tabs)
34     *
35     * @namespace
36     * @alias CQ.CoreComponents.container.utils
37     * @type {{}}
38     */
39    window.CQ.CoreComponents.container.utils = {
40
41        /**
42         * Removes the hash from the URL.
43         */
44        removeUrlHash: function() {
45            history.replaceState(undefined, undefined, " ");
46        },
47
48        /**
49         * Updates the URL hash with the panel ID without scrolling to it.
50         *
51         * @param {Object} component The container component (e.g. Accordion, Carousel, Tabs).
52         * @param {String} itemType The type of the item as defined in the component.
53         * @param {Number} index The index of the container item
54         */
55        updateUrlHash: function(component, itemType, index) {
56            if (component && component._elements && component._elements[itemType] &&
57                component._elements[itemType][index] && component._elements[itemType][index].id) {
58                var ID = component._elements[itemType][index].id;
59                history.replaceState(undefined, undefined, "#" + ID);
60            }
61        },
62
63        /**
64         * Returns the index of the component item (accordion, carousel, tabs) that:
65         * - either corresponds to the deep link in the URL fragment
66         * - or contains the element that corresponds to the deep link in the URL fragment
67         *
68         * @param {Object} component The container component (Accordion, Carousel or Tabs).
69         * @param {String} itemType The type of the item as defined in the component.
70         * @param {String} itemContentType The type of the item content as defined in the component.
71         * @returns {Number} the index within the items array if the item exists, -1 otherwise.
72         */
73        getDeepLinkItemIdx: function(component, itemType, itemContentType) {
74            if (window.location.hash) {
75                var deepLinkId = window.location.hash.substring(1);
76                if (deepLinkId && document.getElementById(deepLinkId) &&
77                    component && component._config && component._config.element &&
78                    component._elements[itemType] &&
79                    component._config.element.querySelector("[id='" + deepLinkId + "']")) {
80                    for (var i = 0; i < component._elements[itemType].length; i++) {
81                        var item = component._elements[itemType][i];
82                        var itemContentContainsId = false;
83                        if (component._elements[itemContentType]) {
84                            var itemContent = component._elements[itemContentType][i];
85                            itemContentContainsId = itemContent && itemContent.querySelector("[id='" + deepLinkId + "']");
86                        }
87                        if (item.id === deepLinkId || itemContentContainsId) {
88                            return i;
89                        }
90                    }
91                }
92                return -1;
93            }
94            return -1;
95        },
96
97        /**
98         * Returns the item of the container component (accordion, carousel, tabs) that:
99         * - either corresponds to the deep link in the URL fragment
100         * - or contains the element that corresponds to the deep link in the URL fragment
101         *
102         * @param {Object} component The Accordion or Tabs component.
103         * @param {String} itemType The type of the item as defined in the component.
104         * @param {String} itemContentType The type of the item content as defined in the component.
105         * @returns {Object} the item if it exists, undefined otherwise.
106         */
107        getDeepLinkItem: function(component, itemType, itemContentType) {
108            var idx = window.CQ.CoreComponents.container.utils.getDeepLinkItemIdx(component, itemType, itemContentType);
109            if (component && component._elements && component._elements[itemType]) {
110                return component._elements[itemType][idx];
111            }
112        },
113
114        /**
115         * Scrolls the browser on page reload (if URI contains URI fragment) to the item of the container component (accordion, tabs)
116           that corresponds to the deep link in the URI fragment.
117         * This method fixes the issue existent with Chrome and related browsers, which are not scrolling on page reload (if URI contains URI fragment)
118           to the element that corresponds to the deep link in the URI fragment.
119         * Small setTimeout is needed, otherwise the scrolling will not work on Chrome.
120         */
121        scrollToAnchor: function() {
122            setTimeout(function() {
123                if (window.location.hash) {
124                    var id = decodeURIComponent(window.location.hash.substring(1));
125                    var anchorElement = document.getElementById(id);
126                    if (anchorElement && anchorElement.offsetTop) {
127                        anchorElement.scrollIntoView();
128                    }
129                }
130            }, 100);
131        }
132    };
133}());
134
135/*******************************************************************************
136 * Copyright 2019 Adobe
137 *
138 * Licensed under the Apache License, Version 2.0 (the "License");
139 * you may not use this file except in compliance with the License.
140 * You may obtain a copy of the License at
141 *
142 *     http://www.apache.org/licenses/LICENSE2.0
143 *
144 * Unless required by applicable law or agreed to in writing, software
145 * distributed under the License is distributed on an "AS IS" BASIS,
146 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
147 * See the License for the specific language governing permissions and
148 * limitations under the License.
149 ******************************************************************************/
150
151/**
152 * Element.matches()
153 * https://developer.mozilla.org/enUS/docs/Web/API/Element/matches#Polyfill
154 */
155if (!Element.prototype.matches) {
156    Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
157}
158
159// eslint-disable-next-line valid-jsdoc
160/**
161 * Element.closest()
162 * https://developer.mozilla.org/enUS/docs/Web/API/Element/closest#Polyfill
163 */
164if (!Element.prototype.closest) {
165    Element.prototype.closest = function(s) {
166        "use strict";
167        var el = this;
168        if (!document.documentElement.contains(el)) {
169            return null;
170        }
171        do {
172            if (el.matches(s)) {
173                return el;
174            }
175            el = el.parentElement || el.parentNode;
176        } while (el !== null && el.nodeType === 1);
177        return null;
178    };
179}
180
181/*******************************************************************************
182 * Copyright 2019 Adobe
183 *
184 * Licensed under the Apache License, Version 2.0 (the "License");
185 * you may not use this file except in compliance with the License.
186 * You may obtain a copy of the License at
187 *
188 *     http://www.apache.org/licenses/LICENSE-2.0
189 *
190 * Unless required by applicable law or agreed to in writing, software
191 * distributed under the License is distributed on an "AS IS" BASIS,
192 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
193 * See the License for the specific language governing permissions and
194 * limitations under the License.
195 ******************************************************************************/
196(function() {
197    "use strict";
198
199    var containerUtils = window.CQ && window.CQ.CoreComponents && window.CQ.CoreComponents.container && window.CQ.CoreComponents.container.utils ? window.CQ.CoreComponents.container.utils : undefined;
200    if (!containerUtils) {
201        // eslint-disable-next-line no-console
202        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.c
202ommons.site.container client library is included on the page.");
203    }
204    var dataLayerEnabled;
205    var dataLayer;
206    var dataLayerName;
207    var delay = 100;
208
209    var NS = "cmp";
210    var IS = "accordion";
211
212    var keyCodes = {
213        ENTER: 13,
214        SPACE: 32,
215        END: 35,
216        HOME: 36,
217        ARROW_LEFT: 37,
218        ARROW_UP: 38,
219        ARROW_RIGHT: 39,
220        ARROW_DOWN: 40
221    };
222
223    var selectors = {
224        self: "[data-" + NS + '-is="' + IS + '"]'
225    };
226
227    var cssClasses = {
228        button: {
229            disabled: "cmp-accordion__button--disabled",
230            expanded: "cmp-accordion__button--expanded"
231        },
232        panel: {
233            hidden: "cmp-accordion__panel--hidden",
234            expanded: "cmp-accordion__panel--expanded"
235        }
236    };
237
238    var dataAttributes = {
239        item: {
240            expanded: "data-cmp-expanded"
241        }
242    };
243
244    var properties = {
245        /**
246         * Determines whether a single accordion item is forced to be expanded at a time.
247         * Expanding one item will collapse all others.
248         *
249         * @memberof Accordion
250         * @type {Boolean}
251         * @default false
252         */
253        "singleExpansion": {
254            "default": false,
255            "transform": function(value) {
256                return !(value === null || typeof value === "undefined");
257            }
258        }
259    };
260
261    /**
262     * Accordion Configuration.
263     *
264     * @typedef {Object} AccordionConfig Represents an Accordion configuration
265     * @property {HTMLElement} element The HTMLElement representing the Accordion
266     * @property {Object} options The Accordion options
267     */
268
269    /**
270     * Accordion.
271     *
272     * @class Accordion
273     * @classdesc An interactive Accordion component for toggling panels of related content
274     * @param {AccordionConfig} config The Accordion configuration
275     */
276    function Accordion(config) {
277        var that = this;
278
279        if (config && config.element) {
280            init(config);
281        }
282
283        /**
284         * Initializes the Accordion.
285         *
286         * @private
287         * @param {AccordionConfig} config The Accordion configuration
288         */
289        function init(config) {
290            that._config = config;
291
292            // prevents multiple initialization
293            config.element.removeAttribute("data-" + NS + "-is");
294
295            setupProperties(config.options);
296            cacheElements(config.element);
297
298            if (that._elements["item"]) {
299                // ensures multiple element types are arrays.
300                that._elements["item"] = Array.isArray(that._elements["item"]) ? that._elements["item"] : [that._elements["item"]];
301                that._elements["button"] = Array.isArray(that._elements["button"]) ? that._elements["button"] : [that._elements["button"]];
302                that._elements["panel"] = Array.isArray(that._elements["panel"]) ? that._elements["panel"] : [that._elements["panel"]];
303
304                if (that._properties.singleExpansion) {
305                    var expandedItems = getExpandedItems();
306                    // multiple expanded items annotated, display the last item open.
307                    if (expandedItems.length > 1) {
308                        toggle(expandedItems.length - 1);
309                    }
310                }
311
312                refreshItems();
313                bindEvents();
314                scrollToDeepLinkIdInAccordion();
315            }
316            if (window.Granite && window.Granite.author && window.Granite.author.MessageChannel) {
317                /*
318                 * Editor message handling:
319                 * - subscribe to "cmp.panelcontainer" message requests sent by the editor frame
320                 * - check that the message data panel container type is correct and that the id (path) matches this specific Accordion component
321                 * - if so, route the "navigate" operation to enact a navigation of the Accordion based on index data
322                 */
323                window.CQ.CoreComponents.MESSAGE_CHANNEL = window.CQ.CoreComponents.MESSAGE_CHANNEL || new window.Granite.author.MessageChannel("cqauthor", window);
324                window.CQ.CoreComponents.MESSAGE_CHANNEL.subscribeRequestMessage("cmp.panelcontainer", function(message) {
325                    if (message.data && message.data.type === "cmp-accordion" && message.data.id === that._elements.self.dataset["cmpPanelcontainerId"]) {
326                        if (message.data.operation === "navigate") {
327                            // switch to single expansion mode when navigating in edit mode.
328                            var singleExpansion = that._properties.singleExpansion;
329                            that._properties.singleExpansion = true;
330                            toggle(message.data.index);
331
332                            // revert to the configured state.
333                            that._properties.singleExpansion = singleExpansion;
334                        }
335                    }
336                });
337            }
338        }
339
340        /**
341         * Displays the panel containing the element that corresponds to the deep link in the URI fragment
342         * and scrolls the browser to this element.
343         */
344        function scrollToDeepLinkIdInAccordion() {
345            if (containerUtils) {
346                var deepLinkItemIdx = containerUtils.getDeepLinkItemIdx(that, "item", "item");
347                if (deepLinkItemIdx > -1) {
348                    var deepLinkItem = that._elements["item"][deepLinkItemIdx];
349                    if (deepLinkItem && !deepLinkItem.hasAttribute(dataAttributes.item.expanded)) {
350                        // if single expansion: close all accordion items
351                        if (that._properties.singleExpansion) {
352                            for (var j = 0; j < that._elements["item"].length; j++) {
353                                if (that._elements["item"][j].hasAttribute(dataAttributes.item.expanded)) {
354                                    setItemExpanded(that._elements["item"][j], false, true);
355                                }
356                            }
357                        }
358                        // expand the accordion item containing the deep link
359                        setItemExpanded(deepLinkItem, true, true);
360                    }
361                    var hashId = window.location.hash.substring(1);
362                    if (hashId) {
363                        var hashItem = document.querySelector("[id='" + hashId + "']");
364                        if (hashItem) {
365                            hashItem.scrollIntoView();
366                        }
367                    }
368                }
369            }
370        }
371
372        /**
373         * Caches the Accordion elements as defined via the {@code data-accordion-hook="ELEMENT_NAME"} markup API.
374         *
375         * @private
376         * @param {HTMLElement} wrapper The Accordion wrapper element
377         */
378        function cacheElements(wrapper) {
379            that._elements = {};
380            that._elements.self = wrapper;
381            var hooks = that._elements.self.querySelectorAll("[data-" + NS + "-hook-" + IS + "]");
382
383            for (var i = 0; i < hooks.length; i++) {
384                var hook = hooks[i];
385                if (hook.closest("." + NS + "-" + IS) === that._elements.self) { // only process own accordion elements
386                    var capitalized = IS;
387                    capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
388                    var key = hook.dataset[NS + "Hook" + capitalized];
389                    if (that._elements[key]) {
390                        if (!Array.isArray(that._elements[key])) {
391                            var tmp = that._elements[key];
392                            that._elements[key] = [tmp];
393                        }
394                        that._elements[key].push(hook);
395                    } else {
396                        that._elements[key] = hook;
397                    }
398                }
399            }
400        }
401
402        /**
403         * Sets up properties for the Accordion based on the passed options.
404         *
405         * @private
406         * @param {Object} options The Accordion options
407         */
408        function setupProperties(options) {
409            that._properties = {};
410
411            for (var key in properties) {
412                if (Object.prototype.hasOwnProperty.call(properties, key)) {
413                    var property = properties[key];
414                    var value = null;
415
416                    if (options && options[key] != null) {
417                        value = options[key];
418
419                        // transform the provided option
420                        if (property && typeof property.transform === "function") {
421                            value = property.transform(value);
422                        }
423                    }
424
425                    if (value === null) {
426                        // value still null, take the property default
427                        value = properties[key]["default"];
428                    }
429
430                    that._properties[key] = value;
431                }
432            }
433        }
434
435        /**
436         * Binds Accordion event handling.
437         *
438         * @private
439         */
440        function bindEvents() {
441            window.addEventListener("hashchange", scrollToDeepLinkIdInAccordion, false);
442            var buttons = that._elements["button"];
443            if (buttons) {
444                for (var i = 0; i < buttons.length; i++) {
445                    (function(index) {
446                        buttons[i].addEventListener("click", function(event) {
447                            toggle(index);
448                            focusButton(index);
449                        });
450                        buttons[i].addEventListener("keydown", function(event) {
451                            onButtonKeyDown(event, index);
452                        });
453                    })(i);
454                }
455            }
456        }
457
458        /**
459         * Handles button keydown events.
460         *
461         * @private
462         * @param {Object} event The keydown event
463         * @param {Number} index The index of the button triggering the event
464         */
465        function onButtonKeyDown(event, index) {
466            var lastIndex = that._elements["button"].length - 1;
467
468            switch (event.keyCode) {
469                case keyCodes.ARROW_LEFT:
470                case keyCodes.ARROW_UP:
471                    event.preventDefault();
472                    if (index > 0) {
473                        focusButton(index - 1);
474                    }
475                    break;
476                case keyCodes.ARROW_RIGHT:
477                case keyCodes.ARROW_DOWN:
478                    event.preventDefault();
479                    if (index < lastIndex) {
480                        focusButton(index + 1);
481                    }
482                    break;
483                case keyCodes.HOME:
484                    event.preventDefault();
485                    focusButton(0);
486                    break;
487                case keyCodes.END:
488                    event.preventDefault();
489                    focusButton(lastIndex);
490                    break;
491                case keyCodes.ENTER:
492                case keyCodes.SPACE:
493                    event.preventDefault();
494                    toggle(index);
495                    focusButton(index);
496                    break;
497                default:
498                    return;
499            }
500        }
501
502        /**
503         * General handler for toggle of an item.
504         *
505         * @private
506         * @param {Number} index The index of the item to toggle
507         */
508        function toggle(index) {
509            var item = that._elements["item"][index];
510            if (item) {
511                if (that._properties.singleExpansion) {
512                    // ensure only a single item is expanded if single expansion is enabled.
513                    for (var i = 0; i < that._elements["item"].length; i++) {
514                        if (that._elements["item"][i] !== item) {
515                            var expanded = getItemExpanded(that._elements["item"][i]);
516                            if (expanded) {
517                                setItemExpanded(that._elements["item"][i], false);
518                            }
519                        }
520                    }
521                }
522                setItemExpanded(item, !getItemExpanded(item));
523
524                if (dataLayerEnabled) {
525                    var accordionId = that._elements.self.id;
526                    var expandedItems = getExpandedItems()
527                        .map(function(item) {
528                            return getDataLayerId(item);
529                        });
530
531                    var uploadPayload = { component: {} };
532                    uploadPayload.component[accordionId] = { shownItems: expandedItems };
533
534                    var removePayload = { component: {} };
535                    removePayload.component[accordionId] = { shownItems: undefined };
536
537                    dataLayer.push(removePayload);
538                    dataLayer.push(uploadPayload);
539                }
540            }
541        }
542
543        /**
544         * Sets an item's expanded state based on the provided flag and refreshes its internals.
545         *
546         * @private
547         * @param {HTMLElement} item The item to mark as expanded, or not expanded
548         * @param {Boolean} expanded true to mark the item expanded, false otherwise
549         * @param {Boolean} keepHash true to keep the hash in the URL, false to update it
550         */
551        function setItemExpanded(item, expanded, keepHash) {
552            if (expanded) {
553                item.setAttribute(dataAttributes.item.expanded, "");
554                var index = that._elements["item"].indexOf(item);
555                if (!keepHash && containerUtils) {
556                    containerUtils.updateUrlHash(that, "item", index);
557                }
558                if (dataLayerEnabled) {
559                    dataLayer.push({
560                        event: "cmp:show",
561                        eventInfo: {
562                            path: "component." + getDataLayerId(item)
563                        }
564                    });
565                }
566
567            } else {
568                item.removeAttribute(dataAttributes.item.expanded);
569                if (!keepHash && containerUtils) {
570                    containerUtils.removeUrlHash();
571                }
572                if (dataLayerEnabled) {
573                    dataLayer.push({
574                        event: "cmp:hide",
575                        eventInfo: {
576                            path: "component." + getDataLayerId(item)
577                        }
578                    });
579                }
580            }
581            refreshItem(item);
582        }
583
584        /**
585         * Gets an item's expanded state.
586         *
587         * @private
588         * @param {HTMLElement} item The item for checking its expanded state
589         * @returns {Boolean} true if the item is expanded, false otherwise
590         */
591        function getItemExpanded(item) {
592            return item && item.dataset && item.dataset["cmpExpanded"] !== undefined;
593        }
594
595        /**
596         * Refreshes an item based on its expanded state.
597         *
598         * @private
599         * @param {HTMLElement} item The item to refresh
600         */
601        function refreshItem(item) {
602            var expanded = getItemExpanded(item);
603            if (expanded) {
604                expandItem(item);
605            } else {
606                collapseItem(item);
607            }
608        }
609
610        /**
611         * Refreshes all items based on their expanded state.
612         *
613         * @private
614         */
615        function refreshItems() {
616            for (var i = 0; i < that._elements["item"].length; i++) {
617                refreshItem(that._elements["item"][i]);
618            }
619        }
620
621        /**
622         * Returns all expanded items.
623         *
624         * @private
625         * @returns {HTMLElement[]} The expanded items
626         */
627        function getExpandedItems() {
628            var expandedItems = [];
629
630            for (var i = 0; i < that._elements["item"].length; i++) {
631                var item = that._elements["item"][i];
632                var expanded = getItemExpanded(item);
633                if (expanded) {
634                    expandedItems.push(item);
635                }
636            }
637
638            return expandedItems;
639        }
640
641        /**
642         * Annotates the item and its internals with
643         * the necessary style and accessibility attributes to indicate it is expanded.
644         *
645         * @private
646         * @param {HTMLElement} item The item to annotate as expanded
647         */
648        function expandItem(item) {
649            var index = that._elements["item"].indexOf(item);
650            if (index > -1) {
651                var button = that._elements["button"][index];
652                var panel = that._elements["panel"][index];
653                button.classList.add(cssClasses.button.expanded);
654                // used to fix some known screen readers issues in reading the correct state of the 'aria-expanded' attribute
655                // e.g. https://bugs.webkit.org/show_bug.cgi?id=210934
656                setTimeout(function() {
657                    button.setAttribute("aria-expanded", true);
658                }, delay);
659                panel.classList.add(cssClasses.panel.expanded);
660                panel.classList.remove(cssClasses.panel.hidden);
661                panel.setAttribute("aria-hidden", false);
662            }
663        }
664
665        /**
666         * Annotates the item and its internals with
667         * the necessary style and accessibility attributes to indicate it is not expanded.
668         *
669         * @private
670         * @param {HTMLElement} item The item to annotate as not expanded
671         */
672        function collapseItem(item) {
673            var index = that._elements["item"].indexOf(item);
674            if (index > -1) {
675                var button = that._elements["button"][index];
676                var panel = that._elements["panel"][index];
677                button.classList.remove(cssClasses.button.expanded);
678                // used to fix some known screen readers issues in reading the correct state of the 'aria-expanded' attribute
679                // e.g. https://bugs.webkit.org/show_bug.cgi?id=210934
680                setTimeout(function() {
681                    button.setAttribute("aria-expanded", false);
682                }, delay);
683                panel.classList.add(cssClasses.panel.hidden);
684                panel.classList.remove(cssClasses.panel.expanded);
685                panel.setAttribute("aria-hidden", true);
686            }
687        }
688
689        /**
690         * Focuses the button at the provided index.
691         *
692         * @private
693         * @param {Number} index The index of the button to focus
694         */
695        function focusButton(index) {
696            var button = that._elements["button"][index];
697            button.focus();
698        }
699    }
700
701    /**
702     * Reads options data from the Accordion wrapper element, defined via {@code data-cmp-*} data attributes.
703     *
704     * @private
705     * @param {HTMLElement} element The Accordion element to read options data from
706     * @returns {Object} The options read from the component data attributes
707     */
708    function readData(element) {
709        var data = element.dataset;
710        var options = [];
711        var capitalized = IS;
712        capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
713        var reserved = ["is", "hook" + capitalized];
714
715        for (var key in data) {
716            if (Object.prototype.hasOwnProperty.call(data, key)) {
717                var value = data[key];
718
719                if (key.indexOf(NS) === 0) {
720                    key = key.slice(NS.length);
721                    key = key.charAt(0).toLowerCase() + key.substring(1);
722
723                    if (reserved.indexOf(key) === -1) {
724                        options[key] = value;
725                    }
726                }
727            }
728        }
729
730        return options;
731    }
732
733    /**
734     * Parses the dataLayer string and returns the ID
735     *
736     * @private
737     * @param {HTMLElement} item the accordion item
738     * @returns {String} dataLayerId or undefined
739     */
740    function getDataLayerId(item) {
741        if (item) {
742            if (item.dataset.cmpDataLayer) {
743                return Object.keys(JSON.parse(item.dataset.cmpDataLayer))[0];
744            } else {
745                return item.id;
746            }
747        }
748        return null;
749    }
750
751    /**
752     * Document ready handler and DOM mutation observers. Initializes Accordion components as necessary.
753     *
754     * @private
755     */
756    function onDocumentReady() {
757        dataLayerEnabled = document.body.hasAttribute("data-cmp-data-layer-enabled");
758        if (dataLayerEnabled) {
759            dataLayerName = document.body.getAttribute("data-cmp-data-layer-name") || "adobeDataLayer";
760            dataLayer = window[dataLayerName] = window[dataLayerName] || [];
761        }
762
763        var elements = document.querySelectorAll(selectors.self);
764        for (var i = 0; i < elements.length; i++) {
765            new Accordion({ element: elements[i], options: readData(elements[i]) });
766        }
767
768        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
769        var body = document.querySelector("body");
770        var observer = new MutationObserver(function(mutations) {
771            mutations.forEach(function(mutation) {
772                // needed for IE
773                var nodesArray = [].slice.call(mutation.addedNodes);
774                if (nodesArray.length > 0) {
775                    nodesArray.forEach(function(addedNode) {
776                        if (addedNode.querySelectorAll) {
777                            var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self));
778                            elementsArray.forEach(function(element) {
779                                new Accordion({ element: element, options: readData(element) });
780                            });
781                        }
782                    });
783                }
784            });
785        });
786
787        observer.observe(body, {
788            subtree: true,
789            childList: true,
790            characterData: true
791        });
792    }
793
794    if (document.readyState !== "loading") {
795        onDocumentReady();
796    } else {
797        document.addEventListener("DOMContentLoaded", onDocumentReady);
798    }
799
800    if (containerUtils) {
801        window.addEventListener("load", containerUtils.scrollToAnchor, false);
802    }
803
804}());
805
806/*******************************************************************************
807 * Copyright 2018 Adobe
808 *
809 * Licensed under the Apache License, Version 2.0 (the "License");
810 * you may not use this file except in compliance with the License.
811 * You may obtain a copy of the License at
812 *
813 *     http://www.apache.org/licenses/LICENSE2.0
814 *
815 * Unless required by applicable law or agreed to in writing, software
816 * distributed under the License is distributed on an "AS IS" BASIS,
817 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
818 * See the License for the specific language governing permissions and
819 * limitations under the License.
820 ******************************************************************************/
821
822/**
823 * Element.matches()
824 * https://developer.mozilla.org/enUS/docs/Web/API/Element/matches#Polyfill
825 */
826if (!Element.prototype.matches) {
827    Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
828}
829
830// eslint-disable-next-line valid-jsdoc
831/**
832 * Element.closest()
833 * https://developer.mozilla.org/enUS/docs/Web/API/Element/closest#Polyfill
834 */
835if (!Element.prototype.closest) {
836    Element.prototype.closest = function(s) {
837        "use strict";
838        var el = this;
839        if (!document.documentElement.contains(el)) {
840            return null;
841        }
842        do {
843            if (el.matches(s)) {
844                return el;
845            }
846            el = el.parentElement || el.parentNode;
847        } while (el !== null && el.nodeType === 1);
848        return null;
849    };
850}
851
852/*******************************************************************************
853 * Copyright 2018 Adobe
854 *
855 * Licensed under the Apache License, Version 2.0 (the "License");
856 * you may not use this file except in compliance with the License.
857 * You may obtain a copy of the License at
858 *
859 *     http://www.apache.org/licenses/LICENSE-2.0
860 *
861 * Unless required by applicable law or agreed to in writing, software
862 * distributed under the License is distributed on an "AS IS" BASIS,
863 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
864 * See the License for the specific language governing permissions and
865 * limitations under the License.
866 ******************************************************************************/
867/* global
868    CQ
869 */
870(function() {
871    "use strict";
872
873    var containerUtils = window.CQ && window.CQ.CoreComponents && window.CQ.CoreComponents.container && window.CQ.CoreComponents.container.utils ? window.CQ.CoreComponents.container.utils : undefined;
874    if (!containerUtils) {
875        // eslint-disable-next-line no-console
876        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.");
877    }
878    var dataLayerEnabled;
879    var dataLayer;
880    var dataLayerName;
881
882    var NS = "cmp";
883    var IS = "tabs";
884
885    var keyCodes = {
886        END: 35,
887        HOME: 36,
888        ARROW_LEFT: 37,
889        ARROW_UP: 38,
890        ARROW_RIGHT: 39,
891        ARROW_DOWN: 40
892    };
893
894    var selectors = {
895        self: "[data-" + NS + '-is="' + IS + '"]',
896        active: {
897            tab: "cmp-tabs__tab--active",
898            tabpanel: "cmp-tabs__tabpanel--active"
899        }
900    };
901
902    /**
903     * Tabs Configuration
904     *
905     * @typedef {Object} TabsConfig Represents a Tabs configuration
906     * @property {HTMLElement} element The HTMLElement representing the Tabs
907     * @property {Object} options The Tabs options
908     */
909
910    /**
911     * Tabs
912     *
913     * @class Tabs
914     * @classdesc An interactive Tabs component for navigating a list of tabs
915     * @param {TabsConfig} config The Tabs configuration
916     */
917    function Tabs(config) {
918        var that = this;
919
920        if (config && config.element) {
921            init(config);
922        }
923
924        /**
925         * Initializes the Tabs
926         *
927         * @private
928         * @param {TabsConfig} config The Tabs configuration
929         */
930        function init(config) {
931            that._config = config;
932
933            // prevents multiple initialization
934            config.element.removeAttribute("data-" + NS + "-is");
935
936            cacheElements(config.element);
937            that._active = getActiveIndex(that._elements["tab"]);
938
939            if (that._elements.tabpanel) {
940                refreshActive();
941                bindEvents();
942                scrollToDeepLinkIdInTabs();
943            }
944
945            if (window.Granite && window.Granite.author && window.Granite.author.MessageChannel) {
946                /*
947                 * Editor message handling:
948                 * - subscribe to "cmp.panelcontainer" message requests sent by the editor frame
949                 * - check that the message data panel container type is correct and that the id (path) matches this specific Tabs component
950                 * - if so, route the "navigate" operation to enact a navigation of the Tabs based on index data
951                 */
952                CQ.CoreComponents.MESSAGE_CHANNEL = CQ.CoreComponents.MESSAGE_CHANNEL || new window.Granite.author.MessageChannel("cqauthor", window);
953                CQ.CoreComponents.MESSAGE_CHANNEL.subscribeRequestMessage("cmp.panelcontainer", function(message) {
954                    if (message.data && message.data.type === "cmp-tabs" && message.data.id === that._elements.self.dataset["cmpPanelcontainerId"]) {
955                        if (message.data.operation === "navigate") {
956                            navigate(message.data.index);
957                        }
958                    }
959                });
960            }
961        }
962
963        /**
964         * Displays the panel containing the element that corresponds to the deep link in the URI fragment
965         * and scrolls the browser to this element.
966         */
967        function scrollToDeepLinkIdInTabs() {
968            if (containerUtils) {
969                var deepLinkItemIdx = containerUtils.getDeepLinkItemIdx(that, "tab", "tabpanel");
970                if (deepLinkItemIdx > -1) {
971                    var deepLinkItem = that._elements["tab"][deepLinkItemIdx];
972                    if (deepLinkItem && that._elements["tab"][that._active].id !== deepLinkItem.id) {
973                        navigateAndFocusTab(deepLinkItemIdx, true);
974                    }
975                    var hashId = window.location.hash.substring(1);
976                    if (hashId) {
977                        var hashItem = document.querySelector("[id='" + hashId + "']");
978                        if (hashItem) {
979                            hashItem.scrollIntoView();
980                        }
981                    }
982                }
983            }
984        }
985
986        /**
987         * Returns the index of the active tab, if no tab is active returns 0
988         *
989         * @param {Array} tabs Tab elements
990         * @returns {Number} Index of the active tab, 0 if none is active
991         */
992        function getActiveIndex(tabs) {
993            if (tabs) {
994                for (var i = 0; i < tabs.length; i++) {
995                    if (tabs[i].classList.contains(selectors.active.tab)) {
996                        return i;
997                    }
998                }
999            }
1000            return 0;
1001        }
1002
1003        /**
1004         * Caches the Tabs elements as defined via the {@code data-tabs-hook="ELEMENT_NAME"} markup API
1005         *
1006         * @private
1007         * @param {HTMLElement} wrapper The Tabs wrapper element
1008         */
1009        function cacheElements(wrapper) {
1010            that._elements = {};
1011            that._elements.self = wrapper;
1012            var hooks = that._elements.self.querySelectorAll("[data-" + NS + "-hook-" + IS + "]");
1013
1014            for (var i = 0; i < hooks.length; i++) {
1015                var hook = hooks[i];
1016                if (hook.closest("." + NS + "-" + IS) === that._elements.self) { // only process own tab elements
1017                    var capitalized = IS;
1018                    capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
1019                    var key = hook.dataset[NS + "Hook" + capitalized];
1020                    if (that._elements[key]) {
1021                        if (!Array.isArray(that._elements[key])) {
1022                            var tmp = that._elements[key];
1023                            that._elements[key] = [tmp];
1024                        }
1025                        that._elements[key].push(hook);
1026                    } else {
1027                        that._elements[key] = hook;
1028                    }
1029                }
1030            }
1031        }
1032
1033        /**
1034         * Binds Tabs event handling
1035         *
1036         * @private
1037         */
1038        function bindEvents() {
1039            window.addEventListener("hashchange", scrollToDeepLinkIdInTabs, false);
1040            var tabs = that._elements["tab"];
1041            if (tabs) {
1042                for (var i = 0; i < tabs.length; i++) {
1043                    (function(index) {
1044                        tabs[i].addEventListener("click", function(event) {
1045                            navigateAndFocusTab(index);
1046                        });
1047                        tabs[i].addEventListener("keydown", function(event) {
1048                            onKeyDown(event);
1049                        });
1050                    })(i);
1051                }
1052            }
1053        }
1054
1055        /**
1056         * Handles tab keydown events
1057         *
1058         * @private
1059         * @param {Object} event The keydown event
1060         */
1061        function onKeyDown(event) {
1062            var index = that._active;
1063            var lastIndex = that._elements["tab"].length - 1;
1064
1065            switch (event.keyCode) {
1066                case keyCodes.ARROW_LEFT:
1067                case keyCodes.ARROW_UP:
1068                    event.preventDefault();
1069                    if (index > 0) {
1070                        navigateAndFocusTab(index - 1);
1071                    }
1072                    break;
1073                case keyCodes.ARROW_RIGHT:
1074                case keyCodes.ARROW_DOWN:
1075                    event.preventDefault();
1076                    if (index < lastIndex) {
1077                        navigateAndFocusTab(index + 1);
1078                    }
1079                    break;
1080                case keyCodes.HOME:
1081                    event.preventDefault();
1082                    navigateAndFocusTab(0);
1083                    break;
1084                case keyCodes.END:
1085                    event.preventDefault();
1086                    navigateAndFocusTab(lastIndex);
1087                    break;
1088                default:
1089                    return;
1090            }
1091        }
1092
1093        /**
1094         * Refreshes the tab markup based on the current {@code Tabs#_active} index
1095         *
1096         * @private
1097         */
1098        function refreshActive() {
1099            var tabpanels = that._elements["tabpanel"];
1100            var tabs = that._elements["tab"];
1101
1102            if (tabpanels) {
1103                if (Array.isArray(tabpanels)) {
1104                    for (var i = 0; i < tabpanels.length; i++) {
1105                        if (i === parseInt(that._active)) {
1106                            tabpanels[i].classList.add(selectors.active.tabpanel);
1107                            tabpanels[i].removeAttribute("aria-hidden");
1108                            tabs[i].classList.add(selectors.active.tab);
1109                            tabs[i].setAttribute("aria-selected", true);
1110                            tabs[i].setAttribute("tabindex", "0");
1111                        } else {
1112                            tabpanels[i].classList.remove(selectors.active.tabpanel);
1113                            tabpanels[i].setAttribute("aria-hidden", true);
1114                            tabs[i].classList.remove(selectors.active.tab);
1115                            tabs[i].setAttribute("aria-selected", false);
1116                            tabs[i].setAttribute("tabindex", "-1");
1117                        }
1118                    }
1119                } else {
1120                    // only one tab
1121                    tabpanels.classList.add(selectors.active.tabpanel);
1122                    tabs.classList.add(selectors.active.tab);
1123                }
1124            }
1125        }
1126
1127        /**
1128         * Focuses the element and prevents scrolling the element into view
1129         *
1130         * @param {HTMLElement} element Element to focus
1131         */
1132        function focusWithoutScroll(element) {
1133            var x = window.scrollX || window.pageXOffset;
1134            var y = window.scrollY || window.pageYOffset;
1135            element.focus();
1136            window.scrollTo(x, y);
1137        }
1138
1139        /**
1140         * Navigates to the tab at the provided index
1141         *
1142         * @private
1143         * @param {Number} index The index of the tab to navigate to
1144         */
1145        function navigate(index) {
1146            that._active = index;
1147            refreshActive();
1148        }
1149
1150        /**
1151         * Navigates to the item at the provided index and ensures the active tab gains focus
1152         *
1153         * @private
1154         * @param {Number} index The index of the item to navigate to
1155         * @param {Boolean} keepHash true to keep the hash in the URL, false to update it
1156         */
1157        function navigateAndFocusTab(index, keepHash) {
1158            var exActive = that._active;
1159            if (!keepHash && containerUtils) {
1160                containerUtils.updateUrlHash(that, "tab", index);
1161            }
1162            navigate(index);
1163            focusWithoutScroll(that._elements["tab"][index]);
1164
1165            if (dataLayerEnabled) {
1166
1167                var activeItem = getDataLayerId(that._elements.tabpanel[index]);
1168                var exActiveItem = getDataLayerId(that._elements.tabpanel[exActive]);
1169
1170                dataLayer.push({
1171                    event: "cmp:show",
1172                    eventInfo: {
1173                        path: "component." + activeItem
1174                    }
1175                });
1176
1177                dataLayer.push({
1178                    event: "cmp:hide",
1179                    eventInfo: {
1180                        path: "component." + exActiveItem
1181                    }
1182                });
1183
1184                var tabsId = that._elements.self.id;
1185                var uploadPayload = { component: {} };
1186                uploadPayload.component[tabsId] = { shownItems: [activeItem] };
1187
1188                var removePayload = { component: {} };
1189                removePayload.component[tabsId] = { shownItems: undefined };
1190
1191                dataLayer.push(removePayload);
1192                dataLayer.push(uploadPayload);
1193            }
1194        }
1195    }
1196
1197    /**
1198     * Reads options data from the Tabs wrapper element, defined via {@code data-cmp-*} data attributes
1199     *
1200     * @private
1201     * @param {HTMLElement} element The Tabs element to read options data from
1202     * @returns {Object} The options read from the component data attributes
1203     */
1204    function readData(element) {
1205        var data = element.dataset;
1206        var options = [];
1207        var capitalized = IS;
1208        capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
1209        var reserved = ["is", "hook" + capitalized];
1210
1211        for (var key in data) {
1212            if (Object.prototype.hasOwnProperty.call(data, key)) {
1213                var value = data[key];
1214
1215                if (key.indexOf(NS) === 0) {
1216                    key = key.slice(NS.length);
1217                    key = key.charAt(0).toLowerCase() + key.substring(1);
1218
1219                    if (reserved.indexOf(key) === -1) {
1220                        options[key] = value;
1221                    }
1222                }
1223            }
1224        }
1225
1226        return options;
1227    }
1228
1229    /**
1230     * Parses the dataLayer string and returns the ID
1231     *
1232     * @private
1233     * @param {HTMLElement} item the accordion item
1234     * @returns {String} dataLayerId or undefined
1235     */
1236    function getDataLayerId(item) {
1237        if (item) {
1238            if (item.dataset.cmpDataLayer) {
1239                return Object.keys(JSON.parse(item.dataset.cmpDataLayer))[0];
1240            } else {
1241                return item.id;
1242            }
1243        }
1244        return null;
1245    }
1246
1247    /**
1248     * Document ready handler and DOM mutation observers. Initializes Tabs components as necessary.
1249     *
1250     * @private
1251     */
1252    function onDocumentReady() {
1253        dataLayerEnabled = document.body.hasAttribute("data-cmp-data-layer-enabled");
1254        if (dataLayerEnabled) {
1255            dataLayerName = document.body.getAttribute("data-cmp-data-layer-name") || "adobeDataLayer";
1256            dataLayer = window[dataLayerName] = window[dataLayerName] || [];
1257        }
1258
1259        var elements = document.querySelectorAll(selectors.self);
1260        for (var i = 0; i < elements.length; i++) {
1261            new Tabs({ element: elements[i], options: readData(elements[i]) });
1262        }
1263
1264        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
1265        var body = document.querySelector("body");
1266        var observer = new MutationObserver(function(mutations) {
1267            mutations.forEach(function(mutation) {
1268                // needed for IE
1269                var nodesArray = [].slice.call(mutation.addedNodes);
1270                if (nodesArray.length > 0) {
1271                    nodesArray.forEach(function(addedNode) {
1272                        if (addedNode.querySelectorAll) {
1273                            var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self));
1274                            elementsArray.forEach(function(element) {
1275                                new Tabs({ element: element, options: readData(element) });
1276                            });
1277                        }
1278                    });
1279                }
1280            });
1281        });
1282
1283        observer.observe(body, {
1284            subtree: true,
1285            childList: true,
1286            characterData: true
1287        });
1288    }
1289
1290    if (document.readyState !== "loading") {
1291        onDocumentReady();
1292    } else {
1293        document.addEventListener("DOMContentLoaded", onDocumentReady);
1294    }
1295
1296    if (containerUtils) {
1297        window.addEventListener("load", containerUtils.scrollToAnchor, false);
1298    }
1299
1300}());
1301
1302/*******************************************************************************
1303 * Copyright 2022 Adobe
1304 *
1305 * Licensed under the Apache License, Version 2.0 (the "License");
1306 * you may not use this file except in compliance with the License.
1307 * You may obtain a copy of the License at
1308 *
1309 *     http://www.apache.org/licenses/LICENSE-2.0
1310 *
1311 * Unless required by applicable law or agreed to in writing, software
1312 * distributed under the License is distributed on an "AS IS" BASIS,
1313 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1314 * See the License for the specific language governing permissions and
1315 * limitations under the License.
1316 ******************************************************************************/
1317(function(document) {
1318    "use strict";
1319
1320    window.CMP = window.CMP || {};
1321    window.CMP.utils = (function() {
1322        var NS = "cmp";
1323
1324        /**
1325         * Reads options data from the Component wrapper element, defined via {@code data-cmp-*} data attributes
1326         *
1327         * @param {HTMLElement} element The component element to read options data from
1328         * @param {String} is The component identifier
1329         * @returns {String[]} The options read from the component data attributes
1330         */
1331        var readData = function(element, is) {
1332            var data = element.dataset;
1333            var options = [];
1334            var capitalized = is;
1335            capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
1336            var reserved = ["is", "hook" + capitalized];
1337
1338            for (var key in data) {
1339                if (Object.prototype.hasOwnProperty.call(data, key)) {
1340                    var value = data[key];
1341
1342                    if (key.indexOf(NS) === 0) {
1343                        key = key.slice(NS.length);
1344                        key = key.charAt(0).toLowerCase() + key.substring(1);
1345
1346                        if (reserved.indexOf(key) === -1) {
1347                            options[key] = value;
1348                        }
1349                    }
1350                }
1351            }
1352            return options;
1353        };
1354
1355        /**
1356         * Set up the final properties of a component by evaluating the transform function or fall back to the default value on demand
1357         * @param {String[]} options the options to transform
1358         * @param {Object} properties object of properties of property functions
1359         * @returns {Object} transformed properties
1360         */
1361        var setupProperties = function(options, properties) {
1362            var transformedProperties = {};
1363
1364            for (var key in properties) {
1365                if (Object.prototype.hasOwnProperty.call(properties, key)) {
1366                    var property = properties[key];
1367                    if (options && options[key] != null) {
1368                        if (property && typeof property.transform === "function") {
1369                            transformedProperties[key] = property.transform(options[key]);
1370                        } else {
1371                            transformedProperties[key] = options[key];
1372                        }
1373                    } else {
1374                        transformedProperties[key] = properties[key]["default"];
1375                    }
1376                }
1377            }
1378            return transformedProperties;
1379        };
1380
1381
1382        return {
1383            readData: readData,
1384            setupProperties: setupProperties
1385        };
1386    }());
1387}(window.document));
1388
1389/*******************************************************************************
1390 * Copyright 2018 Adobe
1391 *
1392 * Licensed under the Apache License, Version 2.0 (the "License");
1393 * you may not use this file except in compliance with the License.
1394 * You may obtain a copy of the License at
1395 *
1396 *     http://www.apache.org/licenses/LICENSE-2.0
1397 *
1398 * Unless required by applicable law or agreed to in writing, software
1399 * distributed under the License is distributed on an "AS IS" BASIS,
1400 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1401 * See the License for the specific language governing permissions and
1402 * limitations under the License.
1403 ******************************************************************************/
1404(function() {
1405    "use strict";
1406
1407    var containerUtils = window.CQ && window.CQ.CoreComponents && window.CQ.CoreComponents.container && window.CQ.CoreComponents.container.utils ? window.CQ.CoreComponents.container.utils : undefined;
1408    if (!containerUtils) {
1409        // eslint-disable-next-line no-console
1410        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.");
1411    }
1412    var dataLayerEnabled;
1413    var dataLayer;
1414    var dataLayerName;
1415
1416    var NS = "cmp";
1417    var IS = "carousel";
1418
1419    var keyCodes = {
1420        SPACE: 32,
1421        END: 35,
1422        HOME: 36,
1423        ARROW_LEFT: 37,
1424        ARROW_UP: 38,
1425        ARROW_RIGHT: 39,
1426        ARROW_DOWN: 40
1427    };
1428
1429    var selectors = {
1430        self: "[data-" + NS + '-is="' + IS + '"]'
1431    };
1432
1433    var properties = {
1434        /**
1435         * Determines whether the Carousel will automatically transition between slides
1436         *
1437         * @memberof Carousel
1438         * @type {Boolean}
1439         * @default false
1440         */
1441        "autoplay": {
1442            "default": false,
1443            "transform": function(value) {
1444                return !(value === null || typeof value === "undefined");
1445            }
1446        },
1447        /**
1448         * Duration (in milliseconds) before automatically transitioning to the next slide
1449         *
1450         * @memberof Carousel
1451         * @type {Number}
1452         * @default 5000
1453         */
1454        "delay": {
1455            "default": 5000,
1456            "transform": function(value) {
1457                value = parseFloat(value);
1458                return !isNaN(value) ? value : null;
1459            }
1460        },
1461        /**
1462         * Determines whether automatic pause on hovering the carousel is disabled
1463         *
1464         * @memberof Carousel
1465         * @type {Boolean}
1466         * @default false
1467         */
1468        "autopauseDisabled": {
1469            "default": false,
1470            "transform": function(value) {
1471                return !(value === null || typeof value === "undefined");
1472            }
1473        }
1474    };
1475
1476    /**
1477     * Carousel Configuration
1478     *
1479     * @typedef {Object} CarouselConfig Represents a Carousel configuration
1480     * @property {HTMLElement} element The HTMLElement representing the Carousel
1481     * @property {*[]} options The Carousel options
1482     */
1483
1484    /**
1485     * Carousel
1486     *
1487     * @class Carousel
1488     * @classdesc An interactive Carousel component for navigating a list of generic items
1489     * @param {CarouselConfig} config The Carousel configuration
1490     */
1491    function Carousel(config) {
1492        var that = this;
1493
1494        if (config && config.element) {
1495            init(config);
1496        }
1497
1498        /**
1499         * Initializes the Carousel
1500         *
1501         * @private
1502         * @param {CarouselConfig} config The Carousel configuration
1503         */
1504        function init(config) {
1505            that._config = config;
1506
1507            // prevents multiple initialization
1508            config.element.removeAttribute("data-" + NS + "-is");
1509
1510            setupProperties(config.options);
1511            cacheElements(config.element);
1512
1513            that._active = 0;
1514            that._paused = false;
1515
1516            if (that._elements.item) {
1517                initializeActive();
1518                bindEvents();
1519                resetAutoplayInterval();
1520                refreshPlayPauseActions();
1521                scrollToDeepLinkIdInCarousel();
1522            }
1523
1524            // TODO: This section is only relevant in edit mode and should move to the editor clientLib
1525            if (window.Granite && window.Granite.author && window.Granite.author.MessageChannel) {
1526                /*
1527                 * Editor message handling:
1528                 * - subscribe to "cmp.panelcontainer" message requests sent by the editor frame
1529                 * - check that the message data panel container type is correct and that the id (path) matches this specific Carousel component
1530                 * - if so, route the "navigate" operation to enact a navigation of the Carousel based on index data
1531                 */
1532                window.CQ = window.CQ || {};
1533                window.CQ.CoreComponents = window.CQ.CoreComponents || {};
1534                window.CQ.CoreComponents.MESSAGE_CHANNEL = window.CQ.CoreComponents.MESSAGE_CHANNEL || new window.Granite.author.MessageChannel("cqauthor", window);
1535                window.CQ.CoreComponents.MESSAGE_CHANNEL.subscribeRequestMessage("cmp.panelcontainer", function(message) {
1536                    if (message.data && message.data.type === "cmp-carousel" && message.data.id === that._elements.self.dataset["cmpPanelcontainerId"]) {
1537                        if (message.data.operation === "navigate") {
1538                            navigate(message.data.index);
1539                        }
1540                    }
1541                });
1542            }
1543        }
1544
1545        /**
1546         * Displays the slide containing the element that corresponds to the deep link in the URI fragment
1547         * and scrolls the browser to this element.
1548         */
1549        function scrollToDeepLinkIdInCarousel() {
1550            if (containerUtils) {
1551                var deepLinkItemIdx = containerUtils.getDeepLinkItemIdx(that, "item", "item");
1552                if (deepLinkItemIdx > -1) {
1553                    var deepLinkItem = that._elements["item"][deepLinkItemIdx];
1554                    if (deepLinkItem && that._elements["item"][that._active].id !== deepLinkItem.id) {
1555                        navigateAndFocusIndicator(deepLinkItemIdx, true);
1556                        // pause the carousel auto-rotation
1557                        pause();
1558                    }
1559                    var hashId = window.location.hash.substring(1);
1560                    if (hashId) {
1561                        var hashItem = document.querySelector("[id='" + hashId + "']");
1562                        if (hashItem) {
1563                            hashItem.scrollIntoView();
1564                        }
1565                    }
1566                }
1567            }
1568        }
1569
1570        /**
1571         * Caches the Carousel elements as defined via the {@code data-carousel-hook="ELEMENT_NAME"} markup API
1572         *
1573         * @private
1574         * @param {HTMLElement} wrapper The Carousel wrapper element
1575         */
1576        function cacheElements(wrapper) {
1577            that._elements = {};
1578            that._elements.self = wrapper;
1579            var hooks = that._elements.self.querySelectorAll("[data-" + NS + "-hook-" + IS + "]");
1580
1581            for (var i = 0; i < hooks.length; i++) {
1582                var hook = hooks[i];
1583                var capitalized = IS;
1584                capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
1585                var key = hook.dataset[NS + "Hook" + capitalized];
1586                if (that._elements[key]) {
1587                    if (!Array.isArray(that._elements[key])) {
1588                        var tmp = that._elements[key];
1589                        that._elements[key] = [tmp];
1590                    }
1591                    that._elements[key].push(hook);
1592                } else {
1593                    that._elements[key] = hook;
1594                }
1595            }
1596        }
1597
1598        /**
1599         * Sets up properties for the Carousel based on the passed options.
1600         *
1601         * @private
1602         * @param {Object} options The Carousel options
1603         */
1604        function setupProperties(options) {
1605            that._properties = {};
1606
1607            for (var key in properties) {
1608                if (Object.prototype.hasOwnProperty.call(properties, key)) {
1609                    var property = properties[key];
1610                    var value = null;
1611
1612                    if (options && options[key] != null) {
1613                        value = options[key];
1614
1615                        // transform the provided option
1616                        if (property && typeof property.transform === "function") {
1617                            value = property.transform(value);
1618                        }
1619                    }
1620
1621                    if (value === null) {
1622                        // value still null, take the property default
1623                        value = properties[key]["default"];
1624                    }
1625
1626                    that._properties[key] = value;
1627                }
1628            }
1629        }
1630
1631        /**
1632         * Binds Carousel event handling
1633         *
1634         * @private
1635         */
1636        function bindEvents() {
1637            window.addEventListener("hashchange", scrollToDeepLinkIdInCarousel, false);
1638            if (that._elements["previous"]) {
1639                that._elements["previous"].addEventListener("click", function() {
1640                    var index = getPreviousIndex();
1641                    navigate(index);
1642                    if (dataLayerEnabled) {
1643                        dataLayer.push({
1644                            event: "cmp:show",
1645                            eventInfo: {
1646                                path: "component." + getDataLayerId(that._elements.item[index])
1647                            }
1648                        });
1649                    }
1650                });
1651            }
1652
1653            if (that._elements["next"]) {
1654                that._elements["next"].addEventListener("click", function() {
1655                    var index = getNextIndex();
1656                    navigate(index);
1657                    if (dataLayerEnabled) {
1658                        dataLayer.push({
1659                            event: "cmp:show",
1660                            eventInfo: {
1661                                path: "component." + getDataLayerId(that._elements.item[index])
1662                            }
1663                        });
1664                    }
1665                });
1666            }
1667
1668            var indicators = that._elements["indicator"];
1669            if (indicators) {
1670                for (var i = 0; i < indicators.length; i++) {
1671                    (function(index) {
1672                        indicators[i].addEventListener("click", function(event) {
1673                            navigateAndFocusIndicator(index);
1674                            // pause the carousel auto-rotation
1675                            pause();
1676                        });
1677                    })(i);
1678                }
1679            }
1680
1681            if (that._elements["pause"]) {
1682                if (that._properties.autoplay) {
1683                    that._elements["pause"].addEventListener("click", onPauseClick);
1684                }
1685            }
1686
1687            if (that._elements["play"]) {
1688                if (that._properties.autoplay) {
1689                    that._elements["play"].addEventListener("click", onPlayClick);
1690                }
1691            }
1692
1693            that._elements.self.addEventListener("keydown", onKeyDown);
1694
1695            if (!that._properties.autopauseDisabled) {
1696                that._elements.self.addEventListener("mouseenter", onMouseEnter);
1697                that._elements.self.addEventListener("mouseleave", onMouseLeave);
1698            }
1699
1700            // for accessibility we pause animation when a element get focused
1701            var items = that._elements["item"];
1702            if (items) {
1703                for (var j = 0; j < items.length; j++) {
1704                    items[j].addEventListener("focusin", onMouseEnter);
1705                    items[j].addEventListener("focusout", onMouseLeave);
1706                }
1707            }
1708        }
1709
1710        /**
1711         * Handles carousel keydown events
1712         *
1713         * @private
1714         * @param {Object} event The keydown event
1715         */
1716        function onKeyDown(event) {
1717            var index = that._active;
1718            var lastIndex = that._elements["indicator"].length - 1;
1719
1720            switch (event.keyCode) {
1721                case keyCodes.ARROW_LEFT:
1722                case keyCodes.ARROW_UP:
1723                    event.preventDefault();
1724                    if (index > 0) {
1725                        navigateAndFocusIndicator(index - 1);
1726                    }
1727                    break;
1728                case keyCodes.ARROW_RIGHT:
1729                case keyCodes.ARROW_DOWN:
1730                    event.preventDefault();
1731                    if (index < lastIndex) {
1732                        navigateAndFocusIndicator(index + 1);
1733                    }
1734                    break;
1735                case keyCodes.HOME:
1736                    event.preventDefault();
1737                    navigateAndFocusIndicator(0);
1738                    break;
1739                case keyCodes.END:
1740                    event.preventDefault();
1741                    navigateAndFocusIndicator(lastIndex);
1742                    break;
1743                case keyCodes.SPACE:
1744                    if (that._properties.autoplay && (event.target !== that._elements["previous"] && event.target !== that._elements["next"])) {
1745                        event.preventDefault();
1746                        if (!that._paused) {
1747                            pause();
1748                        } else {
1749                            play();
1750                        }
1751                    }
1752                    if (event.target === that._elements["pause"]) {
1753                        that._elements["play"].focus();
1754                    }
1755                    if (event.target === that._elements["play"]) {
1756                        that._elements["pause"].focus();
1757                    }
1758                    break;
1759                default:
1760                    return;
1761            }
1762        }
1763
1764        /**
1765         * Handles carousel mouseenter events
1766         *
1767         * @private
1768         * @param {Object} event The mouseenter event
1769         */
1770        function onMouseEnter(event) {
1771            clearAutoplayInterval();
1772        }
1773
1774        /**
1775         * Handles carousel mouseleave events
1776         *
1777         * @private
1778         * @param {Object} event The mouseleave event
1779         */
1780        function onMouseLeave(event) {
1781            resetAutoplayInterval();
1782        }
1783
1784        /**
1785         * Handles pause element click events
1786         *
1787         * @private
1788         * @param {Object} event The click event
1789         */
1790        function onPauseClick(event) {
1791            pause();
1792            that._elements["play"].focus();
1793        }
1794
1795        /**
1796         * Handles play element click events
1797         *
1798         * @private
1799         * @param {Object} event The click event
1800         */
1801        function onPlayClick() {
1802            play();
1803            that._elements["pause"].focus();
1804        }
1805
1806        /**
1807         * Pauses the playing of the Carousel. Sets {@code Carousel#_paused} marker.
1808         * Only relevant when autoplay is enabled
1809         *
1810         * @private
1811         */
1812        function pause() {
1813            that._paused = true;
1814            clearAutoplayInterval();
1815            refreshPlayPauseActions();
1816        }
1817
1818        /**
1819         * Enables the playing of the Carousel. Sets {@code Carousel#_paused} marker.
1820         * Only relevant when autoplay is enabled
1821         *
1822         * @private
1823         */
1824        function play() {
1825            that._paused = false;
1826
1827            // If the Carousel is hovered, don't begin auto transitioning until the next mouse leave event
1828            var hovered = false;
1829            if (that._elements.self.parentElement) {
1830                hovered = that._elements.self.parentElement.querySelector(":hover") === that._elements.self;
1831            }
1832            if (that._properties.autopauseDisabled || !hovered) {
1833                resetAutoplayInterval();
1834            }
1835
1836            refreshPlayPauseActions();
1837        }
1838
1839        /**
1840         * Refreshes the play/pause action markup based on the {@code Carousel#_paused} state
1841         *
1842         * @private
1843         */
1844        function refreshPlayPauseActions() {
1845            setActionDisabled(that._elements["pause"], that._paused);
1846            setActionDisabled(that._elements["play"], !that._paused);
1847        }
1848
1849        /**
1850         * Initialize {@code Carousel#_active} based on the active item of the carousel.
1851         */
1852        function initializeActive() {
1853            var items = that._elements["item"];
1854            if (items && Array.isArray(items)) {
1855                for (var i = 0; i < items.length; i++) {
1856                    if (items[i].classList.contains("cmp-carousel__item--active")) {
1857                        that._active = i;
1858                        break;
1859                    }
1860                }
1861            }
1862        }
1863
1864        /**
1865         * Refreshes the item markup based on the current {@code Carousel#_active} index
1866         *
1867         * @private
1868         */
1869        function refreshActive() {
1870            var items = that._elements["item"];
1871            var indicators = that._elements["indicator"];
1872
1873            if (items) {
1874                if (Array.isArray(items)) {
1875                    for (var i = 0; i < items.length; i++) {
1876                        if (i === parseInt(that._active)) {
1877                            items[i].classList.add("cmp-carousel__item--active");
1878                            items[i].removeAttribute("aria-hidden");
1879                            indicators[i].classList.add("cmp-carousel__indicator--active");
1880                            indicators[i].setAttribute("aria-selected", true);
1881                            indicators[i].setAttribute("tabindex", "0");
1882                        } else {
1883                            items[i].classList.remove("cmp-carousel__item--active");
1884                            items[i].setAttribute("aria-hidden", true);
1885                            indicators[i].classList.remove("cmp-carousel__indicator--active");
1886                            indicators[i].setAttribute("aria-selected", false);
1887                            indicators[i].setAttribute("tabindex", "-1");
1888                        }
1889                    }
1890                } else {
1891                    // only one item
1892                    items.classList.add("cmp-carousel__item--active");
1893                    indicators.classList.add("cmp-carousel__indicator--active");
1894                }
1895            }
1896        }
1897
1898        /**
1899         * Focuses the element and prevents scrolling the element into view
1900         *
1901         * @param {HTMLElement} element Element to focus
1902         */
1903        function focusWithoutScroll(element) {
1904            var x = window.scrollX || window.pageXOffset;
1905            var y = window.scrollY || window.pageYOffset;
1906            element.focus();
1907            window.scrollTo(x, y);
1908        }
1909
1910        /**
1911         * Retrieves the next active index, with looping
1912         *
1913         * @private
1914         * @returns {Number} Index of the next carousel item
1915         */
1916        function getNextIndex() {
1917            return that._active === (that._elements["item"].length - 1) ? 0 : that._active + 1;
1918        }
1919
1920        /**
1921         * Retrieves the previous active index, with looping
1922         *
1923         * @private
1924         * @returns {Number} Index of the previous carousel item
1925         */
1926        function getPreviousIndex() {
1927            return that._active === 0 ? (that._elements["item"].length - 1) : that._active - 1;
1928        }
1929
1930        /**
1931         * Navigates to the item at the provided index
1932         *
1933         * @private
1934         * @param {Number} index The index of the item to navigate to
1935         * @param {Boolean} keepHash true to keep the hash in the URL, false to update it
1936         */
1937        function navigate(index, keepHash) {
1938            if (index < 0 || index > (that._elements["item"].length - 1)) {
1939                return;
1940            }
1941
1942            that._active = index;
1943            refreshActive();
1944
1945            if (!keepHash && containerUtils) {
1946                containerUtils.updateUrlHash(that, "item", index);
1947            }
1948
1949            if (dataLayerEnabled) {
1950                var carouselId = that._elements.self.id;
1951                var activeItem = getDataLayerId(that._elements.item[index]);
1952                var updatePayload = { component: {} };
1953                updatePayload.component[carouselId] = { shownItems: [activeItem] };
1954
1955                var removePayload = { component: {} };
1956                removePayload.component[carouselId] = { shownItems: undefined };
1957
1958                dataLayer.push(removePayload);
1959                dataLayer.push(updatePayload);
1960            }
1961
1962            // reset the autoplay transition interval following navigation, if not already hovering the carousel
1963            if (that._elements.self.parentElement) {
1964                if (that._elements.self.parentElement.querySelector(":hover") !== that._elements.self) {
1965                    resetAutoplayInterval();
1966                }
1967            }
1968        }
1969
1970        /**
1971         * Navigates to the item at the provided index and ensures the active indicator gains focus
1972         *
1973         * @private
1974         * @param {Number} index The index of the item to navigate to
1975         * @param {Boolean} keepHash true to keep the hash in the URL, false to update it
1976         */
1977        function navigateAndFocusIndicator(index, keepHash) {
1978            navigate(index, keepHash);
1979            focusWithoutScroll(that._elements["indicator"][index]);
1980
1981            if (dataLayerEnabled) {
1982                dataLayer.push({
1983                    event: "cmp:show",
1984                    eventInfo: {
1985                        path: "component." + getDataLayerId(that._elements.item[index])
1986                    }
1987                });
1988            }
1989        }
1990
1991        /**
1992         * Starts/resets automatic slide transition interval
1993         *
1994         * @private
1995         */
1996        function resetAutoplayInterval() {
1997            if (that._paused || !that._properties.autoplay) {
1998                return;
1999            }
2000            clearAutoplayInterval();
2001            that._autoplayIntervalId = window.setInterval(function() {
2002                if (document.visibilityState && document.hidden) {
2003                    return;
2004                }
2005                var indicators = that._elements["indicators"];
2006                if (indicators !== document.activeElement && indicators.contains(document.activeElement)) {
2007                    // if an indicator has focus, ensure we switch focus following navigation
2008                    navigateAndFocusIndicator(getNextIndex(), true);
2009                } else {
2010                    navigate(getNextIndex(), true);
2011                }
2012            }, that._properties.delay);
2013        }
2014
2015        /**
2016         * Clears/pauses automatic slide transition interval
2017         *
2018         * @private
2019         */
2020        function clearAutoplayInterval() {
2021            window.clearInterval(that._autoplayIntervalId);
2022            that._autoplayIntervalId = null;
2023        }
2024
2025        /**
2026         * Sets the disabled state for an action and toggles the appropriate CSS classes
2027         *
2028         * @private
2029         * @param {HTMLElement} action Action to disable
2030         * @param {Boolean} [disable] {@code true} to disable, {@code false} to enable
2031         */
2032        function setActionDisabled(action, disable) {
2033            if (!action) {
2034                return;
2035            }
2036            if (disable !== false) {
2037                action.disabled = true;
2038                action.classList.add("cmp-carousel__action--disabled");
2039            } else {
2040                action.disabled = false;
2041                action.classList.remove("cmp-carousel__action--disabled");
2042            }
2043        }
2044    }
2045
2046    /**
2047     * Parses the dataLayer string and returns the ID
2048     *
2049     * @private
2050     * @param {HTMLElement} item the accordion item
2051     * @returns {String} dataLayerId or undefined
2052     */
2053    function getDataLayerId(item) {
2054        if (item) {
2055            if (item.dataset.cmpDataLayer) {
2056                return Object.keys(JSON.parse(item.dataset.cmpDataLayer))[0];
2057            } else {
2058                return item.id;
2059            }
2060        }
2061        return null;
2062    }
2063
2064    /**
2065     * Document ready handler and DOM mutation observers. Initializes Carousel components as necessary.
2066     *
2067     * @private
2068     */
2069    function onDocumentReady() {
2070        dataLayerEnabled = document.body.hasAttribute("data-cmp-data-layer-enabled");
2071        if (dataLayerEnabled) {
2072            dataLayerName = document.body.getAttribute("data-cmp-data-layer-name") || "adobeDataLayer";
2073            dataLayer = window[dataLayerName] = window[dataLayerName] || [];
2074        }
2075
2076        var elements = document.querySelectorAll(selectors.self);
2077        for (var i = 0; i < elements.length; i++) {
2078            new Carousel({ element: elements[i], options: CMP.utils.readData(elements[i], IS) });
2079        }
2080
2081        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
2082        var body = document.querySelector("body");
2083        var observer = new MutationObserver(function(mutations) {
2084            mutations.forEach(function(mutation) {
2085                // needed for IE
2086                var nodesArray = [].slice.call(mutation.addedNodes);
2087                if (nodesArray.length > 0) {
2088                    nodesArray.forEach(function(addedNode) {
2089                        if (addedNode.querySelectorAll) {
2090                            var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self));
2091                            elementsArray.forEach(function(element) {
2092                                new Carousel({ element: element, options: CMP.utils.readData(element, IS) });
2093                            });
2094                        }
2095                    });
2096                }
2097            });
2098        });
2099
2100        observer.observe(body, {
2101            subtree: true,
2102            childList: true,
2103            characterData: true
2104        });
2105    }
2106
2107    var documentReady = document.readyState !== "loading" ? Promise.resolve() : new Promise(function(resolve) {
2108        document.addEventListener("DOMContentLoaded", resolve);
2109    });
2110    Promise.all([documentReady]).then(onDocumentReady);
2111
2112    if (containerUtils) {
2113        window.addEventListener("load", containerUtils.scrollToAnchor, false);
2114    }
2115
2116}());
2117
2118"use strict";(()=>{var d="cmp",g="image",R=0,T="{.width}",v="width={width}",W="{dpr}",w={self:`[data-${d}-is="${g}"]`,image:'[data-cmp-hook-image="image"]',map:'[data-cmp-hook-image="map"]',area:'[data-cmp-hook-image="area"]',viewer:'[data-cmp-hook-image="viewer"]'},O="op_blur=",I="cmp-image__image--is-loading",L="cmp-image__image--smart-crop-loading",y="SmartCrop:Auto",D="data-cmp-svg",p={widths:{default:[],transform:function(n){let e=[];return n.split(",").forEach(function(t){let i=parseFloat(t);isNaN(i)||e.push(i)}),e}},lazy:{default:!1,transform:function(n){return!(n===null||typeof n>"u")}},dmimage:{default:!1,transform:function(n){return!(n===null||typeof n>"u")}},lazythreshold:{default:0,transform:function(n){let e=parseInt(n);return isNaN(e)?R:e}},src:{transform:function(n){return decodeURIComponent(n)}},width:{default:1920,transform:function(n){let e=parseInt(n,10);return isNaN(e)?1920:e}}},E=window.devicePixelRatio||1,A=window.Granite;function S(n){let e=n.dataset,t={},i=g;i=i.charAt(0).toUpperCase()+i.slice(1);let a=["is",`hook${i}`];
2118for(let s in e)if(f(e,s)){let r=e[s];s.indexOf(d)===0&&(s=s.slice(d.length),s=s.charAt(0).toLowerCase()+s.substring(1),a.indexOf(s)===-1&&(t[s]=r))}return t}function f(n,e){return Object.prototype.hasOwnProperty.call(n,e)}function b(n,e){let t;return function(...i){clearTimeout(t),t=setTimeout(()=>n.apply(this,i),e)}}var u=class{smartCrops={};useAssetDelivery=!1;srcUriTemplateWidthVar=T;isImageV3=!1;constructor(e){e&&e.element&&(this.config=e,this.init())}async init(){let{element:e,options:t}=this.config;if(e.removeAttribute(`data-${d}-is`),t.src&&t.src.indexOf(v)>=0&&(this.useAssetDelivery=!0,this.srcUriTemplateWidthVar=v),this.setupProperties(t),this.cacheElements(e),this.isImageV3=this._elements.self.hasAttribute("data-cmp-hook-image")&&this._elements.self.getAttribute("data-cmp-hook-image")==="imageV3",this.isImageV3||this._elements.viewer||(await this.fetchSmartCropData(t),this._elements.container=this._elements.link?this._elements.link:this._elements.self,!this._elements.image))return;A?.author?.util===void 0&&!e.hasAttribute(D)&&this.addLoader(),this._elements.map&&this._elements.image.addEventListener("load",this.onImageMapLoad),this.hasSmartCropRendition&&!this.isSmartCropAuto&&this.removeLoader(),window.addEventListener("resize",this.onWindowResize);let i=["focus","click","transitionend","animationend","scroll","DOMContentLoaded"],a=["load"];i.forEach(r=>{document.addEventListener(r,this.update)}),a.forEach(r=>{window.addEventListener(r,this.update)});let s=this._elements.image.closest(".segmented-component");s&&s.addEventListener("segment-parsed",r=>{let o=r.target;!o||!o.contains(this._elements.image)||this.update()}),this._elements.image.addEventListener("cmp-image-redraw",this.update),this.update(),requestAnimationFrame(()=>{this.setInitialInlineWidth(),this._elements.image.classList.remove("wc-not-ready")})}async fetchSmartCropData(e){if(!(e.src&&f(e,"dmimage")&&e.smartcroprendition===y))return;let i=decodeURIComponent(e.src).split(this.srcUriTemplateWidthVar)[0],a=i.includes("?")?"&":"?",s=`${i}${a}req=set,json`;try{let r=await fetch(s,{method:"GET"});if(!r.ok)throw new Error(`Request failed with status ${r.status}`);let o=await r.text(),m=/^(?:\/\*jsonp\*\/)?\s*([^()]+)\(([\s\S]+),\s*"[0-9]*"\);?$/im,l=/^{[\s\S]*}$/im,_=m.exec(o),c={};if(_){let h=_[2];l.test(h)&&(c=JSON.parse(h))}if(c?.set?.relation?.length>0)for(let h=0;h<c.set.relation.length;h++)this.smartCrops[parseInt(c.set.relation[h].userdata.SmartCropWidth,10)]=`:${c.set.relation[h].userdata.SmartCropDef}`}catch{}}loadImage(){let e=this._properties.widths&&this._properties.widths.length>0||this.hasSmartCrops,t="";if(this.hasSmartCrops){let m=Object.keys(this.smartCrops).map(Number),l=this.getOptimalWidth(m,!1,!0);t=this.smartCrops[l]}else t=e?`${this._properties.dmimage?"":"."}${this.getOptimalWidth(this._properties.widths,!0,!1)}`:"";this.useAssetDelivery&&(t=t!==""?`width=${t.substring(1)}`:"");let i=this._properties.src.replace(this.srcUriTemplateWidthVar,t);i=i.replace(W,String(E));let a=this._elements.image,s=a.getAttribute("src"),r=s?decodeURIComponent(s):null;a.removeEventListener("load",this.onImageLoad),a.addEventListener("load",this.onImageLoad),a.removeEventListener("error",this.onImageError),a.addEventListener("error",this.onImageError);let o=!1;if(i!==r)if(r===null||r&&r.includes(O))a.setAttribute("src",i),o=!0;else{let m=this._properties.src.split(this.srcUriTemplateWidthVar),l=r.startsWith(m[0]);l&&m.length>1&&(l=r.endsWith(m[m.length-1])),l&&(a.setAttribute("src",i),o=!0,e||window.removeEventListener("scroll",this.update))}(this.isImageLoaded(i)||!o)&&this.onImageLoad()}isImageLoaded(e){let t=this._elements.image.getAttribute("src"),i=t?decodeURIComponent(t):null;return this._elements.image.complete&&this._elements.image.naturalWidth>0&&i===e}getOptimalWidth(e,t,i){let a=this._elements.self,s=a.clientWidth;for(;s===0&&a.parentNode;)a=a.parentNode,s=a.clientWidth;let o=s*(t?E:1),m=e.length,l=0;for(;l<m-1&&e[l]<o;)l++;return this._properties.dmimage&&!i&&e[l]>this._properties.width?this._properties.width:e[l]}addLoader(){this._elements.image.classList.add(I),this.hasSmartCrops&&this._elements.image.classList.add(L)}removeLoader=()=>{this._elements.image.classList.remove(I),this._elements.image.classList.remove(L)};onImageLoad=()=>
2118{let{naturalWidth:e,naturalHeight:t}=this._elements.image;this.hasSmartCropRendition||this.updateImageDimensions(e,t),this.isImageHasNaturalDimensions&&(this._elements.image.style.removeProperty("width"),this._elements.image.style.removeProperty("--cmp-image-loading-width"),this.removeLoader(),this._elements.image.removeEventListener("load",this.onImageLoad),this._elements.image.removeEventListener("error",this.onImageError))};onImageError=()=>{this.removeLoader(),this._elements.image.removeEventListener("error",this.onImageError),this._elements.image.removeEventListener("load",this.onImageLoad)};updateImageDimensions(e,t){e!==void 0&&e<=0||t!==void 0&&t<=0||(e?this._elements.image?.setAttribute("width",String(e)):this._elements.image?.removeAttribute("width"),t?this._elements.image?.setAttribute("height",String(t)):this._elements.image?.removeAttribute("height"))}setInitialInlineWidth(){this.isImageWidthAuto&&this._properties.width&&!this._elements.image.style.width&&this._elements.image.style.setProperty("--cmp-image-loading-width",`${this._properties.width}px`)}isLazyVisible(){if(this._elements.container?.offsetParent===null)return!1;let e=window.pageYOffset,t=e+document.documentElement.clientHeight,i=(this._elements.container?.getBoundingClientRect().top||0)+e;return i+(this._elements.container?.clientHeight||0)>=e-this._properties.lazythreshold&&i<=t+this._properties.lazythreshold}resizeAreas(){if(this._elements.areas&&this._elements.areas.length>0)for(let e=0;e<this._elements.areas.length;e++){let t=this._elements.image.width,i=this._elements.image.height;if(t&&i){let a=this._elements.areas[e].dataset.cmpRelcoords;if(a){let s=a.split(",").map(Number),r=new Array(s.length);for(let o=0;o<r.length;o++)o%2===0?r[o]=s[o]*t:r[o]=s[o]*i;this._elements.areas[e].coords=r.join(",")}}}}cacheElements(e){this._elements={},this._elements.self=e;let t=this._elements.self.querySelectorAll(`[data-${d}-hook-${g}]`);for(let i=0;i<t.length;i++){let a=t[i],s=g;s=s.charAt(0).toUpperCase()+s.slice(1);let r=a.dataset[`${d}Hook${s}`];this._elements[r]=a}}setupProperties(e){this._properties={};for(let t in p)if(f(p,t)){let i=p[t];e&&e[t]!=null?i&&typeof i.transform=="function"?this._properties[t]=i.transform(e[t]):this._properties[t]=e[t]:this._properties[t]=p[t].default}}onWindowResize=b(()=>{this.update(),this.resizeAreas()},100);onImageMapLoad=b(()=>{this.resizeAreas()},100);update=()=>{A?.author?.util===void 0&&this._properties.lazy?this.isLazyVisible()&&this.loadImage():this.loadImage()};get isImageHasNaturalDimensions(){return this._elements.image.naturalWidth>0&&this._elements.image.naturalHeight>0}get isImageWidthAuto(){return this._elements.self.classList.contains("cmp-image--do-not-stretch")||this._elements.image.closest("ehe-banner-product")!==null||window.getComputedStyle(this._elements.image).width==="auto"}get hasSmartCrops(){return Object.keys(this.smartCrops).length>0}get hasSmartCropRendition(){return!!this.config.options.smartcroprendition}get isSmartCropAuto(){return this.config.options.smartcroprendition===y}};function C(){let n=document.querySelectorAll(w.self);for(let i=0;i<n.length;i++)new u({element:n[i],options:S(n[i])});let e=document.querySelector("body");new MutationObserver(function(i){for(let a of i)Array.from(a.addedNodes).filter(r=>r instanceof Element).forEach(function(r){Array.from(r.querySelectorAll(w.self)).forEac
2118h(function(m){new u({element:m,options:S(m)})})})}).observe(e,{subtree:!0,childList:!0,characterData:!0})}document.readyState!=="loading"?C():document.addEventListener("DOMContentLoaded",C);})();
2119
2120/*******************************************************************************
2121 * Copyright 2017 Adobe
2122 *
2123 * Licensed under the Apache License, Version 2.0 (the "License");
2124 * you may not use this file except in compliance with the License.
2125 * You may obtain a copy of the License at
2126 *
2127 *     http://www.apache.org/licenses/LICENSE-2.0
2128 *
2129 * Unless required by applicable law or agreed to in writing, software
2130 * distributed under the License is distributed on an "AS IS" BASIS,
2131 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2132 * See the License for the specific language governing permissions and
2133 * limitations under the License.
2134 ******************************************************************************/
2135(function() {
2136    "use strict";
2137
2138    var NS = "cmp";
2139    var IS = "search";
2140
2141    var DELAY = 300; // time before fetching new results when the user is typing a search string
2142    var LOADING_DISPLAY_DELAY = 300; // minimum time during which the loading indicator is displayed
2143    var PARAM_RESULTS_OFFSET = "resultsOffset";
2144
2145    var keyCodes = {
2146        TAB: 9,
2147        ENTER: 13,
2148        ESCAPE: 27,
2149        ARROW_UP: 38,
2150        ARROW_DOWN: 40
2151    };
2152
2153    var selectors = {
2154        self: "[data-" + NS + '-is="' + IS + '"]',
2155        item: {
2156            self: "[data-" + NS + "-hook-" + IS + '="item"]',
2157            title: "[data-" + NS + "-hook-" + IS + '="itemTitle"]',
2158            focused: "." + NS + "-search__item--is-focused"
2159        }
2160    };
2161
2162    var properties = {
2163        /**
2164         * The minimum required length of the search term before results are fetched.
2165         *
2166         * @memberof Search
2167         * @type {Number}
2168         * @default 3
2169         */
2170        minLength: {
2171            "default": 3,
2172            transform: function(value) {
2173                value = parseFloat(value);
2174                return isNaN(value) ? null : value;
2175            }
2176        },
2177        /**
2178         * The maximal number of results fetched by a search request.
2179         *
2180         * @memberof Search
2181         * @type {Number}
2182         * @default 10
2183         */
2184        resultsSize: {
2185            "default": 10,
2186            transform: function(value) {
2187                value = parseFloat(value);
2188                return isNaN(value) ? null : value;
2189            }
2190        }
2191    };
2192
2193    var idCount = 0;
2194
2195    function readData(element) {
2196        var data = element.dataset;
2197        var options = [];
2198        var capitalized = IS;
2199        capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
2200        var reserved = ["is", "hook" + capitalized];
2201
2202        for (var key in data) {
2203            if (Object.prototype.hasOwnProperty.call(data, key)) {
2204                var value = data[key];
2205
2206                if (key.indexOf(NS) === 0) {
2207                    key = key.slice(NS.length);
2208                    key = key.charAt(0).toLowerCase() + key.substring(1);
2209
2210                    if (reserved.indexOf(key) === -1) {
2211                        options[key] = value;
2212                    }
2213                }
2214            }
2215        }
2216
2217        return options;
2218    }
2219
2220    function toggleShow(element, show) {
2221        if (element) {
2222            if (show !== false) {
2223                element.style.display = "block";
2224                element.setAttribute("aria-hidden", false);
2225            } else {
2226                element.style.display = "none";
2227                element.setAttribute("aria-hidden", true);
2228            }
2229        }
2230    }
2231
2232    function serialize(form) {
2233        var query = [];
2234        if (form && form.elements) {
2235            for (var i = 0; i < form.elements.length; i++) {
2236                var node = form.elements[i];
2237                if (!node.disabled && node.name) {
2238                    var param = [node.name, encodeURIComponent(node.value)];
2239                    query.push(param.join("="));
2240                }
2241            }
2242        }
2243        return query.join("&");
2244    }
2245
2246    function mark(node, regex) {
2247        if (!node || !regex) {
2248            return;
2249        }
2250
2251        // text nodes
2252        if (node.nodeType === 3) {
2253            var nodeValue = node.nodeValue;
2254            var match = regex.exec(nodeValue);
2255
2256            if (nodeValue && match) {
2257                var element = document.createElement("mark");
2258                element.className = NS + "-search__item-mark";
2259                element.appendChild(document.createTextNode(match[0]));
2260
2261                var after = node.splitText(match.index);
2262                after.nodeValue = after.nodeValue.substring(match[0].length);
2263                node.parentNode.insertBefore(element, after);
2264            }
2265        } else if (node.hasChildNodes()) {
2266            for (var i = 0; i < node.childNodes.length; i++) {
2267                // recurse
2268                mark(node.childNodes[i], regex);
2269            }
2270        }
2271    }
2272
2273    function Search(config) {
2274        if (config.element) {
2275            // prevents multiple initialization
2276            config.element.removeAttribute("data-" + NS + "-is");
2277        }
2278
2279        this._cacheElements(config.element);
2280        this._setupProperties(config.options);
2281
2282        this._action = this._elements.form.getAttribute("action");
2283        this._resultsOffset = 0;
2284        this._hasMoreResults = true;
2285
2286        this._elements.input.addEventListener("input", this._onInput.bind(this));
2287        this._elements.input.addEventListener("focus", this._onInput.bind(this));
2288        this._elements.input.addEventListener("keydown", this._onKeydown.bind(this));
2289        this._elements.clear.addEventListener("click", this._onClearClick.bind(this));
2290        document.addEventListener("click", this._onDocumentClick.bind(this));
2291        this._elements.results.addEventListener("scroll", this._onScroll.bind(this));
2292
2293        this._makeAccessible();
2294    }
2295
2296    Search.prototype._displayResults = function() {
2297        if (this._elements.input.value.length === 0) {
2298            toggleShow(this._elements.clear, false);
2299            this._cancelResults();
2300        } else if (this._elements.input.value.length < this._properties.minLength) {
2301            toggleShow(this._elements.clear, true);
2302        } else {
2303            this._updateResults();
2304            toggleShow(this._elements.clear, true);
2305        }
2306    };
2307
2308    Search.prototype._onScroll = function(event) {
2309        // fetch new results when the results to be scrolled down are less than the visible results
2310        if (this._elements.results.scrollTop + 2 * this._elements.results.clientHeight >= this._elements.results.scrollHeight) {
2311            this._resultsOffset += this._properties.resultsSize;
2312            this._displayResults();
2313        }
2314    };
2315
2316    Search.prototype._onInput = function(event) {
2317        var self = this;
2318        self._cancelResults();
2319        // start searching when the search term reaches the minimum length
2320        this._timeout = setTimeout(function() {
2321            self._displayResults();
2322        }, DELAY);
2323    };
2324
2325    Search.prototype._onKeydown = function(event) {
2326        var self = this;
2327
2328        switch (event.keyCode) {
2329            case keyCodes.TAB:
2330                if (self._resultsOpen()) {
2331                    toggleShow(self._elements.results, false);
2332                    self._elements.input.setAttribute("aria-expanded", "false");
2333                }
2334                break;
2335            case keyCodes.ENTER:
2336                event.preventDefault();
2337                if (self._resultsOpen()) {
2338                    var focused = self._elements.results.querySelector(selectors.item.focused);
2339                    if (focused) {
2340                        focused.click();
2341                    }
2342                }
2343                break;
2344            case keyCodes.ESCAPE:
2345                self._cancelResults();
2346                break;
2347            case keyCodes.ARROW_UP:
2348                if (self._resultsOpen()) {
2349                    event.preventDefault();
2350                    self._stepResultFocus(true);
2351                }
2352                break;
2353            case keyCodes.ARROW_DOWN:
2354                if (self._resultsOpen()) {
2355                    event.preventDefault();
2356                    self._stepResultFocus();
2357                } else {
2358                    // test the input and if necessary fetch and display the results
2359                    self._onInput();
2360                }
2361                break;
2362            default:
2363                return;
2364        }
2365    };
2366
2367    Search.prototype._onClearClick = function(event) {
2368        event.preventDefault();
2369        this._elements.input.value = "";
2370        toggleShow(this._elements.clear, false);
2371        toggleShow(this._elements.results, false);
2372        this._elements.input.setAttribute("aria-expanded", "false");
2373    };
2374
2375    Search.prototype._onDocumentClick = function(event) {
2376        var inputContainsTarget =  this._elements.input.contains(event.target);
2377        var resultsContainTarget = this._elements.results.contains(event.target);
2378
2379        if (!(inputContainsTarget || resultsContainTarget)) {
2380            toggleShow(this._elements.results, false);
2381            this._elements.input.setAttribute("aria-expanded", "false");
2382        }
2383    };
2384
2385    Search.prototype._resultsOpen = function() {
2386        return this._elements.results.style.display !== "none";
2387    };
2388
2389    Search.prototype._makeAccessible = function() {
2390        var id = NS + "-search-results-" + idCount;
2391        this._elements.input.setAttribute("aria-owns", id);
2392        this._elements.results.id = id;
2393        idCount++;
2394    };
2395
2396    Search.prototype._generateItems = function(data, results) {
2397        var self = this;
2398
2399        data.forEach(function(item) {
2400            var el = document.createElement("span");
2401            el.innerHTML = self._elements.itemTemplate.innerHTML;
2402            el.querySelectorAll(selectors.item.title)[0].appendChild(document.createTextNode(item.title));
2403            el.querySelectorAll(selectors.item.self)[0].setAttribute("href", self._safeHref(item.url));
2404            results.innerHTML += el.innerHTML;
2405        });
2406    };
2407
2408    Search.prototype._safeHref = function(href) {
2409        var a = document.createElement("a");
2410        a.href = href;
2411        return a.pathname;
2412    };
2413
2414    Search.prototype._markResults = function() {
2415        var nodeList = this._elements.results.querySelectorAll(selectors.item.self);
2416        var escapedTerm = this._elements.input.value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
2417        var regex = new RegExp("(" + escapedTerm + ")", "gi");
2418
2419        for (var i = this._resultsOffset - 1; i < nodeList.length; ++i) {
2420            var result = nodeList[i];
2421            mark(result, regex);
2422        }
2423    };
2424
2425    Search.prototype._stepResultFocus = function(reverse) {
2426        var results = this._elements.results.querySelectorAll(selectors.item.self);
2427        var focused = this._elements.results.querySelector(selectors.item.focused);
2428        var newFocused;
2429        var index = Array.prototype.indexOf.call(results, focused);
2430        var focusedCssClass = NS + "-search__item--is-focused";
2431
2432        if (results.length > 0) {
2433
2434            if (!reverse) {
2435                // highlight the next result
2436                if (index < 0) {
2437                    results[0].classList.add(focusedCssClass);
2438                    results[0].setAttribute("aria-selected", "true");
2439                } else if (index + 1 < results.length) {
2440                    results[index].classList.remove(focusedCssClass);
2441                    results[index].setAttribute("aria-selected", "false");
2442                    results[index + 1].classList.add(focusedCssClass);
2443                    results[index + 1].setAttribute("aria-selected", "true");
2444                }
2445
2446                // if the last visible result is partially hidden, scroll up until it's completely visible
2447                newFocused = this._elements.results.querySelector(selectors.item.focused);
2448                if (newFocused) {
2449                    var bottomHiddenHeight = newFocused.offsetTop + newFocused.offsetHeight - this._elements.results.scrollTop - this._elements.results.clientHeight;
2450                    if (bottomHiddenHeight > 0) {
2451                        this._elements.results.scrollTop += bottomHiddenHeight;
2452                    } else {
2453                        this._onScroll();
2454                    }
2455                }
2456
2457            } else {
2458                // highlight the previous result
2459                if (index >= 1) {
2460                    results[index].classList.remove(focusedCssClass);
2461                    results[index].setAttribute("aria-selected", "false");
2462                    results[index - 1].classList.add(focusedCssClass);
2463                    results[index - 1].setAttribute("aria-selected", "true");
2464                }
2465
2466                // if the first visible result is partially hidden, scroll down until it's completely visible
2467                newFocused = this._elements.results.querySelector(selectors.item.focused);
2468                if (newFocused) {
2469                    var topHiddenHeight = this._elements.results.scrollTop - newFocused.offsetTop;
2470                    if (topHiddenHeight > 0) {
2471                        this._elements.results.scrollTop -= topHiddenHeight;
2472                    }
2473                }
2474            }
2475        }
2476    };
2477
2478    Search.prototype._updateResults = function() {
2479        var self = this;
2480        if (self._hasMoreResults) {
2481            var request = new XMLHttpRequest();
2482            var url = self._action + "?" + serialize(self._elements.form) + "&" + PARAM_RESULTS_OFFSET + "=" + self._resultsOffset;
2483
2484            request.open("GET", url, true);
2485            request.onload = function() {
2486                // when the results are loaded: hide the loading indicator and display the search icon after a minimum period
2487                setTimeout(function() {
2488                    toggleShow(self._elements.loadingIndicator, false);
2489                    toggleShow(self._elements.icon, true);
2490                }, LOADING_DISPLAY_DELAY);
2491                if (request.status >= 200 && request.status < 400) {
2492                    // success status
2493                    var data = JSON.parse(request.responseText);
2494                    if (data.length > 0) {
2495                        self._generateItems(data, self._elements.results);
2496                        self._markResults();
2497                        toggleShow(self._elements.results, true);
2498                        self._elements.input.setAttribute("aria-expanded", "true");
2499                    } else {
2500                        self._hasMoreResults = false;
2501                    }
2502                    // the total number of results is not a multiple of the fetched results:
2503                    // -> we reached the end of the query
2504                    if (self._elements.results.querySelectorAll(selectors.item.self).length % self._properties.resultsSize > 0) {
2505                        self._hasMoreResults = false;
2506                    }
2507                } else {
2508                    // error status
2509                }
2510            };
2511            // when the results are loading: display the loading indicator and hide the search icon
2512            toggleShow(self._elements.loadingIndicator, true);
2513            toggleShow(self._elements.icon, false);
2514            request.send();
2515        }
2516    };
2517
2518    Search.prototype._cancelResults = function() {
2519        clearTimeout(this._timeout);
2520        this._elements.results.scrollTop = 0;
2521        this._resultsOffset = 0;
2522        this._hasMoreResults = true;
2523        this._elements.results.innerHTML = "";
2524        this._elements.input.setAttribute("aria-expanded", "false");
2525    };
2526
2527    Search.prototype._cacheElements = function(wrapper) {
2528        this._elements = {};
2529        this._elements.self = wrapper;
2530        var hooks = this._elements.self.querySelectorAll("[data-" + NS + "-hook-" + IS + "]");
2531
2532        for (var i = 0; i < hooks.length; i++) {
2533            var hook = hooks[i];
2534            var capitalized = IS;
2535            capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
2536            var key = hook.dataset[NS + "Hook" + capitalized];
2537            this._elements[key] = hook;
2538        }
2539    };
2540
2541    Search.prototype._setupProperties = function(options) {
2542        this._properties = {};
2543
2544        for (var key in properties) {
2545            if (Object.prototype.hasOwnProperty.call(properties, key)) {
2546                var property = properties[key];
2547                if (options && options[key] != null) {
2548                    if (property && typeof property.transform === "function") {
2549                        this._properties[key] = property.transform(options[key]);
2550                    } else {
2551                        this._properties[key] = options[key];
2552                    }
2553                } else {
2554                    this._properties[key] = properties[key]["default"];
2555                }
2556            }
2557        }
2558    };
2559
2560    function onDocumentReady() {
2561        var elements = document.querySelectorAll(selectors.self);
2562        for (var i = 0; i < elements.length; i++) {
2563            new Search({ element: elements[i], options: readData(elements[i]) });
2564        }
2565
2566        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
2567        var body = document.querySelector("body");
2568        var observer = new MutationObserver(function(mutations) {
2569            mutations.forEach(function(mutation) {
2570                // needed for IE
2571                var nodesArray = [].slice.call(mutation.addedNodes);
2572                if (nodesArray.length > 0) {
2573                    nodesArray.forEach(function(addedNode) {
2574                        if (addedNode.querySelectorAll) {
2575                            var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self));
2576                            elementsArray.forEach(function(element) {
2577                                new Search({ element: element, options: readData(element) });
2578                            });
2579                        }
2580                    });
2581                }
2582            });
2583        });
2584
2585        observer.observe(body, {
2586            subtree: true,
2587            childList: true,
2588            characterData: true
2589        });
2590    }
2591
2592    if (document.readyState !== "loading") {
2593        onDocumentReady();
2594    } else {
2595        document.addEventListener("DOMContentLoaded", onDocumentReady);
2596    }
2597
2598})();
2599
2600;(function(ContextHub, document) {
2601    const STORE_NAME = 'profile';
2602    const DATA_PREFILL_SELECTOR = 'data-prefill';
2603    const DATA_PREFILL_NOOP_VALUE = 'none';
2604    const PROFILE_STORE = ContextHub.getStore(STORE_NAME);
2605
2606    const handleInput = function(element, prefillValue) {
2607        element.value = PROFILE_STORE.getItem(prefillValue)
2608				element.dispatchEvent(new CustomEvent('prefill:done'))
2609    }
2610
2611    const strategies = new Map();
2612    strategies.set('input', handleInput);
2613
2614    const findAndHandleFields = function(parent, prefillValue) {
2615        strategies.forEach((strategy, selector) => {
2616            const fields = parent.querySelectorAll(selector);
2617            if (fields && fields.length) {
2618                fields.forEach((field) => {
2619                    strategy(field, prefillValue)
2620                })
2621            }
2622        })
2623    }
2624
2625	const prefillFields = function() {
2626		const elements = document.querySelectorAll(`[${DATA_PREFILL_SELECTOR}]`);
2627		elements.forEach( el => {
2628			const prefillValue = el.getAttribute(DATA_PREFILL_SELECTOR) || '';
2629				if (!prefillValue || prefillValue == DATA_PREFILL_NOOP_VALUE){
2630						return;
2631				}
2632				findAndHandleFields(el, prefillValue);
2633			});
2634    }
2635
2636	function attachEvents() {
2637		PROFILE_STORE.eventing.on(ContextHub.Constants.EVENT_STORE_READY, prefillFields, 'ngp:profile.prefill-ready', true);
2638	}
2639
2640  if (document.readyState !== 'loading') {
2641		attachEvents()
2642	} else {
2643		document.addEventListener('DOMContentLoaded', attachEvents)
2644	}
2645
2646})(window.ContextHub, document);
2647
2648/*******************************************************************************
2649 * Copyright 2016 Adobe
2650 *
2651 * Licensed under the Apache License, Version 2.0 (the "License");
2652 * you may not use this file except in compliance with the License.
2653 * You may obtain a copy of the License at
2654 *
2655 *     http://www.apache.org/licenses/LICENSE-2.0
2656 *
2657 * Unless required by applicable law or agreed to in writing, software
2658 * distributed under the License is distributed on an "AS IS" BASIS,
2659 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2660 * See the License for the specific language governing permissions and
2661 * limitations under the License.
2662 ******************************************************************************/
2663(function() {
2664    "use strict";
2665
2666    var NS = "cmp";
2667    var IS = "formText";
2668    var IS_DASH = "form-text";
2669    var SELF_SELECTOR = "[data-" + NS + '-is="' + IS + '"]';
2670
2671    var selectors = {
2672        self: SELF_SELECTOR,
2673        validationMessage: SELF_SELECTOR + " .cmp-form-text__validation-message"
2674    };
2675
2676    var displayValidationMessage = false;
2677
2678    var properties = {
2679        /**
2680         * A validation message to display if there is a type mismatch between the user input and expected input.
2681         *
2682         * @type {String}
2683         */
2684        constraintMessage: "",
2685        /**
2686         * A validation message to display if no input is supplied, but input is expected for the field.
2687         *
2688         * @type {String}
2689         */
2690        requiredMessage: ""
2691    };
2692
2693    function readData(element) {
2694        var data = element.dataset;
2695        var options = [];
2696        var capitalized = IS;
2697        capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
2698        var reserved = ["is", "hook" + capitalized];
2699
2700        for (var key in data) {
2701            if (Object.prototype.hasOwnProperty.call(data, key)) {
2702                var value = data[key];
2703
2704                if (key.indexOf(NS) === 0) {
2705                    key = key.slice(NS.length);
2706                    key = key.charAt(0).toLowerCase() + key.substring(1);
2707
2708                    if (reserved.indexOf(key) === -1) {
2709                        options[key] = value;
2710                    }
2711                }
2712            }
2713        }
2714
2715        return options;
2716    }
2717
2718    function FormText(config) {
2719        if (config.element) {
2720            // prevents multiple initialization
2721            config.element.removeAttribute("data-" + NS + "-is");
2722        }
2723
2724        this._cacheElements(config.element);
2725        this._setupProperties(config.options);
2726
2727        this._elements.input.addEventListener("invalid", this._onInvalid.bind(this));
2728        this._elements.input.addEventListener("input", this._onInput.bind(this));
2729        if (displayValidationMessage) {
2730            this._elements.input.checkValidity();
2731        }
2732    }
2733
2734    FormText.prototype._onInvalid = function(event) {
2735        event.target.setCustomValidity("");
2736        if (event.target.validity.typeMismatch) {
2737            if (this._properties.constraintMessage) {
2738                event.target.setCustomValidity(this._properties.constraintMessage);
2739            }
2740        } else if (event.target.validity.valueMissing) {
2741            if (this._properties.requiredMessage) {
2742                event.target.setCustomValidity(this._properties.requiredMessage);
2743            }
2744        }
2745        if (displayValidationMessage) {
2746            var validationMessage = event.target.parentElement.querySelector(".cmp-form-text__validation-message");
2747            if (validationMessage) {
2748                validationMessage.innerText = event.target.validationMessage;
2749            }
2750        }
2751    };
2752
2753    FormText.prototype._onInput = function(event) {
2754        event.target.setCustomValidity("");
2755        if (displayValidationMessage) {
2756            event.target.checkValidity();
2757        }
2758    };
2759
2760    FormText.prototype._cacheElements = function(wrapper) {
2761        this._elements = {};
2762        this._elements.self = wrapper;
2763        var hooks = this._elements.self.querySelectorAll("[data-" + NS + "-hook-" + IS_DASH + "]");
2764        for (var i = 0; i < hooks.length; i++) {
2765            var hook = hooks[i];
2766            var capitalized = IS;
2767            capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1);
2768            var key = hook.dataset[NS + "Hook" + capitalized];
2769            this._elements[key] = hook;
2770        }
2771    };
2772
2773    FormText.prototype._setupProperties = function(options) {
2774        this._properties = {};
2775
2776        for (var key in properties) {
2777            if (Object.prototype.hasOwnProperty.call(properties, key)) {
2778                var property = properties[key];
2779                if (options && options[key] != null) {
2780                    if (property && typeof property.transform === "function") {
2781                        this._properties[key] = property.transform(options[key]);
2782                    } else {
2783                        this._properties[key] = options[key];
2784                    }
2785                } else {
2786                    this._properties[key] = properties[key]["default"];
2787                }
2788            }
2789        }
2790    };
2791
2792    function onDocumentReady() {
2793        var validationMessages = document.querySelectorAll(selectors.validationMessage);
2794        if (validationMessages && validationMessages.length > 0) {
2795            displayValidationMessage = true;
2796        }
2797
2798        var elements = document.querySelectorAll(selectors.self);
2799        for (var i = 0; i < elements.length; i++) {
2800            new FormText({ element: elements[i], options: readData(elements[i]) });
2801        }
2802
2803        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
2804        var body = document.querySelector("body");
2805        var observer = new MutationObserver(function(mutations) {
2806            mutations.forEach(function(mutation) {
2807                // needed for IE
2808                var nodesArray = [].slice.call(mutation.addedNodes);
2809                if (nodesArray.length > 0) {
2810                    nodesArray.forEach(function(addedNode) {
2811                        if (addedNode.querySelectorAll) {
2812                            var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self));
2813                            elementsArray.forEach(function(element) {
2814                                new FormText({ element: element, options: readData(element) });
2815                            });
2816                        }
2817                    });
2818                }
2819            });
2820        });
2821
2822        observer.observe(body, {
2823            subtree: true,
2824            childList: true,
2825            characterData: true
2826        });
2827    }
2828
2829    if (document.readyState !== "loading") {
2830        onDocumentReady();
2831    } else {
2832        document.addEventListener("DOMContentLoaded", onDocumentReady);
2833    }
2834
2835})();
2836
2837/*******************************************************************************
2838 * Copyright 2020 Adobe
2839 *
2840 * Licensed under the Apache License, Version 2.0 (the "License");
2841 * you may not use this file except in compliance with the License.
2842 * You may obtain a copy of the License at
2843 *
2844 *     http://www.apache.org/licenses/LICENSE-2.0
2845 *
2846 * Unless required by applicable law or agreed to in writing, software
2847 * distributed under the License is distributed on an "AS IS" BASIS,
2848 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2849 * See the License for the specific language governing permissions and
2850 * limitations under the License.
2851 ******************************************************************************/
2852(function() {
2853    "use strict";
2854
2855    var NS = "cmp";
2856    var IS = "pdfviewer";
2857    var SDK_URL = "https://acrobatservices.adobe.com/view-sdk/viewer.js";
2858    var SDK_READY_EVENT = "adobe_dc_view_sdk.ready";
2859
2860    var selectors = {
2861        self: "[data-" + NS + '-is="' + IS + '"]',
2862        sdkScript: 'script[src="' + SDK_URL + '"]'
2863    };
2864
2865    function initSDK() {
2866        var sdkIncluded = document.querySelectorAll(selectors.sdkScript).length > 0;
2867        if (!window.adobe_dc_view_sdk && !sdkIncluded) {
2868            var dcv = document.createElement("script");
2869            dcv.type = "text/javascript";
2870            dcv.src = SDK_URL;
2871            document.body.appendChild(dcv);
2872        }
2873    }
2874
2875    function previewPdf(component) {
2876        // prevents multiple initialization
2877        component.removeAttribute("data-" + NS + "-is");
2878
2879        // add the view sdk to the page
2880        initSDK();
2881
2882        // manage the preview
2883        if (component.dataset && component.id) {
2884            if (window.AdobeDC && window.AdobeDC.View) {
2885                dcView(component);
2886            } else {
2887                document.addEventListener(SDK_READY_EVENT, function() {
2888                    dcView(component);
2889                });
2890            }
2891        }
2892    }
2893
2894    function dcView(component) {
2895        var adobeDCView = new window.AdobeDC.View({
2896            clientId: component.dataset.cmpClientId,
2897            divId: component.id + "-content",
2898            reportSuiteId: component.dataset.cmpReportSuiteId
2899        });
2900        adobeDCView.previewFile({
2901            content: { location: { url: component.dataset.cmpDocumentPath } },
2902            metaData: { fileName: component.dataset.cmpDocumentFileName }
2903        }, JSON.parse(component.dataset.cmpViewerConfigJson));
2904    }
2905
2906    /**
2907     * Document ready handler and DOM mutation observers. Initializes Accordion components as necessary.
2908     *
2909     * @private
2910     */
2911    function onDocumentReady() {
2912        var elements = document.querySelectorAll(selectors.self);
2913        for (var i = 0; i < elements.length; i++) {
2914            previewPdf(elements[i]);
2915        }
2916
2917        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
2918        var body = document.querySelector("body");
2919        var observer = new MutationObserver(function(mutations) {
2920            mutations.forEach(function(mutation) {
2921                // needed for IE
2922                var nodesArray = [].slice.call(mutation.addedNodes);
2923                if (nodesArray.length > 0) {
2924                    nodesArray.forEach(function(addedNode) {
2925                        if (addedNode.querySelectorAll) {
2926                            var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self));
2927                            elementsArray.forEach(function(element) {
2928                                previewPdf(element);
2929                            });
2930                        }
2931                    });
2932                }
2933            });
2934        });
2935
2936        observer.observe(body, {
2937            subtree: true,
2938            childList: true,
2939            characterData: true
2940        });
2941
2942    }
2943
2944    if (document.readyState !== "loading") {
2945        onDocumentReady();
2946    } else {
2947        document.addEventListener("DOMContentLoaded", onDocumentReady);
2948    }
2949}());
2950
2951(function (window, document) {
2952
2953	function attachEvents() {
2954
2955		const elementUrl = document.querySelector("meta[property='og:url']");
2956		const url = elementUrl ? elementUrl.getAttribute('content') : '';
2957		const elementMedia = document.querySelector("meta[property='og:image']");
2958		const media = elementMedia ? elementMedia.getAttribute('content') : '';
2959		const elementTitle = document.querySelector("meta[property='og:title']");
2960		const title = elementTitle ? elementTitle.getAttribute('content') : '';
2961
2962		document.querySelectorAll('.shareButtonDirectLink').forEach(button =>
2963			button.addEventListener("click", () => social_sharing.clipboardShare(url))
2964		);
2965
2966		document.querySelectorAll('.shareButtonFacebook').forEach(button =>
2967			button.addEventListener("click", () => social_sharing.fbShare(url))
2968		);
2969
2970		document.querySelectorAll('.shareButtonTwitter').forEach(button =>
2971			button.addEventListener("click", () => social_sharing.ttShare(button.dataset.twitterParams))
2972		);
2973
2974		document.querySelectorAll('.shareButtonLinkedIn').forEach(button =>
2975			button.addEventListener("click", () => social_sharing.linekdInShare(url))
2976		);
2977
2978		document.querySelectorAll('.shareButtonPinterest').forEach(button =>
2979			button.addEventListener("click", () => social_sharing.pinterestShare(url, media, title))
2980		);
2981	}
2982
2983	if (document.readyState !== 'loading') {
2984		attachEvents();
2985	} else {
2986		document.addEventListener('DOMContentLoaded', attachEvents)
2987	}
2988
2989})(window, document);
2990
2991window.social_sharing = (function () {
2992
2993	const popupWindow = function (url, title, w, h) {
2994		const left = (screen.width / 2) - (w / 2);
2995		const top = (screen.height / 2) - (h / 2);
2996		return window.open(url, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width=' + w + ', height=' + h + ', top=' 
2996+ top + ', left=' + left);
2997	};
2998
2999	return {
3000		fbShare: function (url) {
3001			popupWindow('https://www.facebook.com/sharer/sharer.php?u=' + encodeURIComponent(url), "", 800, 600);
3002		},
3003		ttShare: function (params) {
3004			popupWindow('https://twitter.com/intent/tweet' + params, "", 800, 600);
3005		},
3006
3007		linekdInShare: function (url) {
3008			popupWindow('https://www.linkedin.com/sharing/share-offsite/?url=' + encodeURIComponent(url), "", 800, 600);
3009		},
3010		pinterestShare: function (url, media, description) {
3011			popupWindow('http://pinterest.com/pin/create/button/?url=' + encodeURIComponent(url) + '&media=' + encodeURIComponent(media) + '&description=' + encodeURIComponent(description), "", 800, 600);
3012		},
3013		clipboardShare: function (url) {
3014			const textArea = document.createElement("textarea");
3015			textArea.value = url;
3016
3017			textArea.style.top = "0";
3018			textArea.style.left = "0";
3019			textArea.style.position = "fixed";
3020
3021			document.body.appendChild(textArea);
3022			textArea.focus();
3023			textArea.select();
3024			try {
3025				document.execCommand('copy');
3026			} catch (err) {
3027				console.error('Unable to copy to clipboard', err);
3028			}
3029			textArea.remove();
3030		}
3031	}
3032})();
3033
3034// TOOD we should consider migrating this to TypeScript in the future
3035// this should improve code modularity and maintainability
3036
3037; (function () {
3038	const inEditor = isInEditor && isInEditor()
3039	const dataOdl = JSON.parse(document.body.dataset.oneDataLayer || '{}')
3040	const is404 = dataOdl && dataOdl.page && dataOdl.page.errorType === '404'
3041
3042	const DUMMY_CONTEXTHUB_IMPLEMENTATION = {
3043		getItem: function () {
3044			return null
3045		}
3046	}
3047
3048	const ATTR_SKIP_DEFAULT_EVENT = 'skipdefaultevent'
3049
3050	const ASSETS_EXTENSIONS = [
3051		'doc', 'xlsx', 'rar', 'docx', 'tab', 'exe', 'eps', 'csv', 'wma', 'jpg',
3052		'zip', 'mov', 'png', 'txt', 'avi', 'svg', 'vsd', 'wmv', 'xls', 'vxd',
3053		'mp3', 'ppt', 'xml', 'wav', 'pptx', 'js', 'm4v', 'pdf', 'css', 'ris'
3054	];
3055
3056	const clearSourceObject = (source) => {
3057		Object.keys(source)
3058			.forEach(key => source[key] === undefined && delete source[key])
3059	}
3060
3061	const runWhenPageFinishedLoading = (callback) => {
3062		if (document.readyState !== 'loading') {
3063			callback()
3064		} else {
3065			document.addEventListener('DOMContentLoaded', callback)
3066		}
3067	}
3068
3069  /**
3070   * Determines the position/area of a link element within the header structure.
3071   *
3072   * @param {Element} chosenElement - The DOM element to analyze for its position in the header
3073   * @returns {string|undefined} The header area where the element is located:
3074   *   - 'Utilities area' if the element is in utilities slot, EHE-LOGIN component, mobile country selector, or mobile language selector
3075   *   - 'Navigation area' if the element is in the menu component or mobile menu component
3076   *   - 'Brand area' if the element has a brand-href attribute
3077   *   - undefined if the element is not in any recognized header area
3078   */
3079	const handleLinkPositionHeader = (chosenElement) => {
3080		// TODO add also other areas of header if needed
3081		const isInsideUtilitySlot = chosenElement.closest('[slot="utilities"]');
3082		const isEheLoginComponent = chosenElement.nodeName === 'EHE-LOGIN';
3083		const isInsideMobileContrySelector = chosenElement.closest('[data-component="countryselector-mobile"]');
3084		const isInsideMobileLanguageSelector = chosenElement.closest('#language-selector-mobile');
3085		const isInsideMenuComponent = chosenElement.closest('ehe-menu');
3086		const isInsideMobileMenuComponent = chosenElement.getRootNode().host?.nodeName === 'EHE-MOBILE-MENU' || chosenElement.nodeName === 'EHE-MOBILE-MENU' || chosenElement.closest('[slot="mobile-navigation-item"]');
3087		const isInsideBrandArea = chosenElement.hasAttribute('brand-href');
3088
3089		if (isInsideUtilitySlot || isEheLoginComponent || isInsideMobileContrySelector || isInsideMobileLanguageSelector) {
3090			return 'Utilities area';
3091		}
3092
3093		if (isInsideMenuComponent || isInsideMobileMenuComponent) {
3094			return 'Navigation area';
3095		}
3096
3097		if (isInsideBrandArea) {
3098			return 'Brand area';
3099		}
3100
3101		return undefined;
3102	}
3103
3104  /**
3105   * Handles link position detection for site analytics by traversing the event's composed path.
3106   * Searches for the nearest ancestor element with a 'data-odl-position' attribute.
3107   * If found, returns the attribute value; otherwise, falls back to getLinkFallbackPosition.
3108   *
3109   * @param {Event}
3109 event - The DOM event object containing the composed path of elements.
3110   * @returns {string} The link position value from the 'data-odl-position' attribute,
3111   *                   or the fallback position determined by getLinkFallbackPosition.
3112   */
3113	const handleLinkPositionSite = (event) => {
3114		const DATA_ODL_POSITION_ATTR = 'data-odl-position';
3115
3116		/** @type Element[] */
3117		const composedPath = event.composedPath().filter(el => el instanceof Element);
3118		const odlPositionElements = composedPath.filter((el => el.hasAttribute(DATA_ODL_POSITION_ATTR)))
3119
3120		if (odlPositionElements.length) {
3121			return odlPositionElements[0].getAttribute(DATA_ODL_POSITION_ATTR);
3122		}
3123
3124    return getLinkFallbackPosition(composedPath);
3125	}
3126
3127  /**
3128   * Determines a fallback position/label for a link based on its component hierarchy.
3129   * Searches the composed path for either an EHE component or a CMP component and
3130   * extracts a formatted position name from the component's tag name or class name.
3131   *
3132   * @param {Element[]} composedPath - The event's composed path containing the DOM element hierarchy
3133   * @returns {string|undefined} A formatted component name (e.g., "My Component Name") if found,
3134   *                             otherwise undefined
3135   *
3136   * @example
3137   * // Returns "Data Table" for <ehe-data-table>
3138   * getLinkFallbackPosition([elementRef, eheDataTable]);
3139   *
3140   * @example
3141   * // Returns "Card" for <div class="cmp-card">
3142   * getLinkFallbackPosition([elementRef, cmpCardDiv]);
3143   */
3144  const getLinkFallbackPosition = (composedPath) => {
3145    const eheComponent = composedPath.find(el => el.tagName.toLowerCase().startsWith('ehe-'))
3146    if (eheComponent) {
3147      return eheComponent
3148        .tagName
3149        .toLowerCase()
3150        .replace('ehe-', '')
3151        .split('-')
3152        .map(word => word.charAt(0).toUpperCase() + word.slice(1))
3153        .join(' ');
3154    }
3155
3156    const cmpElement = composedPath.find(el => {
3157      const classList = Array.from(el.classList || []);
3158      return classList.some(cls => cls.startsWith('cmp-'));
3159    });
3160    if (cmpElement) {
3161      const cmpClass = Array.from(cmpElement.classList).find(cls => cls.startsWith('cmp-'));
3162      return cmpClass
3163        .replace('cmp-', '')
3164        .split('-')
3165        .map(word => word.charAt(0).toUpperCase() + word.slice(1))
3166        .join(' ');
3167    }
3168
3169    return undefined
3170  }
3171
3172  /**
3173   * Determines the position/location of a link element within the page structure.
3174   * @param {Element} rootElement - The root element to use for determining link position, takes precedence over originalElement
3175   * @param {Element} originalElement - The original element that triggered the link detection
3176   * @param {Event} event - The event object associated with the link interaction
3177   * @returns {*} The link position information from the appropriate handler function, or undefined if no valid element is provided
3178   */
3179	const getLinkPosition = (rootElement, originalElement, event) => {
3180		const chosenElement = rootElement || originalElement;
3181		if (!chosenElement) {
3182			return undefined;
3183		}
3184
3185		let linkMainArea = 'site';
3186
3187		const isInsideHeader = chosenElement.closest('header') || chosenElement.getRootNode().host?.nodeName === 'EHE-HEADER' || chosenElement.getRootNode().host?.nodeName === 'EHE-MOBILE-MENU';
3188		if (isInsideHeader) {
3189			linkMainArea = 'header';
3190		}
3191
3192		switch (linkMainArea) {
3193			case 'header': {
3194				return handleLinkPositionHeader(chosenElement);
3195			}
3196			case 'site':
3197			default: {
3198				return handleLinkPositionSite(event);
3199			}
3200		}
3201	}
3202
3203	const getTimestamp = () => Math.round(new Date().getTime() / 1e3);
3204
3205	class OneDataLayer {
3206		constructor(body) {
3207			this.body = body
3208			this.mediaState = []
3209			this.scrollPositionReached = { '25': false, '50': false, '75': false, '90': false, '100': false }
3210
3211			this.bindDom()
3212			this.bindData()
3213			this.bindEvents()
3214
3215			runWhenPageFinishedLoading(() => {
3216				window.EHEBookmarkManager.bookmarksAreReady().then(() => {
3217					const hasNoBookmarks = !window.EHEBookmarkManager.bookmarks || !window.EHEBookmarkManager.bookmarks.length
3218					const timeout = hasNoBookmarks ? 1500 : 0
3219					setTimeout(() => {
3220						this.handlePageLoad()
3221						document.dispatchEvent(new CustomEvent('OneDataLayer:loaded'))
3222					}, timeout)
3223				})
3224			})
3225		}
3226
3227		bindDom() {
3228			this.dynamicVideoContainers = this.body.querySelectorAll('.dynamic-video-component')
3229		}
3230
3231		bindData() {
3232			// scroll flags
3233			this.scrolledFifty = false
3234			this.scrolledNinety = false
3235
3236			this.contextHubProfile = (ContextHub && ContextHub.getStore('profile')) || DUMMY_CONTEXTHUB_IMPLEMENTATION
3237			this.oneDataLayerData = this.body.dataset.oneDataLayer
3238
3239			// handled events
3240			this.events = {
3241				onsiteSearch: 'onsiteSearch',
3242				scroll: 'scroll',
3243				video: {
3244					error: 'videoError',
3245					start: 'videoStart',
3246					complete: 'videoComplete',
3247					progress: 'videoProgress'
3248				},
3249				defaultLinkClick: 'defaultLinkClick',
3250				downloadLinkClick: 'downloadLinkClick',
3251				audio: {
3252					start: 'audioStart',
3253					complete: 'audioComplete',
3254					progress: 'audioProgress',
3255					error: 'audioError'
3256				}
3257			}
3258		}
3259
3260		bindEvents() {
3261			if (!is404) {
3262				document.addEventListener('scroll', () => this.handlePageScroll(), { passive: true })
3263			}
3264			document.addEventListener('OneDataLayer:search-event', (e) => this.handleSiteSearch(e.detail))
3265			document.addEventListener('OneDataLayer:form-event', (e) => this.handleFormEvents(e.detail))
3266
3267			window.addEventListener('OneTrustReady', () => {
3268				OneTrust.OnConsentChanged(() => {
3269					const user = { consent: this.prepareConsent() }
3270					this.addToMainDataObject({
3271						event: 'consentUpdated',
3272						user
3273					});
3274				});
3275			})
3276
3277			const isIOS = ['iPad Simulator', 'iPhone Simulator', 'iPod Simulator', 'iPad', 'iPhone', 'iPod'].includes(navigator.platform)
3278				|| (navigator.userAgent.includes('Mac') && 'ontouchend' in document)
3279			const layerEventName = isIOS ? 'pagehide' : 'beforeunload';
3280
3281			window.addEventListener('beforeunload', () => {
3282				clearInterval(this.videoProgressInterval)
3283				clearInterval(this.audioProgressInterval)
3284			});
3285
3286			window.addEventListener(layerEventName, () => {
3287				this.setNewPageData(this.parsedOneDataLayerJSON, OneDataLayer.PREVIOUS_PAGE_DATA)
3288			})
3289
3290			this.dynamicVideoEventsHandler()
3291			this.audioEventsHandler()
3292
3293			this.body.addEventListener('click', (e) => {
3294				const getText = (textValue) => {
3295					return textValue && textValue.trim()
3296				}
3297
3298				const target = e.target || e.srcElement
3299
3300				// Skip click events for Download buttons in Audio Card, they are handled separately
3301				if (target.nodeName === 'EHE-CARD-AUDIO' || target.nodeName === 'EHE-AUDIO-LIST') {
3302					return
3303				}
3304
3305				/**
3306				 * Handles click on link element with href
3307				 * @param {Element} element
3308				 */
3309				const handleTagClick = (element) => {
3310					const { href, text, textContent } = element
3311					const tagRootNodeHost = element.getRootNode() && element.getRootNode().host
3312					const tagRootNodeHostTextContent = tagRootNodeHost && getText(tagRootNodeHost.textContent)
3313					const innertext = getText(text) || getText(textContent) || tagRootNodeHostTextContent || ''
3314					const position = getLinkPosition(tagRootNodeHost, element, e)
3315					// FIXME the extension test misses the dot, so it matches any URL whose last characters
3316					// happen to spell an extension - '/en/paris' matches 'ris', '/steps' matches 'eps',
3317					// '/tab' matches 'tab' - and those links are then reported as downloads. It also fails
3318					// on real files carrying a query string or hash ('file.pdf?v=2'). Should compare
3319					// `.${ext}` against the lower-cased `new URL(href, location.origin).pathname` instead.
3320					const hasAssetExtension = ASSETS_EXTENSIONS.some((ext) => href && href.endsWith(ext))
3321					// FIXME the download name does not follow the oneDataLayer spec, which wants the file
3322					// name (or the literal 'other' when it is unknown):
3323					// - `split('.').shift()` truncates names containing dots ('report.v2.pdf' -> 'report')
3324					// - the value is never decodeURIComponent'd, so percent-encoded names leak through
3325					// - when the link is a download because of the `download` attribute rather than a known
3326					//   extension, the name falls back to the CSS class list (e.g. 'download-link')
3327					const fileName = href && href.split('/').pop().split('.').shift()
3328					const name = hasAssetExtension ? fileName : element.classList.value.trim().replaceAll(' ', '.')
3329					const isDownloadLink = (tagRootNodeHost && tagRootNodeHost.hasAttribute('download')) || hasAssetExtension;
3330					const fixedName = position === 'Brand area' ? 'Home link' : undefined;
3331
3332					let linkType = isDownloadLink ? 'downloadLink' : 'Link';
3333
3334					href && this.handleLinkClick({
3335						target,
3336						url: href,
3337						innertext,
3338						name: fixedName || name,
3339						type: linkType,
3340						...(hasAssetExtension && { format: href.split('.').pop() }),
3341						...(position && { position })
3342					})
3343				}
3344
3345				const hasSkipDefaultEvent = (element) => element.hasAttribute(ATTR_SKIP_DEFAULT_EVENT)
3346				const hasHref = (element) => element ? Boolean(element.href) : false
3347
3348				/** @type Element */
3349				const closestElementWhichControlsClickEvents = e
3350					.composedPath()
3351					.filter((node) => node instanceof Element)
3352					.find((element) => hasHref(element) || hasSkipDefaultEvent(element))
3353
3354				if (closestElementWhichControlsClickEvents && !hasSkipDefaultEvent(closestElementWhichControlsClickEvents)) {
3355					handleTagClick(closestElementWhichControlsClickEvents)
3356				}
3357			})
3358		}
3359
3360		prepareConsent() {
3361			const cookieName = 'OptanonConsent';
3362			const consentName = 'groups';
3363			const cookieString = document.cookie.split(';').find((cookie) => cookie.trim().startsWith(cookieName))
3364			const cookieValue = cookieString && cookieString.substring(cookieString.indexOf(cookieName) + 1)
3365			const cookieParams = cookieValue && new URLSearchParams(cookieValue)
3366			const consentValue = cookieParams && cookieParams.get(consentName)
3367			const consent = consentValue && consentValue.split(',').reduce((dst, val) => {
3368				const splitedVal = val.split(':');
3369				if (splitedVal.length > 1) {
3370					const [key, checked] = splitedVal;
3371					return {...dst, [key]: checked === '1'}
3372				}
3373				return dst
3374			}, {})
3375
3376			return consent
3377		}
3378
3379		prepareComponents() {
3380			const summaryElements = document.querySelectorAll('ehe-summary[id]')
3381			if (summaryElements.length === 0) {
3382				return {}
3383			}
3384			const items = Array.from(summaryElements)
3385				.filter(element => !element.closest('.segment-hidden'))
3386				.map(element => {
3387					return { id : element.id }
3388				})
3389			if (items.length === 0) {
3390				return {}
3391			}
3392			return {
3393				contentSummary: {
3394					items
3395				}
3396			}
3397		}
3398
3399		getPercentage(currentTime, totalTime) {
3400			const steps = [10, 25, 50, 75, 100]
3401			const percentage = Number(((currentTime / totalTime) * 100).toFixed(0))
3402			return steps.filter((step) => percentage >= step).pop() || percentage
3403		}
3404
3405		pushVideoEvent(event) {
3406      window.pushOneDataLayerEvent(
3407        event.eventName,
3408        {
3409          content: {
3410            video: {
3411              name: event.videoTitle,
3412              url: event.videoSrc,
3413              provider: 'Dynamic Media Video',
3414              durationTotal: event.duration,
3415              ...(event.campaignId && { campaignId: event.campaignId }),
3416              ...(event.uci && { uci: event.uci }),
3417              ...(event.progress && { progress: event.progress }),
3418              ...(event.error && { error: typeof event.error === 'string' ? event.error : 'Video could not be loaded' }),
3419            },
3420          },
3421        },
3422        event.eventTarget,
3423      );
3424		}
3425
3426		pushAudioEvent(event) {
3427			window.oneDataLayer.push({
3428				event: event.eventName,
3429				timestamp: this.timestamp,
3430				content:{
3431					audio: {
3432							name: event.url.split('/').pop(),
3433							url: event.url,
3434							provider: 'Audio player',
3435							durationTotal: event.durationTotal,
3436							...event.progress && { progress: event.progress },
3437							...event.error && { error: 'Audio could not be loaded' }
3438						}
3439				}
3440		});
3441		}
3442
3443		audioEventsHandler() {
3444			const audioElements = this.body.querySelectorAll('audio')
3445			audioElements.forEach((audio) => {
3446				let audioData = {}
3447				let progressReached = {}
3448
3449				audio.addEventListener('playing', (e) => {
3450					audioData.durationTotal = e.srcElement.duration
3451					audioData.url = e.target.currentSrc
3452					if (!progressReached[0]) {
3453						audioData.eventName = this.events.audio.start
3454						this.pushAudioEvent(audioData)
3455						progressReached[0] = true
3456					}
3457					this.audioProgressInterval = setInterval(() => {
3458					const currentPercentage = this.getPercentage(audio.currentTime, e.srcElement.duration)
3459					audioData.eventName = this.events.audio.progress
3460					switch (currentPercentage) {
3461						case 10:
3462							if (!progressReached[10]) {
3463								audioData.progress = 10
3464								this.pushAudioEvent(audioData)
3465								progressReached[10] = true
3466						}
3467							break
3468						case 25:
3469							if (!progressReached[25]) {
3470								audioData.progress = 25
3471								this.pushAudioEvent(audioData)
3472								progressReached[25] = true
3473						}
3474							break
3475						case 50:
3476							if (!progressReached[50]) {
3477								audioData.progress = 50
3478								this.pushAudioEvent(audioData)
3479								progressReached[50] = true
3480						}
3481							break
3482						case 75:
3483							if (!progressReached[75]) {
3484								audioData.progress = 75
3485								this.pushAudioEvent(audioData)
3486								progressReached[75] = true
3487						}
3488					}
3489					}, 1000)
3490				})
3491
3492				audio.addEventListener('pause', () => {
3493					clearInterval(this.audioProgressInterval)
3494				})
3495
3496				audio.addEventListener('ended', (e) => {
3497					audioData.durationTotal = e.srcElement.duration
3498					audioData.eventName = this.events.audio.complete
3499					audioData.url = e.target.currentSrc
3500					audioData.progress = 100
3501					this.pushAudioEvent(audioData)
3502					clearInterval(this.audioProgressInterval)
3503					audioData = {}
3504					progressReached = {}
3505				})
3506
3507				audio.addEventListener('error', (e) => {
3508					audioData.eventName = this.events.audio.error
3509					audioData.error = true
3510					this.pushAudioEvent(audioData)
3511				})
3512			})
3513		}
3514
3515		dynamicVideoEventsHandler() {
3516			window.oneDataLayer = window.oneDataLayer || []
3517
3518			window.addEventListener('notfUserEvent', (e) => {
3519				if(e.s7event.trackEvent === 'LOAD' && e.target.dataset.component === 'VideoPlayer') {
3520					const video = e.target.querySelector('video')
3521					const parentElement = video.closest('[data-asset-name]')
3522					const storyCardVideoElement = e.target.closest('[data-video-card]')
3523
3524					const videoTitle = parentElement.dataset.title || 'No video title provided'
3525					const videoSrc = parentElement.dataset.assetPath
3526
3527					const campaignId = parentElement.getAttribute('data-campaign-id');
3528					const consentBasedTracking = parentElement.getAttribute('data-consent-based-tracking');
3529
3530					let progressReached = {}
3531
3532					let eventData = {
3533						eventTarget: video,
3534						eventName: '',
3535						videoTitle: videoTitle,
3536						videoSrc: videoSrc,
3537						...(campaignId && { campaignId }),
3538            ...(consentBasedTracking && { uci: this.getUser()?.uci }),
3539					}
3540
3541					video.addEventListener('loadedmetadata', () => {
3542						if (storyCardVideoElement && !storyCardVideoElement.hasAttribute('duration')) {
3543							storyCardVideoElement.setAttribute('duration', video.duration)
3544						}
3545					})
3546
3547					video.addEventListener('playing', (e) => {
3548						eventData.duration = e.srcElement.duration
3549						if (!progressReached[0]) {
3550							eventData.eventName = this.events.video.start
3551							this.pushVideoEvent(eventData)
3552							progressReached[0] = true
3553						}
3554						this.videoProgressInterval = setInterval(() => {
3555							const currentPercentage = this.getPercentage(video.currentTime, e.srcElement.duration)
3556							eventData.eventName = this.events.video.progress
3557							switch (currentPercentage) {
3558								case 10:
3559									if (!progressReached[10]) {
3560										eventData.progress = 10
3561										this.pushVideoEvent(eventData)
3562										progressReached[10] = true
3563								}
3564									break
3565								case 25:
3566									if (!progressReached[25]) {
3567										eventData.progress = 25
3568										this.pushVideoEvent(eventData)
3569										progressReached[25] = true
3570								}
3571									break
3572								case 50:
3573									if (!progressReached[50]) {
3574										eventData.progress = 50
3575										this.pushVideoEvent(eventData)
3576										progressReached[50] = true
3577								}
3578									break
3579								case 75:
3580									if (!progressReached[75]) {
3581										eventData.progress = 75
3582										this.pushVideoEvent(eventData)
3583										progressReached[75] = true
3584								}
3585							}
3586						}, 1000)
3587					})
3588
3589					video.addEventListener('play', () => {
3590						if (storyCardVideoElement) {
3591							storyCardVideoElement.dispatchEvent(new Event('videoClick'))
3592						}
3593					})
3594
3595					video.addEventListener('pause', () => {
3596						clearInterval(this.videoProgressInterval)
3597					})
3598
3599					video.addEventListener('ended', () => {
3600						eventData.eventName = this.events.video.complete
3601						eventData.progress = 100
3602
3603						this.pushVideoEvent(eventData)
3604						clearInterval(this.videoProgressInterval)
3605
3606						const resetState = () => {
3607							return {
3608								eventTarget: video,
3609								eventName: '',
3610								videoTitle: videoTitle,
3611								videoSrc: videoSrc,
3612							}
3613						}
3614						eventData = resetState()
3615						progressReached = {}
3616					})
3617
3618					video.addEventListener('error', () => {
3619						const mediaError = video.error
3620						const errorMessageParts = ['Video could not be loaded']
3621
3622						if (mediaError) {
3623							const errorCodes = {
3624								1: 'MEDIA_ERR_ABORTED',
3625								2: 'MEDIA_ERR_NETWORK',
3626								3: 'MEDIA_ERR_DECODE',
3627								4: 'MEDIA_ERR_SRC_NOT_SUPPORTED'
3628							}
3629							const codeName = errorCodes[mediaError.code] || 'UNKNOWN_ERROR'
3630							errorMessageParts.push(`${codeName} (code ${mediaError.code})`)
3631							if (mediaError.message) {
3632								errorMessageParts.push(mediaError.message)
3633							}
3634						}
3635
3636						const source = video.currentSrc || video.src || videoSrc
3637						if (source) {
3638							errorMessageParts.push(`source: ${source}`)
3639						}
3640
3641						eventData.eventName = this.events.video.error
3642						eventData.error = errorMessageParts.join(' | ')
3643						this.pushVideoEvent(eventData)
3644						clearInterval(this.videoProgressInterval)
3645					})
3646				}
3647			}, false)
3648
3649			const S7_LOAD_TIMEOUT_MS = 5000
3650			const loadedContainers = new Set()
3651
3652			window.addEventListener('notfUserEvent', (e) => {
3653				if (!e || !e.s7event || !e.target || !e.target.dataset) {
3654					return
3655				}
3656				if (e.s7event.trackEvent === 'LOAD' && e.target.dataset.component === 'VideoPlayer') {
3657					const container =
3658						e.target.closest('.s7dm-dynamic-media') ||
3659						e.target.closest('[data-component="video-player"]')
3660					if (container) {
3661						loadedContainers.add(container)
3662					}
3663				}
3664			}, false)
3665
3666			const reportUnloadedContainer = (container) => {
3667				if (loadedContainers.has(container)) {
3668					return
3669				}
3670
3671				// EHE-13959: skip videos hidden from the current visitor - e.g. protected content
3672				// that leaks into the public/anonymous DOM inside a .segment-hidden block. The
3673				// Scene7 viewer never initializes these, so reporting them produced phantom
3674				// videoError events (largely bot/SEA traffic landing on semi-open pages).
3675				if (container.closest('.segment-hidden, [hidden], [aria-hidden="true"]')) {
3676					return
3677				}
3678
3679				const videoSrc = container.dataset.assetPath || container.dataset.videoPath || ''
3680				const videoTitle = container.dataset.assetName || container.dataset.title || 'No video title provided'
3681				const viewerPath = container.dataset.viewerPath || container.dataset.viewerScript || 'https://s7g10.scene7.com/s7viewers/html5/js/VideoViewer.js'
3682
3683				const errorMessageParts = []
3684				if (!window.s7viewers) {
3685					errorMessageParts.push('S7_SDK_LOAD_FAILURE: Scene7 viewer SDK failed to load')
3686					errorMessageParts.push(`source: ${viewerPath}`)
3687				} else {
3688					errorMessageParts.push('S7_INIT_FAILURE: Scene7 viewer failed to initialize')
3689					errorMessageParts.push(`video: ${videoTitle}`)
3690				}
3691
3692				this.pushVideoEvent({
3693					eventTarget: container,
3694					eventName: this.events.video.error,
3695					videoTitle: videoTitle,
3696					videoSrc: videoSrc,
3697					error: errorMessageParts.join(' | '),
3698				})
3699			}
3700
3701			// EHE-13959: only report a load/init failure for videos the user could actually see.
3702			// Videos below the fold that are never scrolled into view (pages can embed many) must
3703			// not be counted as errors - reporting every unloaded container produced phantom
3704			// videoError events that did not reflect a real user-facing playback problem.
3705			const watchVideoContainers = () => {
3706				const allVideoContainers = [
3707					...this.body.querySelectorAll('.s7dm-dynamic-media[data-viewer-type="VideoViewer"]'),
3708					...this.body.querySelectorAll('[data-component="video-player"]'),
3709				]
3710
3711				if (typeof IntersectionObserver === 'undefined') {
3712					// Fallback where IntersectionObserver is unavailable: keep the previous
3713					// page-load timeout so genuine failures are still tracked.
3714					setTimeout(() => allVideoContainers.forEach(reportUnloadedContainer), S7_LOAD_TIMEOUT_MS)
3715					return
3716				}
3717
3718				const scheduledContainers = new Set()
3719				const observer = new IntersectionObserver((entries) => {
3720					entries.forEach((entry) => {
3721						if (!entry.isIntersecting || scheduledContainers.has(entry.target)) {
3722							return
3723						}
3724						const container = entry.target
3725						scheduledContainers.add(container)
3726						observer.unobserve(container)
3727						// Give the viewer time to load after it becomes visible, then report
3728						// only if it still never loaded.
3729						setTimeout(() => reportUnloadedContainer(container), S7_LOAD_TIMEOUT_MS)
3730					})
3731
3732					// Once every container has been scheduled there is nothing left to watch.
3733					if (scheduledContainers.size === allVideoContainers.length) {
3734						observer.disconnect()
3735					}
3736				})
3737
3738				allVideoContainers.forEach((container) => observer.observe(container))
3739			}
3740
3741			if (document.readyState === 'complete') {
3742				watchVideoContainers()
3743			} else {
3744				window.addEventListener('load', watchVideoContainers, { once: true })
3745			}
3746		}
3747
3748		handleSiteSearch({ term, results, filter, searchComponent }) {
3749			const json = {
3750				event: this.events && this.events.onsiteSearch,
3751				timestamp: this.timestamp,
3752				content: {
3753					onsiteSearch: {
3754						term,
3755						results,
3756						filter,
3757						searchComponent
3758					}
3759				}
3760			}
3761
3762			this.addToMainDataObject(json)
3763		}
3764
3765	handleLinkClick({ target, url, innertext, name, type, position, format }) {
3766		const { hostname: domain, pathname: path } = url && new URL(url, window.location.origin)
3767
3768    const defaultLinkClickData = {
3769      link: {
3770        url,
3771        domain,
3772        path,
3773        innertext,
3774        name,
3775        type,
3776        ...(position && { position }),
3777      },
3778    }
3779
3780    const downloadLinkClickData = { name, url, format }
3781
3782		const isDownloadLink = type === "downloadLink"
3783
3784    // TODO for links that live in the light DOM this duplicates the ODL Launch external script, which
3785    // scrapes the DOM after load and tracks the download itself - the click is then counted twice.
3786    // Do not simply drop the branch though: Launch cannot see links inside a shadow root, so EWC
3787    // downloads would stop being tracked. Scope a fix to light-DOM links only, and for those push
3788    // neither downloadLinkClick nor defaultLinkClick (pushing a link click for a download is an
3789    // explicit spec violation).
3790    window.pushOneDataLayerEvent(
3791      isDownloadLink ? this.events.downloadLinkClick : this.events.defaultLinkClick,
3792      isDownloadLink ? downloadLinkClickData : defaultLinkClickData,
3793      target,
3794    )
3795	}
3796
3797	handleFormEvents({ event, name, type, step, error }) {
3798			const json = {
3799				event,
3800				timestamp: this.timestamp,
3801				content: {
3802					form: {
3803						name,
3804						type,
3805						step,
3806						error
3807					}
3808				}
3809			}
3810
3811			clearSourceObject(json.content.form)
3812			this.addToMainDataObject(json)
3813		}
3814
3815	getPageData() {
3816		if (!this.oneDataLayerDataJson?.page) return {}
3817
3818		const { previousPage, ...pageData } = this.oneDataLayerDataJson.page
3819
3820		if (previousPage) {
3821			pageData.previousPage = previousPage
3822		} else {
3823			pageData.previousPage = this.previousPageData ? JSON.parse(this.previousPageData) : {}
3824		}
3825
3826		if (document.referrer) {
3827			pageData.referrer = document.referrer;
3828		}
3829
3830		return pageData
3831	}
3832
3833	handlePageScroll() {
3834		const scrollTop = window.innerHeight + window.scrollY
3835		const height = document.body.scrollHeight
3836		const breakpoints = [25, 50, 75, 90, 100]
3837
3838		breakpoints.forEach((breakpoint) => {
3839			if (scrollTop >= height * (breakpoint / 100) && !this.scrollPositionReached[breakpoint.toString()]) {
3840				const pageData = this.getPageData()
3841
3842				delete pageData.currency
3843
3844				this.addToMainDataObject({
3845					event: this.events && this.events.scroll,
3846					timestamp: this.timestamp,
3847					page: {
3848						...pageData,
3849						scrolled: breakpoint
3850					}
3851				})
3852				this.scrollPositionReached[breakpoint.toString()] = true
3853			}
3854		})
3855	}
3856
3857	handlePageLoad() {
3858		this.parsedOneDataLayerJSON = this.oneDataLayerDataJson
3859
3860		const consent = this.prepareConsent()
3861
3862		const components = this.prepareComponents()
3863
3864		this.parsedOneDataLayerJSON.components = {
3865				...this.parsedOneDataLayerJSON.components,
3866				...components
3867		}
3868
3869		this.parsedOneDataLayerJSON.page.previousPage = this.previousPageData
3870			? JSON.parse(this.previousPageData)
3871			: {};
3872
3873			this.setPagePath(this.parsedOneDataLayerJSON)
3874			this.setPageUrl(this.parsedOneDataLayerJSON)
3875			this.setNewPageData(this.parsedOneDataLayerJSON, OneDataLayer.CURRENT_PAGE_DATA)
3876			this.setUserData(this.parsedOneDataLayerJSON)
3877
3878			if (consent) {
3879				this.parsedOneDataLayerJSON.user.consent = consent
3880			}
3881
3882			if (document.referrer) {
3883				this.parsedOneDataLayerJSON.page.referrer = document.referrer;
3884			}
3885
3886			if (window.EHEBookmarkManager) {
3887				const currentUrl = `${window.location.origin}${window.location.pathname}`;
3888				const isCurrentPageBookmarked = window.EHEBookmarkManager.isBookmarked(currentUrl);
3889
3890				if (isCurrentPageBookmarked) {
3891					const allBookmarks = window.EHEBookmarkManager.getBookmarks();
3892					const currentPageBookmark = allBookmarks.find(bookmark => bookmark.url === currentUrl);
3893
3894					if (currentPageBookmark) {
3895						const bookmarkObj = {
3896							bookmark: {
3897								bookmarked: true,
3898								items: [{
3899									id: currentPageBookmark.id || "",
3900									title: currentPageBookmark.title || ""
3901								}]
3902							}
3903						}
3904
3905						this.parsedOneDataLayerJSON.components = {
3906							...this.parsedOneDataLayerJSON.components,
3907							...bookmarkObj
3908						}
3909					}
3910				} else {
3911					const bookmarkObj = { bookmark: { bookmarked: false } }
3912					this.parsedOneDataLayerJSON.components = {
3913						...this.parsedOneDataLayerJSON.components,
3914						...bookmarkObj
3915					}
3916				}
3917			}
3918
3919			this.addToMainDataObject(this.parsedOneDataLayerJSON)
3920		}
3921
3922		setNewPageData({ page }, destination = OneDataLayer.PREVIOUS_PAGE_DATA) {
3923			const { url, path, pageTitle, pageName, mainCategory, categoryTree } = page
3924			const newPageData = { url, path, pageTitle, pageName, mainCategory, categoryTree }
3925
3926			this.setSessionStorageData(
3927				destination,
3928				JSON.stringify(newPageData)
3929			)
3930		}
3931
3932		getUser() {
3933			const profileItem = (name) => this.contextHubProfile.getItem(name) || ''
3934			const userIpMeta = document.querySelector('meta[name="user_ip"]')
3935			const userIp = userIpMeta ? userIpMeta.content : ''
3936			const userAccount = { intEm: this.isInternalEmail(profileItem('email')) }
3937
3938			const user = {
3939				isLoggedIn: !!profileItem('uci') || !!profileItem('email'),
3940				uci: profileItem('uci'),
3941				mdm: profileItem('mdm'),
3942				mdmId: profileItem('mdmId'),
3943				sId: profileItem('salesforceId'),
3944				profession: profileItem('g_profession'),
3945				position: profileItem('position'),
3946				specialty: profileItem('g_specialty'),
3947				center: profileItem('center'),
3948				account: userAccount
3949			}
3950
3951			if (userIp) {
3952				user.account.ipAddress = userIp
3953			}
3954
3955			return user;
3956		}
3957
3958		isInternalEmail(email) {
3959			return email.endsWith('@roche.com') || email.endsWith('.roche.com')
3960		}
3961
3962		setPageUrl(oneDataLayerDataJson) {
3963			oneDataLayerDataJson.page.url = window.location.href;
3964		}
3965
3966		setPagePath(oneDataLayerDataJson) {
3967			oneDataLayerDataJson.page.path = window.location.pathname;
3968		}
3969
3970		setUserData(oneDataLayerDataJson) {
3971			oneDataLayerDataJson.user = this.getUser()
3972		}
3973
3974		addToMainDataObject(json) {
3975			json.timestamp = json.timestamp || this.timestamp
3976			this.mainDataObject.push(json)
3977		}
3978
3979		setSessionStorageData(name, value) {
3980			sessionStorage.setItem(name, value)
3981		}
3982
3983		get mainDataObject() {
3984			// @ts-ignore
3985			return oneDataLayer
3986		}
3987
3988		get previousPageData() {
3989			return sessionStorage.getItem(OneDataLayer.PREVIOUS_PAGE_DATA)
3990		}
3991
3992		get currentPageData() {
3993			return sessionStorage.getItem(OneDataLayer.CURRENT_PAGE_DATA)
3994		}
3995
3996		get timestamp() {
3997			return getTimestamp()
3998		}
3999
4000		get oneDataLayerDataJson() {
4001			const oneDataLayerData = this.oneDataLayerData
4002			try {
4003				return JSON.parse(oneDataLayerData)
4004			} catch (e) {
4005				return {}
4006			}
4007		}
4008	}
4009
4010	OneDataLayer.PREVIOUS_PAGE_DATA = 'previousPageData'
4011	OneDataLayer.CURRENT_PAGE_DATA = 'currentPageData'
4012
4013	const init = () => {
4014		// @ts-ignore
4015		window.oneDataLayer = window.oneDataLayer || []
4016    window.pushOneDataLayerEvent = window.pushOneDataLayerEvent || ((type, data, _element) => {
4017      window.oneDataLayer.push({
4018        event: type,
4019        timestamp: getTimestamp(),
4020				...data
4021      });
4022    });
4023
4024		const body = document.body
4025		new OneDataLayer(body)
4026	}
4027
4028	if (!inEditor) {
4029		init()
4030	}
4031})();
4032
4033(function () {
4034	'use strict';
4035
4036	function onDocumentReady() {
4037		const elementsToEmbed = document.querySelectorAll('.embed-fetch-html');
4038		elementsToEmbed.forEach((element) => {
4039			appendExternalHtml(element);
4040		});
4041
4042		const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
4043		const body = document.body;
4044		const observer = new MutationObserver(function (mutations) {
4045			mutations.forEach(function (mutation) {
4046				const nodesArray = Array.prototype.slice.call(mutation.addedNodes);
4047				if (nodesArray.length > 0) {
4048					nodesArray.forEach(function (addedNode) {
4049						if (addedNode.querySelectorAll) {
4050							const fetchHtmlArray = Array.prototype.slice.call(addedNode.querySelectorAll('.embed-fetch-html'));
4051							fetchHtmlArray.forEach(function (element) {
4052								appendExternalHtml(element);
4053							});
4054						}
4055					});
4056				}
4057			});
4058		});
4059
4060		function appendExternalHtml(element) {
4061			const url = element.dataset.fetchUrl;
4062			if (url) {
4063				fetch(url)
4064					.then(res => res.text())
4065					.then(data => {
4066						const childElement = document.createElement('div');
4067						childElement.innerHTML = data;
4068						const text = element.querySelector('.embeded-text');
4069						text.appendChild(childElement);
4070					});
4071			}
4072		}
4073
4074		observer.observe(body, {
4075			subtree: true,
4076			childList: true,
4077			characterData: true
4078		});
4079	}
4080
4081	if (document.readyState !== 'loading') {
4082		onDocumentReady();
4083	} else {
4084		document.addEventListener('DOMContentLoaded', onDocumentReady);
4085	}
4086})();
4087
4088;{
4089	let oneTrustLoaded = false
4090	const OptanonWrapper = () => {
4091		let accept = false
4092		const { OnetrustActiveGroups, OneTrust } = window
4093
4094		if (!oneTrustLoaded) {
4095			window.dispatchEvent(new CustomEvent('OneTrustReady'))
4096			oneTrustLoaded = true
4097		}
4098
4099		if (!OneTrust.IsAlertBoxClosedAndValid()) {
4100			const event = new CustomEvent('onetrustAlertBoxDisplayed')
4101			window.dispatchEvent(event)
4102		}
4103
4104		if (
4105			typeof OnetrustActiveGroups === 'undefined' ||
4106			OnetrustActiveGroups === undefined ||
4107			OnetrustActiveGroups.includes('C0004')
4108		) {
4109			accept = true
4110		}
4111
4112		const event = new CustomEvent('onetrustChanged', { detail: { accept } })
4113
4114		window.dispatchEvent(event)
4115	}
4116
4117	window.OptanonWrapper = OptanonWrapper
4118}
4119
4120;{
4121
4122	const setCompliance = (e) => {
4123		const { ppms } = window
4124		if (typeof ppms === 'undefined') {
4125			return
4126		}
4127
4128		if (ppms && ppms.cm && ppms.cm.api) {
4129			const acceptedCookie = e.detail.accept
4130      const status = acceptedCookie ? 1 : -1;
4131				ppms.cm.api(
4132					"setComplianceSettings",
4133					{
4134						consents: {
4135							analytics: {
4136								status: status,
4137							},
4138						},
4139					},
4140					function onSuccess() {
4141					},
4142					function onError() {
4143					}
4144				)
4145			}
4146	}
4147
4148	const initCompliance = () => {
4149		const { ppms } = window
4150		if (typeof ppms === 'undefined') {
4151			return
4152		}
4153
4154		if (ppms && ppms.cm && ppms.cm.api) {
4155			ppms.cm.api(
4156				"setInitialComplianceSettings",
4157				{
4158					consents: ["analytics"],
4159				},
4160				function onSuccess() {
4161				},
4162				function onError() {
4163				}
4164			)
4165		}
4166	}
4167
4168	const initPiwikPro = () => {
4169			window.addEventListener('onetrustAlertBoxDisplayed', initCompliance)
4170			window.addEventListener('onetrustChanged', setCompliance)
4171	}
4172
4173	if (document.readyState !== 'loading') {
4174		initPiwikPro()
4175	} else {
4176		document.addEventListener('DOMContentLoaded', initPiwikPro)
4177	}
4178}
4179

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.