PageSourceSearch

https://corporate.vanguard.com/etc.clientlibs/corporatesite/client….ACSHASHc7d7732a60f7b377dba8b50b85305bfc.js

js vanguard.com collected 2026-09-24 08:24:16 UTC 466,289 bytes, 10,034 lines download raw bytes

1class Player {
2    static instances = [];
3    static intersectionObserver = null;
4    static visibilityListenerRegistered = false;
5    #interval = null;
6    #mediaName = "";
7    #playerLoadTimeout = window.Granite?.author ? 2000 : 15000; // 2s for sites authors
8                                                                // or 15 seconds timeout for Wistia to load based on
9    #wistiaCanPlay = false;
10    #youtubeVideoId = null;
11    #youTubeFallbackInstance = null;
12    #fallbackActivated = false;
13
14    constructor(mediaID, element, button) {
15        this.mediaID = mediaID;
16        this.button = button;
17        this.wistiaPlayer = element;
18        this.#mediaName = element.getAttribute("data-media-name");
19        this.#youtubeVideoId = element.getAttribute("data-youtube-videoid");
20        Player.instances.push(this);
21        this.init();
22        this.#startWistiaLoadMonitor();
23    }
24
25    init() {
26        this.playerPlayerListener();
27        this.endPlayerListener();
28    }
29
30    // -------------------------------------------------------------------------
31    // Wistia load monitor
32    // -------------------------------------------------------------------------
33
34    // Listens for can-play to confirm the media is playable and cancels the
35    // fallback timeout. If can-play never fires within #playerLoadTimeout,
36    // the fallback is triggered.
37    //
38    // VideoPlayer starts a silent muted probe (via _startProbe()) so that
39    // can-play fires without requiring a user gesture. Banner mode does not
40    // need this — Wistia autoplays and loads naturally.
41    #startWistiaLoadMonitor() {
42        let timeoutHandle = null;
43
44        const onWistiaReady = (eventName) => {
45            if (this.#wistiaCanPlay) return;
46            this.#wistiaCanPlay = true;
47            console.debug(eventName + ' FIRED for: ', this.mediaID);
48            clearTimeout(timeoutHandle);
49            this._onWistiaConfirmed();
50        };
51
52        this.wistiaPlayer.addEventListener('can-play',          () => onWistiaReady('can-play'));
53        this.wistiaPlayer.addEventListener('api-ready',         () => onWistiaReady('api-ready'));
54        this.wistiaPlayer.addEventListener('loaded-media-data', () => onWistiaReady('loaded-media-data'));
55        this.wistiaPlayer.addEventListener('loaded-data',       () => onWistiaReady('loaded-data'));
56        this.wistiaPlayer.addEventListener('loaded-metadata',   () => onWistiaReady('loaded-metadata'));
57
58        timeoutHandle = setTimeout(() => {
59            if (!this.#wistiaCanPlay) {
60                this.#triggerFallback('Wistia player load timeout - can-play event not received');
61            }
62        }, this.#playerLoadTimeout);
63    }
64
65    // -------------------------------------------------------------------------
66    // Subclass hooks — override in VideoPlayer / VideoBanner as needed
67    // -------------------------------------------------------------------------
68
69    // Returns true while a silent probe is in progress.
70    // Used to suppress analytics and side-effects that must not fire during the probe.
71    // Override in VideoPlayer to return this.#probePending.
72    _isProbing() { return false; }
73
74    // Returns true for subclasses that should be paused when another player on the
75    // page starts playing. Override to return true in VideoPlayer.
76    // Default false — banner mode should not be interrupted by a VideoPlayer starting.
77    _shouldPauseOnOtherPlay() { return false; }
78
79    // Returns true for subclasses that should be paused when the browser tab is hidden.
80    // Override to return true in VideoPlayer.
81    // Default false — banner mode has its own visibility handling.
82    _shouldPauseOnVisibilityHide() { return false; }
83
84    // Called when can-play fires, confirming Wistia loaded successfully.
85    // Override in VideoPlayer to restore state after the silent probe.
86    _onWistiaConfirmed() {}
87
88    // Initiates a silent muted probe: mutes and calls play() so that can-play
89    // fires on page load without a user gesture. Only called by VideoPlayer after
90    // customElements.whenDefined('wistia-player') resolves.
91    _startProbe() {
92        console.debug("Muting the video and playing it");
93        this.wistiaPlayer.classList.remove('player-hidden');
94        this.wistiaPlayer.muted = true;
95        this.wistiaPlayer.play();
96    }
97
98    // -------------------------------------------------------------------------
99    // Fallback
100    // -------------------------------------------------------------------------
101
102    #triggerFallback(reason) {
103        if (this.#fallbackActivated) return;
104
105        // In WCM edit mode: surface the error to the author instead of activating
106        // the YouTube fallback. Authors need to know the media ID is wrong.
107        if (window.Granite?.author) {
108            this.#showAuthorError();
109        }
110
111
112        // The following code is intentionally disabled to prevent the YouTube fallback from activating in PROD live.
113        // It can be re-enabled in the future.
114        /*
115        this.#fallbackActivated = true;
116        if (!this.#youtubeVideoId) return;
117        const analyticsDiv = document.getElementById('analytics-detail
117s');
118        if (analyticsDiv && analyticsDiv.getAttribute('data-wistia-youtube-fallback-enabled') === 'false') return;
119        this.activateFallback();
120        */
121    }
122
123    #showAuthorError() {
124        const wrapper = this.wistiaPlayer.closest('.wistia-player-wrapper') || this.wistiaPlayer.parentNode;
125
126        const title = document.createElement('div');
127        title.className = 'wistia-author-error__title';
128        title.textContent = 'Video player error';
129
130        const message = document.createElement('div');
131        message.className = 'wistia-author-error__message';
132        message.textContent = 'The player could not be loaded. Please verify the Media ID is correct.';
133
134        const mediaId = document.createElement('div');
135        mediaId.className = 'wistia-author-error__media-id';
136        mediaId.textContent = 'Media ID: ' + this.mediaID;
137
138        const overlay = document.createElement('div');
139        overlay.className = 'wistia-author-error';
140        overlay.appendChild(title);
141        overlay.appendChild(message);
142        overlay.appendChild(mediaId);
143
144        wrapper.appendChild(overlay);
145    }
146
147    // Override in subclasses to implement mode-specific fallback behaviour.
148    activateFallback() {
149    }
150
151    // -------------------------------------------------------------------------
152    // Accessors
153    // -------------------------------------------------------------------------
154
155    isFallbackActive() { return this.#fallbackActivated; }
156    getYoutubeVideoId() { return this.#youtubeVideoId; }
157    getMediaName() { return this.#mediaName; }
158    setFallbackInstance(instance) { this.#youTubeFallbackInstance = instance; }
159    getFallbackInstance() { return this.#youTubeFallbackInstance; }
160
161    // -------------------------------------------------------------------------
162    // Playback
163    // -------------------------------------------------------------------------
164
165    play() {
166        if (this.isFallbackActive() && this.getFallbackInstance()) {
167            this.getFallbackInstance().play();
168        } else {
169            this.wistiaPlayer.play();
170        }
171    }
172
173    pause() {
174        if (this.isFallbackActive() && this.getFallbackInstance()) {
175            this.getFallbackInstance().pause();
176        } else {
177            this.wistiaPlayer.pause();
178        }
179    }
180
181    // -------------------------------------------------------------------------
182    // Event listeners
183    // -------------------------------------------------------------------------
184
185    playerPlayerListener() {
186        this.wistiaPlayer.addEventListener('play', () => {
187            // Suppress during the silent probe — the play event fires when startProbe()
188            // calls play(), but analytics and pause-others must not trigger at that point.
189            if (this._isProbing()) return;
190
191            this.trackProgress();
192            Player.instances.forEach(player => {
193                if (player !== this && player._shouldPauseOnOtherPlay()) {
194                    player.pause();
195                }
196            });
197        });
198    }
199
200    endPlayerListener() {
201        this.wistiaPlayer.addEventListener('ended', () => {
202            // Suppress during the silent probe — a rapid play→pause from the probe
203            // can fire ended on some browsers, which would incorrectly show the banner
204            // and send a mediaComplete analytics event.
205            if (this._isProbing()) return;
206
207            $(this.button).closest('.wistia-banner').removeClass('banner-hidden');
208            $(this.wistiaPlayer).addClass('player-hidden');
209            this.sendAdobeEvent(this.#mediaName, 'mediaComplete');
210        });
211    }
212
213    // -------------------------------------------------------------------------
214    // Analytics
215    // -------------------------------------------------------------------------
216
217    trackProgress() {
218        const milestones = [0, 25, 50, 75];
219        const reached = { 0: false, 25: false, 50: false, 75: false };
220        if (this.#interval !== null) return;
221        this.#interval = setInterval(() => {
222            const currentTime = this.wistiaPlayer.currentTime;
223            const duration = this.wistiaPlayer.duration;
224            if (!duration) return;
225            const percent = (currentTime / duration) * 100;
226            milestones.forEach(milestone => {
227                if (!reached[milestone] && percent >= milestone) {
228                    reached[milestone] = true;
229                    if (milestone === 0) {
230                        this.sendAdobeEvent(this.#mediaName, 'mediaPlay');
231                    } else {
232                        switch (milestone) {
233                            case 25: this.sendAdobeEvent(this.#mediaName, 'media25PercentPlayed'); break;
234                            case 50: this.sendAdobeEvent(this.#mediaName, 'media50PercentPlayed'); break;
235                            case 75: this.sendAdobeEvent(this.#mediaName, 'media75PercentPlayed'); break;
236                        }
237                    }
238                }
239            });
240            if (percent > 98) {
241                clearInterval(this.#interval);
242                this.#interval = null;
243                $(this.button).closest('.wistia-banner').removeClass('banner-hidden');
244            }
245        }, 500);
246    }
247
248    sendAdobeEvent(mediaNm, mediaEvent) {
249        window.dataLayer = window.dataLayer || [];
250        window.dataLayer.push({
251            'event': mediaEvent,
252            'media': {
253                'mediaName': mediaNm,
254                'mediaType': 'video'
255            }
256        });
257    }
258
259    // -------------------------------------------------------------------------
260    // Lifecycle
261    // -------------------------------------------------------------------------
262
263    // Clears the analytics interval and removes this instance from the shared
264    // instances array. Call when the component is removed from the page.
265    destroy() {
266        if (this.#interval !== null) {
267            clearInterval(this.#interval);
268            this.#interval = null;
269        }
270        const index = Player.instances.indexOf(this);
271        if (index !== -1) Player.instances.splice(index, 1);
272    }
273
274    // -------------------------------------------------------------------------
275    // Static — IntersectionObserver + visibility
276    // -------------------------------------------------------------------------
277
278    static registerWithObserver(player) {
279        if (!Player.intersectionObserver) {
280            Player.intersectionObserver = new IntersectionObserver((entries) => {
281                entries.forEach(entry => {
282                    if (!entry.isIntersecting) {
283                        const currentPlayer = Player.instances.find(p => p.wistiaPlayer === entry.target);
284                        if (currentPlayer && currentPlayer._shouldPauseOnVisibilityHide() && currentPlayer.wistiaPlayer.state === 'playing') {
285                            currentPlayer.pause();
286                        }
287                    }
288                });
289            }, { threshold: 0.1 });
290        }
291        Player.intersectionObserver.observe(player.wistiaPlayer);
292
293        if (!Player.visibilityListenerRegistered) {
294            document.addEventListener('visibilitychange', Player.handleVisibilityChange);
295            Player.visibilityListenerRegistered = true;
296        }
297    }
298
299    static handleVisibilityChange() {
300        if (document.hidden) {
301            Player.instances.forEach(player => {
302                if (
303                    player._shouldPauseOnVisibilityHide() &&
304                    player.wistiaPlayer.getAttribute('data-outOfViewBehavior') === 'pause' &&
305                    player.wistiaPlayer.state === 'playing'
306                ) {
307                    player.pause();
308                }
309            });
310        }
311    }
312}
313
314class VideoPlayer extends Player {
315    // true while the silent probe is running (between _startProbe() and _onWistiaConfirmed()).
316    // Guards analytics and pause-others logic in Player so they don't fire during the probe.
317    // Also used by playButtonClickListener to detect a click before can-play fires.
318    #probePending = false;
319
320    constructor(mediaID, element, button) {
321        super(mediaID, element, button);
322        // #probePending is safe to write here — private fields initialize after super() returns.
323        // Cannot do this in init() because Player's constructor calls init() before VideoPlayer's
324        // private fields exist, causing a "Cannot write private member" error.
325        //
326        // Gate on customElements.whenDefined() — a standard browser Promise that resolves as soon
327        // as the Wistia script registers the wistia-player element class, at which point play()
328        // exists on every instance. If Wistia's script fails to load entirely, whenDefined never
329        // resolves, the probe never starts, and the timeout fires as the backstop.
330        this.#probePending = true;
331        customElements.whenDefined('wistia-player').then(() => {
332            console.debug("Probing video playback started");
333            this._startProbe();
334        });
335    }
336
337    init() {
338        super.init();
339        this.playButtonClickListener();
340
341        if (this.wistiaPlayer.getAttribute('data-outOfViewBehavior') === 'pause') {
342            Player.registerWithObserver(this);
343        }
344
345        // Attach transcript show/hide functionality only if the button exists
346        this.showHideTranscript();
347
348        if (!hasOneTrustTargetingConsent()) {
349            this.wistiaPlayer.setAttribute('do-not-track', 'true');
350        }
351    }
352
353    // -------------------------------------------------------------------------
354    // Subclass hook overrides
355    // -------------------------------------------------------------------------
356
357    _isProbing() { return this.#probePending; }
358
359    _shouldPauseOnOtherPlay() { return true; }
360
361    _shouldPauseOnVisibilityHide() { return true; }
362
363    // Called when can-play fires. Restores the player to its pre-probe state:
364    // pause and unmute so it sits ready for the user's first click.
365    // If the user already clicked during the probe (#probePending cleared by the
366    // click handler), skip the pause/unmute — the player is already playing audibly.
367    _onWistiaConfirmed() {
368        if (!this.#probePending) {
369            console.debug('[VideoPlayer] probe: user already clicked — skipping restore');
370            return;
371        }
372        this.#probePending = false;
373        this.wistiaPlayer.pause();
374        this.wistiaPlayer.muted = false;
375    }
376
377    // -------------------------------------------------------------------------
378    // Fallback
379    // -------------------------------------------------------------------------
380
381    activateFallback() {
382        const youtubeVideoId = this.getYoutubeVideoId();
383        if (!youtubeVideoId) {
384            console.error('[VideoPlayer] No YouTube video ID for fallback');
385            logClientError("ERROR", "No YouTube video ID available for fallback", "VideoPlayer.js");
386            return;
387        }
388
389        console.debug('[VideoPlayer] Activating YouTube fallback for:', youtubeVideoId);
390
391        // Get the banner element (wistia-banner)
392        const bannerElement = $(this.button).closest('.wistia-player-wrapper').find('.wistia-banner')[0];
393
394        // Create the fallback player
395        const fallbackInstance = WistiaFallback.createVideoPlayerFallback({
396            youtubeVideoId: youtubeVideoId,
397            wistiaElement: this.wistiaPlayer,
398            bannerElement: bannerElement,
399            button: this.button,
400            mediaName: this.getMediaName(),
401            sendAdobeEvent: this.sendAdobeEvent.bind(this),
402            onPlay: () => { console.debug('[VideoPlayer] YouTube fallback playing'); },
403            onPause: () => { console.debug('[VideoPlayer] YouTube fallback paused'); },
404            onEnd: () => {
405                console.debug('[VideoPlayer] YouTube fallback ended');
406                $(bannerElement).removeClass('banner-hidden');
407            }
408        });
409
410        this.setFallbackInstance(fallbackInstance);
411    }
412
413    // -------------------------------------------------------------------------
414    // Playback
415    // -------------------------------------------------------------------------
416
417    play() {
418        if (this.isFallbackActive() && this.getFallbackInstance()) {
419            this.getFallbackInstance().play();
420        } else {
421            this.wistiaPlayer.play();
422        }
423    }
424
425    // -------------------------------------------------------------------------
426    // Event listeners
427    // -------------------------------------------------------------------------
428
429    playButtonClickListener() {
430        this.button.click(function() {
431            $(this.button).closest('.wistia-banner').addClass('banner-hidden');
432            $(this.wistiaPlayer).removeClass('player-hidden');
433
434            if (this.#probePending) {
435                // Probe is still running — player is already playing but muted.
436                // Mark probe done and unmute so audio starts immediately.
437                // _onWistiaConfirmed will see #probePending=false and skip the pause.
438                console.debug('[VideoPlayer] click during probe — unmuting in-progress probe');
439                this.#probePending = false;
440                this.wistiaPlayer.muted = false;
441            } else {
442                this.play();
443            }
444        }.bind(this));
445    }
446
447    // -------------------------------------------------------------------------
448    // Transcript
449    // -------------------------------------------------------------------------
450
451    showHideTranscript() {
452        const isInsideTimelineSlide = $(this.button).closest('.cmp-hrztimelineslide').length > 0;
453        const wistiaPlayerContainer = $(this.button).closest('.main-wistia-container');
454        if (isInsideTimelineSlide) {
455            this.handleTranscriptModal(wistiaPlayerContainer);
456        } else {
457            this.handleTranscript(wistiaPlayerContainer);
458        }
459    }
460
461    handleTranscriptModal(wistiaPlayerContainer) {
462        const slideTranscriptButton = wistiaPlayerContainer.find('.wistia-details-container .transcriptShowHide');
463        if(slideTranscriptButton.length > 0){
464
465            //update button styling
466            slideTranscriptButton.removeClass('transcriptShowHide');
467            slideTranscriptButton.addClass('wistia-transcript-modal-button');
468
469            //remove inline transcript container
470            wistiaPlayerContainer.find('.wistia-transcript-container').remove();
471
472            this.attachTranscriptModalButtonClick(slideTranscriptButton);
473
474            const closePreferences = wistiaPlayerContainer.find('.video-transcript-modal--closeButton');
475            this.attachTranscriptModalCloseClickListener(closePreferences);
476        }
477    }
478
479    handleTranscript(wistiaPlayerContainer) {
480        const transcriptButton = wistiaPlayerContainer.find('.wistia-details-container .transcriptShowHide');
481        wistiaPlayerContainer.find('.video-transcript-modal').remove();
482        if (transcriptButton.length > 0) {
483            transcriptButton.click(function() {
484                const transcriptContent = wistiaPlayerContainer.find('.wistia-transcript-container');
485                const showLabel = transcriptButton.attr('data-showTranscript-label');
486                const hideLabel = transcriptButton.attr('data-hideTranscript-label');
487                transcriptContent.toggleClass('wistia-transcript-open');
488                if (transcriptContent.hasClass('wistia-transcript-open')) {
489                    transcriptButton.find('.player-transcript-label').text(hideLabel);
490                    transcriptContent.attr('aria-hidden', 'false');
491                    transcriptContent.css('display', 'block');
492                } else {
493                    transcriptButton.find('.player-transcript-label').text(showLabel);
494                    transcriptContent.attr('aria-hidden', 'true');
495                    transcriptContent.css('display', 'none');
496                }
497            });
498        }
499    }
500
501    attachTranscriptModalButtonClick(slideTranscriptButton) {
502        slideTranscriptButton.click(() => {
503            const modal = document.querySelector(`#wistia-modal-video-${this.mediaID}`);
504            if (modal) {
505                this.#handleEscapeModal(`#wistia-modal-video-${this.mediaID}`);
506                this.#sendButtonAdobeAnalytics(
507                    $(document).find('title')[0].text.split('|')[0].trim(),
508                    'Open the video transcript modal',
509                    'button'
510                );
511                setTimeout(() => { modal.showModal(); document.querySelector('body').style.overflow = 'hidden'; }, 180);
512                setTimeout(() => { $(modal).fadeIn(700); }, 250);
513            } else {
514                console.warn(`Modal with ID #wistia-modal-video-${this.mediaID} not found.`);
515            }
516        });
517    }
518
519    #handleEscapeModal(targetModalSelector) {
520        const targetModal = document.querySelector(targetModalSelector);
521
522        // Unbind previous handlers for this namespace
523        $(document).off('keydown.wistiaTranscriptModal');
524        $(targetModalSelector).off('click.wistiaTranscriptModal');
525        $(document).on('keydown.wistiaTranscriptModal', function(event) {
526            if (event.key === 'Escape' && targetModal.hasAttribute('open')) {
527                this.#closeModal(targetModal);
528            }
529        }.bind(this));
530
531        $(targetModalSelector).on('click.wistiaTranscriptModal',function (event) {
532            if (event.target.nodeName === 'DIALOG') {
533                this.#closeModal(targetModal);
534            }
535        }.bind(this));
536    }
537
538    #closeModal(modal) {
539        $(modal).fadeOut(300);
540        setTimeout(() => {
541            modal.close();
542            document.querySelector('body').style.overflow = '';
543        }, 350);
544    }
545
546    attachTranscriptModalCloseClickListener(closePreferences) {
547        closePreferences.click(function() {
548            const modal = $(this.button).closest('.main-wistia-container').find('.video-transcript-modal')[0];
549            if (modal) this.#closeModal(modal);
550            this.#sendButtonAdobeAnalytics(
551                $(document).find('title')[0].text.split('|')[0].trim(),
552                'Close the video transcript modal',
553                'button'
554            );
555        }.bind(this));
556    }
557
558    #sendButtonAdobeAnalytics(location, name, type) {
559        window.dataLayer = window.dataLayer || [];
560        window.dataLayer.push({
561            "event": "callToAction", //e111
562            "callToAction": {
563                "location": location, //v103
564                "ctaName": name, //v104
565                "ctaType": type //v111
566            }
567        });
568    }
569}
570class VideoBanner extends Player {
571    constructor(mediaID, element, button) {
572        super(mediaID, element, button);
573    }
574
575    init(){
576        this.wistiaPlayer.addEventListener('api-ready', () => {
577            this.play();
578        });
579        this.overlayClickListener();
580        this.endPlayerListener();
581    }
582
583    play () {
584        this.wistiaPlayer.play();
585    }
586
587    pause () {
588        this.wistiaPlayer.pause();
589    }
590
591    endPlayerListener () {
592        //attach end event
593        this.wistiaPlayer.addEventListener("ended", () => {
594            this.play();
595        });
596    }
597
598    activateFallback() {
599        const youtubeVideoId = this.getYoutubeVideoId();
600        if (!youtubeVideoId) {
601            console.error('[VideoBanner] No YouTube video ID for fallback');
602            logClientError("ERROR", "No YouTube video ID available for banner fallback", "VideoBanner.js");
603            return;
604        }
605
606        console.debug('[VideoBanner] Activating YouTube fallback for:', youtubeVideoId);
607
608        const wrapper = $(this.wistiaPlayer).closest('.wistia-player-wrapper');
609        const overlayElement = wrapper.find('.player-overlay')[0];
610
611        WistiaFallback.createVideoBannerFallback({
612            youtubeVideoId: youtubeVideoId,
613            wistiaElement: this.wistiaPlayer,
614            overlayElement: overlayElement,
615            button: this.button,
616            mediaName: this.getMediaName()
617        });
618    }
619
620    overlayClickListener () {
621        this.button.closest(".player-overlay").click(function(e) {
622            if (this.wistiaPlayer.state === 'playing' && this.button.find("span").hasClass("pause-icon")) {
623                this.button.find("span").removeClass("pause-icon");
624                this.button.find("span").addClass("play-icon");
625                this.button.attr('aria-label', 'Play video');
626                this.button.attr('title', 'Play video');
627                this.pause();
628            } else if(this.wistiaPlayer.state === 'paused' && this.button.find("span").hasClass("play-icon")){
629                this.button.find("span").removeClass("play-icon");
630                this.button.find("span").addClass("pause-icon");
631                this.button.attr('aria-label', 'Pause video');
632                this.button.attr('title', 'Pause video');
633                this.play();
634            }
635        }.bind(this));
636    }
637}
638window.WistiaPlayerFactory = {
639    createPlayer(mediaID, element, button) {
640        if ($(element).data('type') === 'videoBanner') {
641            return new VideoBanner(mediaID, element, button);
642        } else {
643            return new VideoPlayer(mediaID, element, button);
644        }
645    },
646
647    // function being used by the editor to reinitialize the players in the page after an editable update,
648    // to make sure that the players are correctly initialized in the new DOM structure
649    reinitialize() {
650        Player.instances
651            .filter(p => !document.body.contains(p.wistiaPlayer))
652            .forEach(p => p.destroy());
653
654        $('wistia-player[media-id]').not('[data-type="audioPlayer"]').each(function() {
655            const element = this;
656            const alreadyInitialized = Player.instances.some(p => p.wistiaPlayer === element);
657            if (!alreadyInitialized) {
658                const mediaID = $(element).attr('media-id');
659                const button = $(element).closest('.wistia-player-wrapper').find('button.play-pause-btn');
660                WistiaPlayerFactory.createPlayer(mediaID, element, button);
661            }
662        });
663    }
664};
665
666$(document).ready(function(){
667    $('wistia-player[media-id]').not('[data-type="audioPlayer"]').each(function() {
668        const mediaID = $(this).attr('media-id');
669        const button = $(this).closest('.wistia-player-wrapper').find('button.play-pause-btn');
670        WistiaPlayerFactory.createPlayer(mediaID, this, button);
671    });
672});
673
674/**
675 * Wistia Audio Player
676 *
677 * Three-part UI:
678 * 1. Inline trigger — "Podify AI / Listen as a podcast" with play button
679 * 2. Bottom sticky player — shows on play with title, expand/collapse, play/pause, progress bar
680 * 3. Full-screen expanded panel — title, date, transcript (sticky player overlays at bottom)
681 *
682 * Uses Wistia <wistia-player> web component for audio playback.
683 */
684
685const MILESTONE_EVENTS = {
686    0:  'mediaPlay',
687    25: 'media25PercentPlayed',
688    50: 'media50PercentPlayed',
689    75: 'media75PercentPlayed'
690};
691
692// Below this percentage the playhead counts as "at the start", so returning here
693// after real progress is treated as a replay rather than mid-track scrubbing.
694const RESTART_THRESHOLD_PERCENT = 1;
695
696const ICONS = {
697    expand:   '/etc.clientlibs/corporatesite/clientlibs/clientlib-site/resources/icons/button-icons/expand_White.svg',
698    collapse: '/etc.clientlibs/corporatesite/clientlibs/clientlib-site/resources/icons/button-icons/collapse_White.svg',
699    speed1x:  '/etc.clientlibs/corporatesite/clientlibs/clientlib-site/resources/icons/button-icons/1x_White.svg',
700    speed2x:  '/etc.clientlibs/corporatesite/clientlibs/clientlib-site/resources/icons/button-icons/2x_White.svg'
701};
702
703class PodifyPlayer {
704    constructor(trigger) {
705        this._initialised = false;
706        this._trigger = trigger;
707        this._wistiaPlayer = trigger.find('wistia-player.wistia-audio-player-element')[0];
708
709        if (!this._wistiaPlayer) return;
710
711        this._mediaName = this._wistiaPlayer.getAttribute('data-media-name') || '';
712        this._stickyPlayer = trigger.nextAll('.podify-sticky-player').first();
713        this._expandedPanel = trigger.nextAll('.podify-expanded').first();
714
715        if (!this._stickyPlayer.length || !this._expandedPanel.length) return;
716
717        this._isPlaying = false;
718        this._durationsSet = false;
719        this._progressInterval = null;
720        this._playbackRate = 1;
721        this._resetMilestones();
722
723        this._bindElements();
724        this._bindEvents();
725        this._initialised = true;
726    }
727
728    // -------------------------------------------------------------------------
729    // Init
730    // -------------------------------------------------------------------------
731
732    _bindElements() {
733        const trigger = this._trigger;
734        const sticky = this._stickyPlayer;
735        const expanded = this._expandedPanel;
736
737        this._body = $('body');
738
739        this._triggerPlayBtn   = trigger.find('.podify-trigger__play-btn');
740        this._triggerReadTime  = trigger.find('.podify-trigger__read-time');
741
742        this._stickyPlayBtn        = sticky.find('.podify-sticky-player__play-btn:not(.podify-sticky-player__play-btn--mobile)');
743        this._stickyPlayIcons      = sticky.find('.podify-sticky-player__play-icon');
744        this._stickyPauseIcons     = sticky.find('.podify-sticky-player__pause-icon');
745        this._mobilePlayBtn        = sticky.find('.podify-sticky-player__play-btn--mobile');
746        this._expandBtn            = sticky.find('.podify-sticky-player__expand-btn');
747        this._expandBtnImg         = this._expandBtn.find('img');
748        this._collapseBtn          = sticky.find('.podify-sticky-player__collapse-btn');
749        this._closeBtn             = sticky.find('.podify-sticky-player__close-btn');
750        this._rewindBtn            = sticky.find('.podify-sticky-player__rewind-btn');
751        this._forwardBtn           = sticky.find('.podify-sticky-player__forward-btn');
752        this._speedBtn             = sticky.find('.podify-sticky-player__speed-btn');
753        this._shareBtn             = sticky.find('.podify-sticky-player__share-btn');
754        this._stickyCurrentTime    = sticky.find('.podify-sticky-player__current-time');
755        this._stickyDuration       = sticky.find('.podify-sticky-player__duration');
756        this._stickySeekbar        = sticky.find('.podify-sticky-player__seekbar');
757        this._stickyProgressFilled = sticky.find('.podify-sticky-player__progress-filled');
758
759        this._backBtn              = expanded.find('.podify-expanded__back-btn');
760        this._transcriptWrapper    = expanded.find('.podify-expanded__transcript-wrapper')[0];
761        this._transcriptEl         = expanded.find('wistia-transcript')[0];
762    }
763
764    _bindEvents() {
765        const player = this._wistiaPlayer;
766
767        // Playback buttons — arrow functions preserve `this` as the class instance
768        this._triggerPlayBtn.on('click', () => this.togglePlayback());
769        this._stickyPlayBtn.on('click', () => this.togglePlayback());
770        this._mobilePlayBtn.on('click', (e) => {
771            e.stopPropagation();
772            this.togglePlayback();
773        });
774
775        // Panel controls
776        this._expandBtn.on('click', () => {
777            if (this._expandedPanel.hasClass('podify-expanded--visible')) {
778                this._hideExpandedPanel();
779            } else {
780                this._showExpandedPanel();
781            }
782        });
783        this._collapseBtn.on('click', () => this._hideExpandedPanel());
784        this._backBtn.on('click', () => this._hideExpandedPanel());
785        this._closeBtn.on('click', () => this._onClose());
786
787        // Transport controls
788        this._rewindBtn.on('click', () => {
789            player.currentTime = Math.max(0, player.currentTime - 15);
790            this._updateProgress();
791        });
792        this._forwardBtn.on('click', () => {
793            player.currentTime = Math.min(player.duration || 0, player.currentTime + 15);
794            this._updateProgress();
795        });
796        this._speedBtn.on('click', () =>
796 this._onSpeedToggle());
797        this._shareBtn.on('click', () => this._onShare());
798
799        // Seekbar
800        this._stickySeekbar.on('input', (e) => this._onSeek(e));
801
802        // Transcript auto-scroll
803        this._bindTranscriptScroll();
804
805        // Wistia player events — duration detection
806        customElements.whenDefined('wistia-player').then(() => {
807            this._setDurations();
808            ['can-play', 'api-ready', 'loaded-media-data', 'loaded-data', 'loaded-metadata'].forEach((evt) => {
809                player.addEventListener(evt, () => this._setDurations());
810            });
811        });
812
813        player.addEventListener('play', () => {
814            this._isPlaying = true;
815            this._setPlayingState();
816            this._showStickyPlayer();
817            if (!this._progressInterval) {
818                this._progressInterval = setInterval(() => this._updateProgress(), 250);
819            }
820        });
821
822        player.addEventListener('pause', () => {
823            this._stopPlayback();
824        });
825
826        player.addEventListener('ended', () => {
827            this._stopPlayback();
828            this._sendAdobeEvent('mediaComplete');
829            this._resetProgress();
830        });
831    }
832
833    // -------------------------------------------------------------------------
834    // Transcript scroll
835    // -------------------------------------------------------------------------
836
837    // register a MutationObserver to resolve the delay of the transcript
838    // shown on screen being delayed compared to the spoken audio.
839    // This will help to keep the transcript in sync with the audio.
840    _bindTranscriptScroll() {
841        if (!this._transcriptEl || !this._transcriptWrapper) return;
842
843        const attach = () => {
844            if (!this._transcriptEl.shadowRoot) return;
845            this._transcriptObserver = new MutationObserver(() => this._scrollToCue());
846            this._transcriptObserver.observe(this._transcriptEl.shadowRoot, {
847                subtree: true,
848                attributeFilter: ['part']
849            });
850        };
851
852        if (this._transcriptEl.shadowRoot) {
853            attach();
854        } else {
855            customElements.whenDefined('wistia-transcript').then(attach);
856        }
857    }
858
859    _scrollToCue() {
860        const cue = this._transcriptEl.shadowRoot.querySelector('[part~="current-cue"]');
861        if (!cue || !this._transcriptWrapper) return;
862
863        const wrapperRect = this._transcriptWrapper.getBoundingClientRect();
864        const cueRect = cue.getBoundingClientRect();
865        const targetScrollTop = this._transcriptWrapper.scrollTop + (cueRect.top - wrapperRect.top) - (wrapperRect.height / 2) + (cueRect.height / 2);
866
867        this._transcriptWrapper.scrollTo({ top: targetScrollTop, behavior: 'smooth' });
868    }
869
870    // -------------------------------------------------------------------------
871    // Playback
872    // -------------------------------------------------------------------------
873
874    _stopPlayback() {
875        this._isPlaying = false;
876        this._setPausedState();
877        clearInterval(this._progressInterval);
878        this._progressInterval = null;
879    }
880
881    togglePlayback() {
882        if (!this._initialised) return;
883        if (this._isPlaying) {
884            this._wistiaPlayer.pause();
885        } else {
886            this._wistiaPlayer.play();
887        }
888    }
889
890    _showStickyPlayer() {
891        this._stickyPlayer.addClass('podify-sticky-player--visible');
892        this._stickyPlayer.attr('aria-hidden', 'false');
893    }
894
895    _onClose() {
896        this._wistiaPlayer.pause();
897        if (this._expandedPanel.hasClass('podify-expanded--visible')) {
898            this._hideExpandedPanel();
899        }
900        this._stickyPlayer.removeClass('podify-sticky-player--visible');
901        this._stickyPlayer.attr('aria-hidden', 'true');
902    }
903
904    // -------------------------------------------------------------------------
905    // Panel
906    // -------------------------------------------------------------------------
907
908    _showExpandedPanel() {
909        this._expandedPanel.addClass('podify-expanded--visible');
910        this._expandedPanel.attr('aria-hidden', 'false');
911        this._body.css('overflow', 'hidden');
912        this._expandBtn.hide();
913        this._collapseBtn.show();
914        this._expandBtnImg.attr('src', ICONS.collapse);
915        this._expandBtn.attr({ 'aria-label': 'Collapse player', 'title': 'Collapse player', 'aria-expanded': 'true' });
916    }
917
918    _hideExpandedPanel() {
919        this._expandedPanel.removeClass('podify-expanded--visible');
920        this._expandedPanel.attr('aria-hidden', 'true');
921        this._body.css('overflow', '');
922        this._collapseBtn.hide();
923        this._expandBtn.show();
924        this._expandBtnImg.attr('src', ICONS.expand);
925        this._expandBtn.attr({ 'aria-label': 'Expand player', 'title': 'Expand player', 'aria-expanded': 'false' });
926    }
927
928    // -------------------------------------------------------------------------
929    // Progress
930    // -------------------------------------------------------------------------
931
932    _resetProgress() {
933        this._stickyProgressFilled.css('width', '0%');
934        this._stickySeekbar.val(0);
935        this._stickyCurrentTime.text('0:00');
936        this._resetMilestones();
937    }
938
939    _resetMilestones() {
940        this._milestoneReached = {};
941        Object.keys(MILESTONE_EVENTS).forEach((key) => {
942            this._milestoneReached[key] = false;
943        });
944        this._progressedBeyondStart = false;
945    }
946
947    // Only a return to the start *after* real progress counts as a replay, so
948    // pausing and resuming near the start cannot re-fire the milestones.
949    _maybeHandleRestart(percent) {
950        if (percent >= RESTART_THRESHOLD_PERCENT) {
951            this._progressedBeyondStart = true;
952        } else if (this._progressedBeyondStart) {
953            this._resetMilestones();
954        }
955    }
956
957    _updateProgress() {
958        const currentTime = this._wistiaPlayer.currentTime;
959        const duration = this._wistiaPlayer.duration;
960        if (!duration) return;
961
962        const percent = (currentTime / duration) * 100;
963
964        this._stickyProgressFilled.css('width', percent + '%');
965        this._stickySeekbar.val(percent);
966        this._stickyCurrentTime.text(PodifyPlayer._formatTime(currentTime));
967        this._maybeHandleRestart(percent);
968        this._trackMilestones(percent);
969    }
970
971    _onSeek(e) {
972        const duration = this._wistiaPlayer.duration;
973        if (!duration) return;
974        const seekPercent = parseFloat($(e.target).val());
975        const seekTime = (seekPercent / 100) * duration;
976        this._wistiaPlayer.currentTime = seekTime;
977        this._stickyProgressFilled.css('width', seekPercent + '%');
978        this._stickyCurrentTime.text(PodifyPlayer._formatTime(seekTime));
979        this._maybeHandleRestart(seekPercent);
980    }
981
982    // -------------------------------------------------------------------------
983    // UI state
984    // -------------------------------------------------------------------------
985
986    _setPlayingState() {
987        this._triggerPlayBtn.find('.podify-trigger__play-icon').hide();
988        this._triggerPlayBtn.find('.podify-trigger__pause-icon').show();
989        this._triggerPlayBtn.attr('aria-label', 'Pause podcast audio').attr('title', 'Pause podcast audio');
990
991        this._stickyPlayIcons.hide();
992        this._stickyPauseIcons.show();
993        this._stickyPlayBtn.attr('aria-label', 'Pause audio').attr('title', 'Pause audio');
994        this._mobilePlayBtn.attr('aria-label', 'Pause audio').attr('title', 'Pause audio');
995    }
996
997    _setPausedState() {
998        this._triggerPlayBtn.find('.podify-trigger__play-icon').show();
999        this._triggerPlayBtn.find('.podify-trigger__pause-icon').hide();
1000        this._triggerPlayBtn.attr('aria-label', 'Play podcast audio').attr('title', 'Play podcast audio');
1001
1002        this._stickyPlayIcons.show();
1003        this._stickyPauseIcons.hide();
1004        this._stickyPlayBtn.attr('aria-label', 'Play audio').attr('title', 'Play audio');
1005        this._mobilePlayBtn.attr('aria-label', 'Play audio').attr('title', 'Play audio');
1006    }
1007
1008    // -------------------------------------------------------------------------
1009    // Speed / Share
1010    // -------------------------------------------------------------------------
1011
1012    _onSpeedToggle() {
1013        this._playbackRate = this._playbackRate === 1 ? 2 : 1;
1014        this._wistiaPlayer.playbackRate = this._playbackRate;
1015        const speedImg = this._speedBtn.find('img');
1016        if (this._playbackRate === 2) {
1017            speedImg.attr('src', ICONS.speed2x);
1018            speedImg.attr('alt', '2x');
1019        } else {
1020            speedImg.attr('src', ICONS.speed1x);
1021            speedImg.attr('alt', '1x');
1022        }
1023    }
1024
1025    _onShare() {
1026        if (navigator.share) {
1027            navigator.share({ title: document.title, url: window.location.href }).catch(() => {});
1028        } else if (navigator.clipboard) {
1029            navigator.clipboard.writeText(window.location.href).catch(() => {});
1030        }
1031    }
1032
1033    // -------------------------------------------------------------------------
1034    // Analytics
1035    // -------------------------------------------------------------------------
1036
1037    _trackMilestones(percent) {
1038        Object.keys(MILESTONE_EVENTS).forEach((key) => {
1039            const milestone = Number(key);
1040            if (!this._milestoneReached[milestone] && percent >= milestone) {
1041                this._milestoneReached[milestone] = true;
1042                this._sendAdobeEvent(MILESTONE_EVENTS[key]);
1043            }
1044        });
1045    }
1046
1047    _sendAdobeEvent(mediaEvent) {
1048        window.dataLayer = window.dataLayer || [];
1049        window.dataLayer.push({
1050            'event': mediaEvent,
1051            'media': {
1052                'mediaName': this._mediaName,
1053                'mediaType': 'audio'
1054            }
1055        });
1056    }
1057
1058    // -------------------------------------------------------------------------
1059    // Utility
1060    // -------------------------------------------------------------------------
1061
1062    _setDurations() {
1063        if (this._durationsSet || !this._wistiaPlayer.duration) return;
1064        this._durationsSet = true;
1065        this._stickyDuration.text(PodifyPlayer._formatTime(this._wistiaPlayer.duration));
1066        this._triggerReadTime.text(PodifyPlayer._formatTriggerTime(this._wistiaPlayer.duration));
1067    }
1068
1069    static _formatTime(seconds) {
1070        if (!seconds || isNaN(seconds)) return '0:00';
1071        const mins = Math.floor(seconds / 60);
1072        const secs = Math.floor(seconds % 60);
1073        return mins + ':' + (secs < 10 ? '0' : '') + secs;
1074    }
1075
1076    static _formatTriggerTime(seconds) {
1077        if (!seconds || isNaN(seconds)) return '0 minutes 0 seconds';
1078        const mins = Math.floor(seconds / 60);
1079        const secs = Math.floor(seconds % 60);
1080        const parts = [];
1081        if (mins > 0) parts.push(mins + (mins === 1 ? ' minute' : ' minutes'));
1082        if (secs > 0) parts.push(secs + (secs === 1 ? ' second' : ' seconds'));
1083        return parts.join(' ');
1084    }
1085}
1086
1087$(document).ready(function () {
1088    $('.podify-trigger').each(function () {
1089        new PodifyPlayer($(this));
1090    });
1091});
1092
1093(() => {
1094    $(document).ready(function () {
1095        'use strict';
1096        const videoTranscriptButton = $('.video-transcript');
1097        const closePreferences = $('.video-transcript-modal__buttonContainer--close');
1098        const bodyElement = document.querySelector("body");
1099
1100        //need to extract this function as a global function so it can be reused without creating code duplication
1101        const handleClickAndEscapeModal = function handleEscapeModal(targetModalSelector) {
1102            const targetModal = document.querySelector(targetModalSelector);
1103
1104            // Unbind previous handlers for this namespace
1105            $(document).off('keydown.videoTranscriptModal');
1106            $(targetModalSelector).off('click.videoTranscriptModal');
1107
1108            $(document).on('keydown.videoTranscriptModal', function (event) {
1109                if (event.key === "Escape" && targetModal.hasAttribute("open")) {
1110                    closeModal(targetModal);
1111                }
1112            });
1113
1114            $(targetModalSelector).on('click.videoTranscriptModal',function (event) {
1115                if (event.target.nodeName === 'DIALOG') {
1116                    closeModal(targetModal);
1117                }
1118            });
1119        };
1120
1121        videoTranscriptButton.click(function () {
1122            let currentVideoID = $(this).data('video-modal-id');
1123            const modal = document.querySelector(`#modal-video-${currentVideoID}`);
1124
1125            if (modal) {
1126                handleClickAndEscapeModal(`#modal-video-${currentVideoID}`);
1127
1128
1129                setTimeout(function () {
1130                    modal.showModal();
1131                    bodyElement.style.overflow = 'hidden';
1132                    sendAdobeAnalytics($(document).find("title")[0].text.split("|")[0].trim(), "Open the video" +
1133                        " transcript modal", "button");
1134                }, 180);
1135                setTimeout(function () {
1136                    $(modal).fadeIn(700);
1137                }, 250);
1138            } else {
1139                console.warn(`Modal with ID #modal-video-${currentVideoID} not found.`);
1140            }
1141        });
1142
1143        function closeModal(modal) {
1144            $(modal).fadeOut(300);
1145
1146            setTimeout(() => {
1147                modal.close();
1148                bodyElement.style.overflow = '';
1149            }, 350);
1150        }
1151
1152        closePreferences.click(function () {
1153            const modal = $(this).closest('.video-transcript-modal')[0];
1154            if (modal) {
1155                closeModal(modal);
1156            }
1157
1158            sendAdobeAnalytics($(document).find("title")[0].text.split("|")[0].trim(), "Close the video transcript modal", "button");
1159        });
1160
1161        const sendAdobeAnalytics = function (location, name, type) {
1162            window.dataLayer = window.dataLayer || [];
1163            window.dataLayer.push({
1164                "event": "callToAction", //e111
1165                "callToAction": {
1166                    "location": location, //v103
1167                    "ctaName": name, //v104
1168                    "ctaType": type //v111
1169                }
1170            });
1171        }
1172    });
1173})();
1174
1175
1176
1177
1178$(document).ready(function(){
1179    $(document).on("scroll", stickyStroll);
1180    var linkItem = $('.fixed-item .link-item');
1181    if ( linkItem.length ) {
1182        linkItem.first().addClass('active');
1183    }
1184});
1185
1186function stickyStroll(event){
1187    if($('.timelinecontainer').offset()){
1188		stickyStrollTimeline(event);
1189    }else{
1190
1191     let stickyRow = $('.sticky-row');
1192     let scrollPosition = $(document).scrollTop();
1193
1194     stickyRow.each(function(){
1195         var currentElement = $(this);
1196         var currentElePosition = currentElement.position().top-70 ;
1197         if(currentElePosition <= scrollPosition && (currentElePosition + currentElement.height()) > scrollPosition){
1198             currentElement.addClass("fixed");
1199         } else {
1200             currentElement.removeClass("fixed");
1201         }
1202     });
1203    }
1204
1205}
1206
1207function stickyStrollTimeline(event){
1208
1209    let stickyRow = $('.sticky-row');
1210    let scrollPosition = $(document).scrollTop();
1211    let timeLinePosition = $('.timelinecontainer').offset().top;
1212    if(scrollPosition >= timeLinePosition){
1213        stickyRow.each(function(){
1214            let currentElement = $(this);
1215            let currentElePosition = currentElement.offset().top-200;
1216            if(currentElePosition <= scrollPosition && (currentElePosition + currentElement.height()) > scrollPosition){
1217                currentElement.addClass("fixed");
1218            } else {
1219                currentElement.removeClass("fixed");
1220            }
1221    	});
1222    } else {
1223        stickyRow.each(function(){
1224        let currentElement = $(this);
1225            currentElement.removeClass("fixed");
1226        });
1227    }
1228}
1229(() => {
1230    $(document).ready(function () {
1231        'use strict';
1232
1233        let currentEventId = null;
1234        const vanguardEffectCTA = $('.htl-tve-button');
1235        const closePreferences = $('.cmp-vanguard-effect-modal__buttonContainer--close');
1236        const bodyElement = document.querySelector("body");
1237
1238        //need to extract this function as a global function so it can be reused without creating code duplication
1239        const handleClickAndEscapeModal = function handleEscapeModal(targetModalSelector) {
1240            const targetModal = document.querySelector(targetModalSelector);
1241
1242            $(document).keydown(function (event) {
1243                if (event.key === "Escape" && targetModal.hasAttribute("open")) {
1244                    closeModal(targetModal);
1245                }
1246            });
1247
1248            $(targetModalSelector).click(function (event) {
1249                if (event.target.nodeName === 'DIALOG') {
1250                    closeModal(targetModal);
1251                }
1252            });
1253        };
1254
1255        vanguardEffectCTA.click(function () {
1256            currentEventId = $(this).attr('data-event-id');
1257            const modal = document.querySelector(`#modal-event-${currentEventId}`);
1258            if (modal) {
1259                handleClickAndEscapeModal(`#modal-event-${currentEventId}`);
1260                //$(modal).fadeIn(700);
1261
1262                 setTimeout(function () {
1263                    modal.showModal();
1264                    bodyElement.style.overflow = 'hidden';
1265                     sendAnalytics('Open the Vanguard Effect modal', modal);
1266                }, 680);
1267            } else {
1268                console.warn(`Modal with ID #modal-event-${currentEventId} not found.`);
1269            }
1270        });
1271
1272        function closeModal(modal) {
1273            $(modal).fadeOut(300);
1274            setTimeout(() => {
1275                modal.close();
1276                //Remove jQuery fadeOut display none
1277                $(modal).css('display', '');
1278                bodyElement.style.overflow = '';
1279                sendAnalytics('Close the Vanguard Effect modal', modal);
1280            }, 350);
1281        }
1282
1283        closePreferences.click(function () {
1284            const modal = $(this).closest('.cmp-vanguard-effect-modal')[0];
1285            if (modal) {
1286                closeModal(modal);
1287            }
1288        });
1289
1290        function sendAnalytics(ctaName, modal){
1291            let analyticsHeadline = modal.getAttribute('data-headline-analytics');
1292            window.dataLayer = window.dataLayer || [];
1293            window.dataLayer.push({
1294                "event": "callToAction", //e111
1295                "callToAction": {
1296                    "location": document.title.split("|")[0].replace("’", "'").trim() + " " + anal
1296yticsHeadline, //v103
1297                    "ctaName": ctaName, //v104
1298                    "ctaType": 'button' //v111
1299                }
1300            });
1301        }
1302    });
1303})();
1304
1305
1306
1307
1308(function () {
1309      "use strict";
1310      var topicVisualPositioning = {}; 
1311      if(properties["topicVisualSingleStart"] != undefined || properties["topicVisualSingleStart"] != null) { 
1312              if(properties["topicVisualSingleStart"] == 'true' && properties["topicVisualSpaceEvenly"] == 'false'){
1313                topicVisualPositioning.imageStyleElement = "col-12 col-md-6 order-md-1 col-lg-5 order-lg-1";
1314                topicVisualPositioning.textStyleElement = "col-10 offset-1 col-md-9 order-md-1 offset-md-2 col-lg-7 offset-lg-1 order-lg-2 cmp-topicVisualSingle__textContainerOnRight";
1315                topicVisualPositioning.colLGsixFlexUpdate = "flex: 0 0 44.666666%";
1316                topicVisualPositioning.topicTitleLGMarginBottom = "margin-bottom: 20%";
1317                //added new  
1318                topicVisualPositioning.textContainer = "col-12 col-md-6 order-md-1 col-lg-7 order-lg-1";
1319          }else if(properties["topicVisualSingleStart"] == 'false' && properties["topicVisualSpaceEvenly"] == 'false'){
1320                topicVisualPositioning.imageStyleElement = "col-12 col-md-6 order-md-2 col-lg-5 order-lg-2";
1321                topicVisualPositioning.textStyleElement = "col-10 offset-1 col-md-9 offset-md-1 order-md-1 col-lg-7 order-lg-1 cmp-topicVisualSingle__textContainerOnLeft";
1322                topicVisualPositioning.colLGsixFlexUpdate =  "";
1323                topicVisualPositioning.topicTitleLGMarginBottom = "margin-bottom: 20%;";
1324                //added new  
1325                topicVisualPositioning.textContainer = "col-12 col-md-6 order-md-1 col-lg-7 order-lg-1";
1326          }else if(properties["topicVisualSingleStart"] == 'true' && properties["topicVisualSpaceEvenly"] == 'true'){
1327                topicVisualPositioning.imageStyleElement = "col-12 col-md-6 order-md-1 col-lg-6 order-lg-1";
1328                topicVisualPositioning.textStyleElement = "col-10 offset-1 col-md-9 offset-md-1 order-md-2 col-lg-7 offset-lg-1 order-lg-2 cmp-topicVisualSingle__textContainerOnRightEvenly";
1329                topicVisualPositioning.colLGsixFlexUpdate =  "";
1330                topicVisualPositioning.topicTitleLGMarginBottom = "";
1331                //added new  
1332                topicVisualPositioning.textContainer = "col-12 col-md-6 order-md-2 col-lg-6 order-lg-2";
1333          }else if(properties["topicVisualSingleStart"] == 'false' && properties["topicVisualSpaceEvenly"] == 'true'){
1334                topicVisualPositioning.imageStyleElement = "col-12 col-md-6 order-md-2 col-lg-6 order-lg-2";
1335                topicVisualPositioning.textStyleElement = "col-10 offset-1 col-md-9 offset-md-1 order-md-1 col-lg-7 offset-lg-1 order-lg-1 cmp-topicVisualSingle__textContainerOnLeftEvenly";
1336                topicVisualPositioning.colLGsixFlexUpdate =  "";    
1337                topicVisualPositioning.topicTitleLGMarginBottom = "";
1338                //added new  
1339                topicVisualPositioning.textContainer = "col-12 col-md-6 col-lg-6 order-lg-1";
1340          }
1341      }
1342      return topicVisualPositioning;
1343  });
1344  
1345$(document).ready(function () {
1346  const pageTitle = document.querySelector('.vg-container .cmp-topicSectionOverview');
1347
1348  if( pageTitle != null && pageTitle != undefined){
1349    document.querySelector('.cmp-topicSectionOverview').style.paddingTop = "2.5%";
1350  }
1351});
1352$(document).ready(function () {
1353  const pageTitle = document.querySelector('.cmp-topicPageHeader .cmp-topicPageHeader__pageTitle');
1354
1355  if( pageTitle != null && pageTitle != undefined){
1356    document.querySelector('.cmp-topicPageOverview').classList.add("cmp-topicPageOverview__headline-padding");
1357  }
1358});
1359(function () {
1360
1361  "use strict";
1362  var designStyles = {};
1363
1364  let noPageTitle = resource.properties["topicHeaderPageTitle"];
1365
1366  if (noPageTitle) {
1367    designStyles.pageTitle = "padding-top : 0"
1368  } else if (!noPageTitle) {
1369    designStyles.pageTitle = "padding-top : 36";
1370  }
1371
1372  return designStyles;
1373
1374});
1375(function () {
1376
1377  "use strict";
1378  var designStyles = {};
1379
1380  let onLight = (resource.properties["bgColor"] == '#FFFFFF' || resource.properties["bgColor"] == '#E6E6E6') ? true : false;
1381  let noPageTitle = resource.properties["topicHeaderPageTitle"];
1382
1383  if (onLight) {
1384    designStyles.textColor = '#040505';
1385
1386  } else if (!onLight) {
1387    designStyles.textColor = '#FFFFFF';
1388  }
1389
1390  if (noPageTitle) {
1391    designStyles.pageTitle = "padding-top : 0"
1392  } else if (!noPageTitle) {
1393    designStyles.pageTitle = "padding-top : 36";
1394  }
1395
1396  return designStyles;
1397
1398});
1399$(document).ready(function() {
1400    var loadMoreIncrement = 10;
1401    var corpArticleFltrElements = [];
1402    var totalArticles = 0;
1403    var filteredArticles = 0;
1404    const filterChipContainer = $('.cmp-teaserFilter__filterChips');
1405
1406    // Configuration constants
1407    const FILTER_CONFIG = {
1408        MAX_FILTERS: 50,
1409        MAX_PARAM_LENGTH: 1000,
1410        MAX_SEGMENT_LENGTH: 100
1411    };
1412
1413    function getSelectedFilters() {
1414        const topicFilters = $('input[name="topicFilter"]:checked').map(function() {
1415            return $(this).val();
1416        }).get();
1417        const mediaFilters = $('input[name="mediaFilter"]:checked').map(function() {
1418            return $(this).val();
1419        }).get();
1420
1421        return topicFilters.concat(mediaFilters);
1422    }
1423
1424    function updateURLWithFilters() {
1425        const selectedFilters = getSelectedFilters();
1426        const url = new URL(window.location);
1427
1428        if (selectedFilters.length > 0) {
1429            // Limit to reasonable number of filters to prevent abuse
1430            const safeFilters = selectedFilters
1431                .slice(0, FILTER_CONFIG.MAX_FILTERS)
1432                .map(function(filter) { return encodeURIComponent(filter); });
1433            url.searchParams.set('filters', safeFilters.join(','));
1434        } else {
1435            url.searchParams.delete('filters');
1436        }
1437
1438        // Persist sort selection
1439        var activeSort = $('#teaserFilter-sort-select').val();
1440        if (activeSort && activeSort !== 'Newest') {
1441            url.searchParams.set('sort', activeSort);
1442        } else {
1443            url.searchParams.delete('sort');
1444        }
1445
1446        // Update URL without reloading the page
1447        window.history.replaceState({}, '', url);
1448    }
1449
1450    function loadSortFromURL(urlParams) {
1451        var sortParam = urlParams.get('sort');
1452        if (!sortParam) return;
1453
1454        var $select = $('#teaserFilter-sort-select');
1455        if ($select.find('option[value="' + sortParam + '"]').length === 0) return;
1456
1457        $select.val(sortParam);
1458        performSort(sortParam);
1459    }
1460
1461    function loadFiltersFromURL(urlParams) {
1462        try {
1463            const filtersParam = urlParams.get('filters');
1464            if (!filtersParam) return;
1465
1466            // Validate maximum length to prevent memory exhaustion attacks
1467            if (filtersParam.length > FILTER_CONFIG.MAX_PARAM_LENGTH) {
1468                console.warn(`Filters parameter exceeds maximum length of ${FILTER_CONFIG.MAX_PARAM_LENGTH} characters and will be ignored`);
1469                return;
1470            }
1471
1472            // Early validation: check for excessive comma-separated values
1473            const commaCount = (filtersParam.match(/,/g) || []).length;
1474            if (commaCount > FILTER_CONFIG.MAX_FILTERS) {
1475                console.warn('Filters parameter contains too many values and will be ignored');
1476                return;
1477            }
1478
1479            // Parse and validate filters
1480            const filters = filtersParam
1481                .split(',')
1482                .map(function(segment) {
1483                    try {
1484                        return decodeURIComponent(segment);
1485                    } catch (e) {
1486                        console.warn('Invalid URL encoding in filter:', segment);
1487                        return null;
1488                    }
1489                })
1490                .filter(Boolean)
1491                .filter(function(segment) {
1492                    return segment.length <= FILTER_CONFIG.MAX_SEGMENT_LENGTH;
1493                })
1494                .slice(0, FILTER_CONFIG.MAX_FILTERS);
1495
1496            if (filters.length === 0) return;
1497
1498            // Build checkbox lookup map (O(m) - one-time cost)
1499            const checkboxMap = new Map();
1500            $('input[name="topicFilter"]').each(function() {
1501                checkboxMap.set(this.value, this);
1502            });
1503            $('input[name="mediaFilter"]').each(function() {
1504                checkboxMap.set(this.value, this);
1505            });
1506
1507            // Apply filters using map lookup (O(n) - linear)
1508            filters.forEach(function(filter) {
1509                const checkbox = checkboxMap.get(filter);
1510                if (checkbox) {
1511                    checkbox.checked = true;
1512                }
1513            });
1514
1515            checkAndDisplay();
1516            displayFilterChips();
1517
1518        } catch (error) {
1519            console.error('Failed to load filters from URL:', error);
1520            logClientError("WARN", "Failed to load filters from URL: " + (error && error.message ? error.message : String(error)), "teaser-filter.js");
1521            // Clean up invalid URL params
1522            const url = new URL(window.location);
1523            url.searchParams.delete('filters');
1524            window.history.replaceState({}, '', url);
1525        }
1526    }
1527
1528    function clearCheckboxes(){
1529        let topicFilter = document.getElementsByName("topicFilter");
1530        let mediaFilter = document.getElementsByName("mediaFilter");
1531        topicFilter.forEach(element => {
1532            element.checked = false;
1533        });
1534        mediaFilter.forEach(element => {
1535            element.checked = false;
1536        });
1537    }
1538
1539    function clearChips() {
1540        filterChipContainer.empty();
1541        displayArticleCounts("topicFilter");
1542        displayArticleCounts("mediaFilter");
1543    }
1544
1545    $(".reset-all-filters").click(function(event) {
1546        $(this).hide();
1547        event.preventDefault();
1548        clearCheckboxes();
1549        clearChips();
1550        displayAllArticles();
1551        updateURLWithFilters();
1552        displayNoArticlesMessage();
1553        displayArticleCounts("topicFilter");
1554        displayArticleCounts("mediaFilter");
1555    });
1556
1557
1558    displayAllArticles();
1559
1560    filterChipContainer.on('click', '.clear-all-chip-filter', function() {
1561        clearCheckboxes();
1562        $('.reset-all-filters').hide();
1563        clearChips();
1564        displayAllArticles();
1565        updateURLWithFilters();
1566        displayNoArticlesMessage();
1567        displayArticleCounts("topicFilter");
1568        displayArticleCounts("mediaFilter");
1569    });
1570
1571    // Initialize on page load
1572
1573    function initializeFilters() {
1574        totalArticles = $(".corparticle-filter-list").length;
1575        $("#totalArticles").html(totalArticles);
1576        loadFiltersFromURL(new URL(window.location).searchParams);
1577
1578        if (getSelectedFilters().length === 0) {
1579
1580            displayAllArticles();
1581        }
1582    }
1583
1584    initializeFilters();
1585    displayArticleCounts("topicFilter");
1586    displayArticleCounts("mediaFilter");
1587
1588    $('input[name="topicFilter"]').change( function() {
1589        checkAndDisplay();
1590        displayFilterChips();
1591        updateURLWithFilters();
1592        displayNoArticlesMessage();
1593        displayArticleCounts("topicFilter");
1594        displayArticleCounts("mediaFilter");
1595    });
1596
1597    $('input[name="mediaFilter"]').change( function() {
1598        checkAndDisplay();
1599        displayFilterChips();
1600        updateURLWithFilters();
1601        displayNoArticlesMessage();
1602        displayArticleCounts("topicFilter");
1603        displayArticleCounts("mediaFilter");
1604    });
1605
1606    filterChipContainer.on('click', '.cmp-teaserFilter__filterChipClose', function() {
1607
1608        let filterToRemove = $(this).parent().text();
1609        // revert filter display name to original value using helper
1610        filterToRemove = revertFilterToOriginal(filterToRemove);
1611        $('input[name="topicFilter"][value="' + filterToRemove + '"]').prop('checked', false);
1612        $('input[name="mediaFilter"][value="' + filterToRemove + '"]').prop('checked', false);
1613        $(this).parent().remove();
1614
1615        if ( filterChipContainer.children().length <= 1 ) {
1616            filterChipContainer.empty();
1617        }
1618
1619        checkAndDisplay();
1620        updateURLWithFilters();
1621        displayArticleCounts("topicFilter");
1622        displayArticleCounts("mediaFilter");
1623        displayNoArticlesMessage();
1624    });
1625
1626    function formatFilters(filterType) {
1627        let filterElements = [];
1628        let filterChecked = $('input[name="' + filterType + '"]:checked');
1629
1630        if ( filterChecked.length > 0 ) {
1631            filterChecked.each( function() {
1632                let selectedFilter = $(this).val();
1633                // remove extra characters for display
1634                if (selectedFilter.includes("___")) {
1635                    selectedFilter = selectedFilter.replace(/___/g, " & ");
1636                } if (selectedFilter.includes("_")) {
1637                    selectedFilter = selectedFilter.replace(/_/g, " ");
1638                } if (selectedFilter === "Durable") {
1639                    selectedFilter = "About Vanguard";
1640                }
1641                filterElements.push(selectedFilter);
1642            });
1643        }
1644
1645        return filterElements;
1646    }
1647
1648    function displayFilterChips() {
1649        let filterElements = formatFilters("topicFilter").concat(formatFilters("mediaFilter"));
1650        filterChipContainer.empty();
1651
1652        // add chips for selected filters to page
1653        filterElements.forEach(function(filter) {
1654            let filterChipDiv = $('<div class="cmp-teaserFilter__filterChip"></div>');
1655            let filterChipClose = $('<button type="button" class="cmp-teaserFilter__filterChipClose" aria-label="Remove filter: ' + filter + '"></button>');
1656            filterChipDiv.text(filter);
1657            filterChipDiv.append(filterChipClose);
1658            filterChipContainer.append(filterChipDiv);
1659        });
1660
1661        // add clear all option to page if there are selected filters
1662        if ( filterElements.length > 0 ) {
1663            filterChipContainer.show();
1664            let clearAllDiv = $('<button type="button" aria-label="Clear all filters" class="clear-all-chip-filter"></button>');
1665            clearAllDiv.text("Clear all");
1666            filterChipContainer.append(clearAllDiv);
1667        }
1668    }
1669
1670    // Helper to revert a filter display name back to its original internal value
1671    function revertFilterToOriginal(displayName) {
1672        if (displayName === "About Vanguard") {
1673            return "Durable";
1674        }
1675        // Apply the same sequence of replacements as previously done inline
1676        let original = displayName.replace(/ & /g, "___");
1677        original = original.replace(/ /g, "_");
1678        return original;
1679    }
1680
1681    function filterArticles(filterType) {
1682        let filteredArticles = [];
1683
1684        $('input[name="' + filterType + '"]:checked').each( function() {
1685            let selectedArticleType = $(this).val();
1686            let classList = $("."+selectedArticleType).map(function() {
1687                return $(this).attr('class');
1688            }).get();
1689            filteredArticles =  Array.from(new Set(classList.concat([...filteredArticles])));
1690        });
1691
1692        return filteredArticles;
1693    }
1694
1695    function checkAndDisplay() {
1696        corpArticleFltrElements = [];
1697        let topicFilterChecked = $('input[name="topicFilter"]:checked').length > 0;
1698        let mediaFilterChecked = $('input[name="mediaFilter"]:checked').length > 0;
1699
1700        if (topicFilterChecked && mediaFilterChecked) {
1701            $(".reset-all-filters").show();
1702            let filteredByTopic = filterArticles("topicFilter");
1703            let filteredByMedia = filterArticles("mediaFilter");
1704            corpArticleFltrElements = filteredByTopic.filter(value => filteredByMedia.includes(value));
1705            $('.corparticle-filter-list').hide();
1706
1707        } else if (topicFilterChecked && !mediaFilterChecked) {
1708            $(".reset-all-filters").show();
1709            corpArticleFltrElements = filterArticles("topicFilter");
1710            $('.corparticle-filter-list').hide();
1711
1712        } else if (mediaFilterChecked && !topicFilterChecked) {
1713            $(".reset-all-filters").show();
1714            corpArticleFltrElements = filterArticles("mediaFilter");
1715            $('.corparticle-filter-list').hide();
1716
1717        } else {
1718            $(".reset-all-filters").hide();
1719            displayAllArticles();
1720        }
1721
1722        checkForLoadMore();
1723
1724    }
1725
1726    function displayAllArticles() {
1727        corpArticleFltrElements = $(".corparticle-filter-list").map(function() {
1728            return $(this).attr('class');
1729        }).get();
1730        totalArticles = corpArticleFltrElements.length;
1731        $("#totalArticles").html(totalArticles);
1732        checkForLoadMore();
1733    }
1734
1735    function displayArticleCounts(filterType) {
1736        let topicFilterChecked = $('input[name="topicFilter"]:checked').length > 0;
1737        let mediaFilterChecked = $('input[name="mediaFilter"]:checked').length > 0;
1738        let filteredByTopic = filterArticles("topicFilter");
1739        let filteredByMedia = filterArticles("mediaFilter");
1740        let articleFilterCounts = {};
1741
1742        $('input[name="' + filterType + '"]').each( function() {
1743            let filter = $(this).val();
1744            let count = 0;
1745
1746            if (!topicFilterChecked && !mediaFilterChecked) {
1747                count = $("."+filter).length;
1748                articleFilterCounts[filter] = count;
1749                grayZeroCountFilterItem($(this), count);
1750                $(this).parent().find(".cmp-teaserFilter__articleCount").html( "(" + count + ")" );
1751
1752            } else if (topicFilterChecked && mediaFilterChecked) {
1753                if ( $(this).is(':checked') ) {
1754                    let filteredArticles = filteredByTopic.filter(value => filteredByMedia.includes(value));
1755                    count = $("."+filter).filter(function() {
1756                        return filteredArticles.includes($(this).attr('class'));
1757                    }).length;
1758                } else if (filterType === 'mediaFilter') {
1759                    count = $("."+filter).filter(function() {
1760                        return filteredByTopic.includes($(this).attr('class'));
1761                    }).length;
1762                } else if (filterType === 'topicFilter') {
1763                    count = $("." + filter).filter(function () {
1764                        return filteredByMedia.includes($(this).attr('class'));
1765                    }).length;
1766                }
1767                articleFilterCounts[filter] = count;
1768                grayZeroCountFilterItem($(this), count);
1769                $(this).parent().find(".cmp-teaserFilter__articleCount").html( "(" + count + ")" );
1770
1771            } else if (topicFilterChecked && !mediaFilterChecked) {
1772                if ( $(this).is(':checked') || filterType === 'topicFilter') {
1773                    count = $("."+filter).length;
1774                    articleFilterCounts[filter] = count;
1775                    grayZeroCountFilterItem($(this), count);
1776                    $(this).parent().find(".cmp-teaserFilter__articleCount").html( "(" + count + ")" );
1777
1778                } else if (filterType === 'mediaFilter') {
1779                    count = $("."+filter).filter(function() {
1780                        return filteredByTopic.includes($(this).attr('class'));
1781                    }).length;
1782                    articleFilterCounts[filter] = count;
1783                    grayZeroCountFilterItem($(this), count);
1784                    $(this).parent().find(".cmp-teaserFilter__articleCount").html( "(" + count + ")" );
1785                }
1786
1787            } else if (mediaFilterChecked && !topicFilterChecked) {
1788                if ( $(this).is(':checked') || filterType === 'mediaFilter') {
1789                    count = $("." + filter).length;
1790                    articleFilterCounts[filter] = count;
1791                    grayZeroCountFilterItem($(this), count);
1792                    $(this).parent().find(".cmp-teaserFilter__articleCount").html("(" + count + ")");
1793
1794                } else if (filterType === 'topicFilter') {
1795                    count = $("."+filter).filter(function() {
1796                        return filteredByMedia.includes($(this).attr('class'));
1797                    }).length;
1798                    articleFilterCounts[filter] = count;
1799                    grayZeroCountFilterItem($(this), count);
1800                    $(this).parent().find(".cmp-teaserFilter__articleCount").html( "(" + count + ")" );
1801                }
1802            }
1803        });
1804    }
1805
1806    function grayZeroCountFilterItem (filter, count) {
1807        if (count === 0) {
1808            filter.parent().addClass("zero-article-filter");
1809            filter.prop('disabled', !filter.prop('checked'));
1810        } else {
1811            filter.parent().removeClass("zero-article-filter");
1812            filter.prop('disabled', false);
1813        }
1814    }
1815
1816    function displayNoArticlesMessage() {
1817        if (corpArticleFltrElements.length === 0) {
1818            $(".cmp-teaserFilter__noResults").show();
1819        } else {
1820            $(".cmp-teaserFilter__noResults").hide();
1821        }
1822    }
1823
1824    function checkForLoadMore() {
1825        $("#filteredArticles").html(corpArticleFltrElements.length);
1826        $.each(corpArticleFltrElements, function(index, value) {
1827            if ( index < loadMoreIncrement ) {
1828                $('.'+value.replace(/ /g, '.')).show();
1829            } else {
1830                $('.'+value.replace(/ /g, '.')).hide();
1831            }
1832        });
1833
1834        if ( corpArticleFltrElements.length <= loadMoreIncrement ) {
1835            $(".cmp-filterList__buttonCtaContainer").hide();
1836        } else {
1837            $(".cmp-filterList__buttonCtaContainer").show();
1838        }
1839        $(".cmp-filterList__buttonCtaContainer").data("visibleItems" , 10);
1840
1841    }
1842
1843    $(".cmp-filterList__buttonCtaContainer").click(function(){
1844        loadMoreArticles();
1845    });
1846
1847    function loadMoreArticles(){
1848        let visibleItems = $(".cmp-filterList__buttonCtaContainer").data("visibleItems");
1849        let noItemsToBeShown = (visibleItems + loadMoreIncrement) ;
1850        $.each(corpArticleFltrElements, function(index, value) {
1851            if ( index < noItemsToBeShown ) {
1852                $('.'+value.replace(/ /g, '.')).show();
1853            } else {
1854                $('.'+value.replace(/ /g, '.')).hide();
1855            }
1856        });
1857
1858        $(".cmp-filterList__buttonCtaContainer").data("visibleItems" , noItemsToBeShown);
1859        if ( corpArticleFltrElements.length <= noItemsToBeShown ) {
1860            $(".cmp-filterList__buttonCtaContainer").hide();
1861        }
1862
1863    }
1864
1865
1866    function sortDescendingWithClassSelector(firstEle, secondEle) {
1867        var date1  = new Date($('.'+firstEle.replace(/ /g, '.')).find(".article-publish-date").text());
1868        var date2  = new Date($('.'+secondEle.replace(/ /g, '.')).find(".article-publish-date").text());
1869
1870        return (isNaN(date1) || (isNaN(date1) && isNaN(date2)) || (!isNaN(date2) && date1 < date2)) ? 1 : -1;
1871    };
1872
1873
1874    function sortAscendingWithClassSelector(firstEle, secondEle) {
1875        var date1  = new Date($('.'+firstEle.replace(/ /g, '.')).find(".article-publish-date").text());
1876        var date2  = new Date($('.'+secondEle.replace(/ /g, '.')).find(".article-publish-date").text());
1877
1878        return (isNaN(date1) || (isNaN(date1) && isNaN(date2)) || (!isNaN(date2) && date1 < date2)) ? -1 : 1;
1879    }
1880
1881    function sortDescending(firstEle, secondEle) {
1882        var date1  = new Date($(firstEle).find(".article-publish-date").text());
1883        var date2  = new Date($(secondEle).find(".article-publish-date").text());
1884
1885        return (isNaN(date1) || (isNaN(date1) && isNaN(date2)) || (!isNaN(date2) && date1 < date2)) ? 1 : -1;
1886    };
1887
1888
1889    function sortAscending(firstEle, secondEle) {
1890        var date1  = new Date($(firstEle).find(".article-publish-date").text());
1891        var date2  = new Date($(secondEle).find(".article-publish-date").text());
1892
1893        return (isNaN(date1) || (isNaN(date1) && isNaN(date2)) || (!isNaN(date2) && date1 < date2)) ? -1 : 1;
1894    }
1895
1896    function performSort(value) {
1897        if (value === "Newest") {
1898            corpArticleFltrElements.sort(sortDescendingWithClassSelector);
1899            $('#corpfilterarticles .corparticle-filter-list').sort(sortDescending).appendTo('#corpfilterarticles');
1900        } else {
1901            corpArticleFltrElements.sort(sortAscendingWithClassSelector);
1902            $('#corpfilterarticles .corparticle-filter-list').sort(sortAscending).appendTo('#corpfilterarticles');
1903        }
1904        checkForLoadMore();
1905    }
1906
1907    // Sort dropdown: handle selection
1908    $("#teaserFilter-sort-select").on('change', function () {
1909        var value = $(this).val();
1910        performSort(value);
1911        updateURLWithFilters();
1912    });
1913
1914    $(window).bind('resize load', function() {
1915        if ($(this).width() > 1022) {
1916            $('.filter-accordion').removeClass('collapsed');
1917            $('.collapse').addClass('show');
1918        } else {
1919            $('.filter-accordion').addClass('collapsed');
1920            $('.collapse').removeClass('show');
1921        }
1922
1923    });
1924
1925    // Mobile filter modal
1926    var $component = $('.cmp-teaserFilter');
1927    var $mobileModal = $component.find('.cmp-teaserFilter__mobileModal');
1928    var $modalBody = $mobileModal.find('.cmp-teaserFilter__mobileModal-body');
1929    var $filterColumn = $component.find('.cmp-teaserFilter__filterColumn');
1930    var $dateSort = $component.find('.cmp-teaserFilter__dateSort');
1931
1932    var $triggerBtn = $component.find('.cmp-teaserFilter__mobileFilterBtn');
1933    var $mainContent = $component.find('.cmp-teaserFilter__container');
1934
1935    //Direct Accessibility Focus to Pop Up Teaser Filter Modal (when on mobile/small screen)
1936    function trapFocus(e) {
1937        var focusableEls = $mobileModal.find('button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])').filter(':visible');
1938        var firstEl = focusableEls.first();
1939        var lastEl = focusableEls.last();
1940
1941        if (e.key === 'Tab') {
1942            if (!$mobileModal[0].contains(document.activeElement)) {
1943                e.preventDefault();
1944                firstEl.focus();
1945            } else if (e.shiftKey) {
1946                if (document.activeElement === firstEl[0]) {
1947                    e.preventDefault();
1948                    lastEl.focus();
1949                }
1950            } else {
1951                if (document.activeElement === lastEl[0]) {
1952                    e.preventDefault();
1953                    firstEl.focus();
1954                }
1955            }
1956        }
1957
1958        if (e.key === 'Escape') {
1959            $mobileModal.find('.cmp-teaserFilter__mobileModal-close').trigger('click');
1960        }
1961    }
1962
1963    $triggerBtn.on('click', function() {
1964        $modalBody.append($filterColumn.find('.cmp-teaserFilter__filterAccordion'));
1965        $modalBody.append($dateSort);
1966        $mobileModal.removeAttr('hidden');
1967        $mainContent.attr('aria-hidden', 'true');
1968        $('body').css('overflow', 'hidden');
1969        $mobileModal.find('.cmp-teaserFilter__mobileModal-close').focus();
1970        $(document).on('keydown.mobileModal', trapFocus);
1971    });
1972
1973    $mobileModal.on('click', '.cmp-teaserFilter__mobileModal-close', function() {
1974        $filterColumn.append($modalBody.find('.cmp-teaserFilter__filterAccordion'));
1975        $component.find('.cmp-teaserFilter__filterDateContainer').append($modalBody.find('.cmp-teaserFilter__dateSort'));
1976        $mobileModal.attr('hidden', '');
1977        $mainContent.removeAttr('aria-hidden');
1978        $('body').css('overflow', '');
1979        $(document).off('keydown.mobileModal');
1980        $triggerBtn.focus();
1981    });
1982});
1983
1984jQuery(document).ready(function($){
1985    var k = 1;
1986    $('.table').each(function(){
1987        $(this).attr('id','tableIndex'+k);
1988        var tabledata = $(this).attr('id');
1989		var i = 1;
1990        $('#'+tabledata+' '+' .table-data tr:first-child td').each(function(){
1991			$(this).addClass('row-count'+i);
1992            i++;
1993        });
1994        k++;
1995        setTimeout(addScroll, 1000);
1996        function addScroll(){
1997            if( $('#'+tabledata+ ' .left-column-padding .row-count5').length === 1  ){
1998                $('#'+tabledata+' '+'.table-container').addClass('activate-scroll');
1999                $('#'+tabledata+' '+' .scroll-btns').addClass('activate-buttons');
2000            }
2001            if( $('#'+tabledata+ ' .no-left-padding .row-count7').length === 1 ){
2002                $('#'+tabledata+' '+'.table-container').addClass('activate-scroll');
2003                $('#'+tabledata+' '+' .scroll-btns').addClass('activate-buttons');
2004            }
2005        }
2006
2007
2008		var colTotalCount = $('#'+tabledata+ ' table th').length;
2009        var colCount = colTotalCount - 1;
2010        var currentColumn = 1;
2011
2012
2013		const columnWidth = 100/colTotalCount;
2014
2015        if( $('#'+tabledata+ ' table').attr('data-firstcolumnwidth') == 'on'){
2016			$('#'+tabledata+ ' table tr td:first-child').attr('width', columnWidth+10+'%');
2017            const restColumnsWidth = 90/colTotalCount;
2018            $('#'+tabledata+ ' table tr td:not(:first-child)').attr('width', restColumnsWidth+'%');
2019            let oldWidthValue = $('#'+tabledata+ ' table tr td:first-child').width();
2020            let updatedWidthValue = oldWidthValue + 50;
2021            $('#'+tabledata+ ' table tr td:first-child').css('cssText', 'min-width:'+ updatedWidthValue+'px;');
2022            $('#'+tabledata+ ' .left-scroll-shadow').css('cssText', 'left:'+ updatedWidthValue+'px;');
2023
2024        }else{
2025			$('#'+tabledata+ ' table tr td').attr('width', columnWidth+'%');
2026        }
2027
2028
2029
2030        $('#'+tabledata+ ' table tr td:first-child').each(function(){
2031            let rowSpanCount = $(this).attr('rowspan');
2032            if($(this).attr('rowspan') > 1){                
2033				$(this).addClass('extended-row');
2034                $('#'+tabledata+ ' table tr').slice('-'+rowSpanCount).remove();
2035            }
2036        });
2037
2038        if(colTotalCount > 6){
2039			 $('#'+tabledata+' '+' .scroll-btns').addClass('activate-buttons');
2040             $('#'+tabledata+' '+' .table-container').addClass('activate-scroll');
2041        }
2042        if (window.matchMedia('(max-width: 767px)').matches) {
2043            $('#'+tabledata+' '+'.table-container').addClass('activate-scroll');
2044        }
2045        else  if (window.matchMedia('(max-width: 1023px)').matches && colTotalCount > 4 ) {
2046            $('#'+tabledata+' '+' .scroll-btns').addClass('activate-buttons');
2047            $('#'+tabledata+' '+'.table-container').addClass('activate-scroll');
2048
2049        }
2050
2051        const tableContainer = $('#'+tabledata+' '+' .table-container');
2052		var containerWidth = tableContainer.width();
2053  		var lastVal;  
2054
2055
2056         tableContainer.scroll(function () {
2057            var scrollPercentage=100*this.scrollLeft/this.scrollWidth/(1-this.clientWidth/this.scrollWidth);
2058    		let scrollValue = scrollPercentage.toFixed(2);
2059
2060             if(scrollValue < 1){
2061				$('#'+tabledata+ ' .left-button').addClass('disable-btn');
2062                $('#'+tabledata+ ' .right-button').removeClass('disable-btn');
2063             }else if(scrollValue > 1){
2064				$('#'+tabledata+ ' .left-button').removeClass('disable-btn');
2065             }
2066             if(scrollValue > 99){
2067				$('#'+tabledata+ ' .right-button').addClass('disable-btn');
2068             }else if(scrollValue < 99){
2069				$('#'+tabledata+ ' .right-button').removeClass('disable-btn');
2070             }
2071
2072            var scrollLeftValue = tableContainer[0].scrollLeft;
2073			var scrollFullValue = tableContainer[0].scrollWidth;
2074
2075             var scrollCount = scrollLeftValue;
2076             var columnSize = scrollCount/130;
2077             if(scrollCount >= 20){
2078				$('#'+tabledata+ ' .table-column').addClass('activate-left-shadow');
2079             }else{
2080				$('#'+tabledata+ ' .table-column').removeClass('activate-left-shadow');
2081             }
2082
2083
2084             var roundingValue =  Math.ceil(columnSize);
2085             var tabVersionValue = roundingValue + 2;
2086			 if($('#'+tabledata+ ' .table-column').hasClass('left-column-padding')){
2087                var desktopversionValue = roundingValue + 3;
2088             } else{
2089                var desktopversionValue = roundingValue + 4;
2090             }
2091			 if (window.matchMedia('(max-width: 1023px)').matches && roundingValue > 4) {
2092				$('#'+tabledata+' '+' .scroll-btns').addClass('activate-buttons');
2093             } 
2094     });
2095
2096
2097        $('#'+tabledata+ ' .right-button').click(function(event) {
2098          	event.preventDefault();
2099         	$('#'+tabledata+ ' .table-container').animate({
2100            			scrollLeft: "+=" + 130 + "px"
2101             }, "slow");
2102        });
2103
2104        $('#'+tabledata+ ' .left-button').click(function(event) {
2105         	event.preventDefault();
2106         	$('#'+tabledata+ ' .table-container').animate({
2107            			scrollLeft: "-=" + 130 + "px"
2108            }, "slow");
2109        });
2110
2111    });
2112
2113
2114    $('table .cmp-text').each(function(){
2115        if($(this).attr('data-backgroun-dcolor')){
2116        let bgcolor = $(this).css('background-color');
2117        $(this).closest('th').css('background', bgcolor);
2118        $(this).closest('td').css('cssText', 'background-color:'+ bgcolor+'!important;');
2119        }
2120    });
2121
2122    $('.table-data').each(function(){
2123 		$(this).attr('id','tableId'+k);
2124        var tableId = $(this).attr('id');
2125        var tableBorderWidth = $(this).attr('data-linewidth');
2126        var tableBorderColor = $(this).attr('data-linecolor');
2127
2128
2129        if($(this).attr('data-frametype') == 'lines'){
2130            $(this).addClass('table-frame');
2131            $(this).find('td').css('border', 'solid');
2132            $(this).find('td').css('border-width', tableBorderWidth);
2133            $(this).find('th').css('border-color', tableBorderColor);
2134            $(this).find('td').css('border-color', tableBorderColor);
2135            $(this).find('td').css('padding', columnGap);
2136			$(this).find('th').css('cssText', 'border: solid '+ tableBorderColor +' '+ tableBorderWidth+'px');
2137        }
2138
2139        if($(this).attr('data-verticallines') == 'off'){
2140			$(this).addClass('no-vertical-borders');
2141
2142        }
2143        if($(this).attr('data-horizontallines') == 'off'){
2144			$(this).addClass('no-horizontal-borders');
2145
2146
2147        }
2148    });
2149
2150
2151});
2152(function (window) {
2153    window.subscriptionAnalytics = {
2154
2155        SUBSCRIBER_STATUS_TO_SUBPAGE_ID: {
2156            confirmed:              "subscription-dashboard-preferences",
2157            subscriptionActivated:  "subscription-dashboard-subscribed",
2158            newSubscriptionManaged: "subscription-dashboard-subscribed-preferences-saved",
2159            alreadySubscribed:      "subscription-dashboard-already-subscribed-preferences",
2160            oldSubscriptionManaged: "subscription-dashboard-already-subscribed-preferences-saved"
2161        },
2162
2163        SUBSCRIBER_STATUS_TO_CTA_LOCATION: {
2164            verifyEmailSent:        "verify email",
2165            confirmed:              "preferences",
2166            subscriptionActivated:  "subscribed",
2167            newSubscriptionManaged: "subscribed preferences saved",
2168            alreadySubscribed:      "already subscribed preferences",
2169            oldSubscriptionManaged: "already subscribed preferences saved"
2170        },
2171
2172        SUBSCRIBER_STATUS_TO_PAGE_LOAD_DATA: {
2173            verifyEmailSent: {
2174                pageName: 'us:en:corp:subscription-dashboard-verify-email',
2175                pageTitle: 'verify email'
2176            },
2177            subscriptionActivated: {
2178                pageName: 'us:en:corp:subscription-dashboard-subscribed',
2179                pageTitle: 'subscribed'
2180            },
2181            newSubscriptionManaged: {
2182                pageName: 'us:en:corp:subscription-dashboard-subscribed-preferences-saved',
2183                pageTitle: 'subscribed preferences saved'
2184            },
2185            alreadySubscribed: {
2186                pageName: 'us:en:corp:subscription-dashboard-already-subscribed-preferences',
2187                pageTitle: 'already subscribed preferences'
2188            },
2189            oldSubscriptionManaged: {
2190                pageName: 'us:en:corp:subscription-dashboard-already-subscribed-preferences-saved',
2191                pageTitle: 'already subscribed preferences saved'
2192            }
2193        },
2194
2195        getSubPageIDBySubscriberStatus() {
2196            const subscriberStatus = this.getSubscriberStatus();
2197            return this.SUBSCRIBER_STATUS_TO_SUBPAGE_ID[subscriberStatus] || "";
2198        },
2199
2200        getCallToActionLocationBySubscriberStatus() {
2201            const subscriberStatus = this.getSubscriberStatus();
2202            let location = this.SUBSCRIBER_STATUS_TO_CTA_LOCATION[subscriberStatus] || "";
2203            location += " | subscription dashboard";
2204            return location;
2205        },
2206
2207        getSubscriberStatus() {
2208            try {
2209                if (typeof window !== "undefined" && window.localStorage) {
2210                    return window.localStorage.getItem("subscriberStatus") || "";
2211                }
2212            } catch (e) {
2213                logClientError("WARN", "localStorage access failed: " + (e && e.message ? e.message : String(e)), "subscriptionAnalytics.js");
2214            }
2215            return "";
2216        },
2217
2218        getPageLoadDataBySubscriberStatus() {
2219            const subscriberStatus = this.getSubscriberStatus();
2220            return this.SUBSCRIBER_STATUS_TO_PAGE_LOAD_DATA[subscriberStatus] || "";
2221        },
2222
2223        pushToDataLayer(payload) {
2224            window.dataLayer = window.dataLayer || [];
2225            window.dataLayer.push(payload);
2226        },
2227
2228        pushSubPageViewedEventOnSignUpTabClick() {
2229            this.pushToDataLayer({
2230                event: "subPageViewed", //e166
2231                subPage: {
2232                    "subPageID": "subscription-dashboard:sign up", //v105
2233                    "spClicked": "true"
2234                }
2235            });
2236        },
2237
2238        pushSubPageViewedEventOnAlreadySubscribedTabClick() {
2239            this.pushToDataLayer({
2240                event: "subPageViewed", //e166
2241                subPage: {
2242                    "subPageID": "subscription-dashboard:already subscribed", //v105
2243                    "spClicked": "true"
2244                }
2245            });
2246        },
2247
2248        pushSubPageViewedEventOnSubscriptionPreferencesTabClick() {
2249            let subPageID = this.getSubPageIDBySubscriberStatus();
2250            subPageID += ":subscription preferences";
2251            this.pushToDataLayer({
2252                event: "subPageViewed", //e166
2253                subPage: {
2254                    "subPageID": subPageID, //v105
2255                    "spClicked": "true"
2256                }
2257            });
2258        },
2259
2260        pushSubPageViewedEventOnPersonalInformationTabClick() {
2261            let subPageID = this.getSubPageIDBySubscriberStatus();
2262            subPageID += ":personal information";
2263            this.pushToDataLayer({
2264                event: "subPageViewed", //e166
2265                subPage: {
2266                    "subPageID": subPageID, //v105
2267                    "spClicked": "true"
2268                }
2269            });
2270        },
2271
2272        pushCallToActionEventOnViewPrivacyPolicy() {
2273            this.pushToDataLayer({
2274                event: "callToAction", //e111
2275                callToAction: {
2276                    "location": "subscription-dashboard", //v103
2277                    "ctaName": "privacy policy", //v104
2278                    "ctaType": "text link" //v111
2279                }
2280            });
2281        },
2282
2283        pushCallToActionEventOnResendEmail() {
2284            let location = this.getCallToActionLocationBySubscriberStatus();
2285            this.pushToDataLayer({
2286                event: "callToAction", //e111
2287                callToAction: {
2288                    "location": location, //v103
2289                    "ctaName": "click here to resend", //v104
2290                    "ctaType": "text link" //v111
2291                }
2292            });
2293        },
2294
2295        pushCallToActionEventOnSelectAllCheckboxClick() {
2296            let location = this.getCallToActionLocationBySubscriberStatus();
2297            this.pushToDataLayer({
2298                event: "callToAction", //e111
2299                callToAction: {
2300                    "location": location, //v103
2301                    "ctaName": "select all", //v104
2302                    "ctaType": "checkbox" //v111
2303                }
2304            });
2305        },
2306
2307        pushCallToActionEventOnUnsubscribeFromAllLinkClick() {
2308            let location = this.getCallToActionLocationBySubscriberStatus();
2309            this.pushToDataLayer({
2310                event: "callToAction", //e111
2311                callToAction: {
2312                    "location": location, //v103
2313                    "ctaName": "unsubscribe from all", //v104
2314                    "ctaType": "text link" //v111
2315                }
2316            });
2317        },
2318
2319        pushCallToActionEventOnUnsubscribeFromAllDismissButtonClick() {
2320            let location = this.getCallToActionLocationBySubscriberStatus();
2321            this.pushToDataLayer({
2322                event: "callToAction", //e111
2323                callToAction: {
2324                    "location": location, //v103
2325                    "ctaName": "confirm leave without saving", //v104
2326                    "ctaType": "primary button" //v111
2327                }
2328            });
2329        },
2330
2331        pushCallToActionEventOnAlreadySubscribedNextButtonClick() {
2332            this.pushToDataLayer({
2333                event: "callToAction", //e111
2334                callToAction: {
2335                    "location": "subscription-dashboard", //v103
2336                    "ctaName": "next", //v104
2337                    "ctaType": "primary button" //v111
2338                }
2339            });
2340        },
2341
2342        pushCallToActionEventOnPersonalInformationEdit() {
2343            this.pushToDataLayer({
2344                event: "callToAction", //e111
2345                callToAction: {
2346                    "location": "Dashboard - Personal Details", //v103
2347                    "ctaName": "edit", //v104
2348                    "ctaType": "text link" //v111
2349                }
2350            });
2351        },
2352
2353        pushCallToActionEventOnPersonalInformationSave() {
2354            this.pushToDataLayer({
2355                event: "callToAction", //e111
2356                callToAction: {
2357                    "location": "Dashboard - Personal Details", //v103
2358                    "ctaName": "save", //v104
2359                    "ctaType": "primary button" //v111
2360                }
2361            });
2362        },
2363
2364        pushCallToActionEventOnPersonalInformationCancel() {
2365            this.pushToDataLayer({
2366                event: "callToAction", //e111
2367                callToAction: {
2368                    "location": "Dashboard - Personal Details", //v103
2369                    "ctaName": "cancel", //v104
2370                    "ctaType": "text link" //v111
2371                }
2372            });
2373        },
2374
2375        pushSubscriptionInitiatedEvent() {
2376            this.pushToDataLayer({
2377                event: "subscriptionInitiated", //e94
2378                subscription: {
2379                    "subscriptionName": ['DASHBOARD'], //list1
2380                }
2381            });
2382        },
2383
2384        pushPageLoadedEventBySubscriberStatus() {
2385            let pageData = this.getPageLoadDataBySubscriberStatus();
2386            let pageName = pageData.pageName;
2387            let pageTitle = pageData.pageTitle + ' | subscription dashboard';
2388            this.pushToDataLayer({
2389                event: "pageLoaded",
2390                page: {
2391                    "pageTitle": pageTitle, //c30
2392                    "pageName":  pageName, //c18, v18
2393                    "siteSection1": "global", //c38
2394                    "siteSection2": "subscription-dashboard", //c39
2395                    "siteIdentifier": "corp",
2396                    "is404": "false",
2397                    "DLVersion": "1.0.0", //c42, v42
2398                }
2399            });
2400        },
2401
2402        pushSubscriptionActivatedEvent(subscriptionNames) {
2403            this.pushToDataLayer({
2404                event: "subscriptionActivated", //e95
2405                subscription: {
2406                    subscriptionName: subscriptionNames, //list1
2407                    subscriberID: "", //v61
2408                    subscriptionType: "dashboard" //v242
2409                }
2410            });
2411        },
2412
2413        pushSubscriptionManagedEvent(subscriptionNames) {
2414            this.pushToDataLayer({
2415                event: "subscriptionManaged", //e96
2416                subscription: {
2417                    subscriptionName: subscriptionNames, //list1
2418                    subscriberID: "", //v61
2419                    subscriptionType: "dashboard" //v242
2420                }
2421            });
2422        },
2423
2424        pushSubscriptionUnsubscribedEvent(subscriptionNames) {
2425            this.pushToDataLayer({
2426                event: "subscriptionUnsubscribed", //e97
2427                subscription: {
2428                    subscriptionName: subscriptionNames, //list1
2429                    subscriberID: "" //v61
2430                }
2431            });
2432        },
2433
2434        pushPageLoadCompletedEvent() {
2435            this.pushToDataLayer({
2436                event: "pageLoadCompleted"
2437            });
2438        }
2439    };
2440}) (window);
2441
2442/* The Subscription Dashboard is a single-page application (SPA). Since user flow does not change the URL,
2443   we keep track of the current state in the "subscriberStatus" value in localStorage for analytics tracking purposes.
2444
2445   "subscriberStatus" values:
2446      verifyEmailSent         : New subscriber submitted email and industry; verification email sent
2447      confirmed               : New subscriber clicked on verify link in email; subscriptions not yet activated
2448      subscriptionActivated   : New subscriber activated subscriptions (clicked "Subscribe")
2449      newSubscriptionManaged  : New subscriber managed preferences (clicked "Save preferences")
2450      alreadySubscribed       : Existing subscriber logged in to manage preferences
2451      oldSubscriptionManaged  : Existing subscriber managed preferences
2452      unsubscribedAll         : Existing subscriber unsubscribed from all
2453*/
2454
2455(() => {
2456
2457    $(document).ready(function () {
2458
2459        const selectAllCheckbox = $('.checking-select-all-or-none');
2460        const checkboxes = $('.checking-select');
2461        const unsubscribe = $('#label-unsubscribe');
2462        const tabSignUp = $('#loginSignTab');
2463        const tabAlreadySubscribed = $('#loginAlreadyTab');
2464        const navPrefsMain = $('#tabPreferences');
2465        const navPersMain = $('#tabPersonalInfo');
2466        const personalContainer = $('.personal-info-container');
2467        const returnUserLoginContainer = $('#subDashReturnLoginContainer')
2468        const emailPrefsLogin = $('#emailPrefsLogin');
2469        const emailPrefsMain = $('#emailPrefsMain');
2470        const dashboardConfirmationContainer = $('.dashboard-confirmation-container');
2471        const plainPersonalInfo = $('.dashboard-plain-personal-info');
2472        const dashboardEditButton = $('#dashboardEditButton');
2473        const dashboardCancelButton = $('#dashboard-cancel-button');
2474        const inputPersonalInfo = $('#input-personal-info');
2475        const saveButton = $('.dashboard-submit-button');
2476        const unsubscribeAllDismissButton = $('#unsubscribe-all-dismiss-button');
2477        const unsubscribeAllConfirmationButton = $('#unsubscribe-all-confirmation-button');
2478        const personalSaveButton = $('#dashboard-save-button');
2479        const dashboardLoginPage = $('#dashboardLoginPage');
2480        const dashboardConfirmSubscriptionPage = $('#dashboardConfirmSubscriptionPage');
2481        const dashboardMainPage = $('#dashboardMainPage');
2482        const alreadySubButton = $('#alreadySub-done-btn');
2483        const doneButton = $('#done-btn');
2484        const dashboardPrivacyPolicy = $('.privacy-policy');
2485        const updateSuccessMessage = $('#subDashSuccessConfirmationMessage');
2486        const dashboardEditMyPreferences = $("#dashboardEditMyPreferences");
2487        const hideSubscriptionDashboardModal = $('.hide-subscriptionDashboard-modal');
2488        const confirmBannerJq = $('#confirmBanner');
2489        const confirmBannerClsBtn = $('#confirmBannerCloseBtn');
2490
2491        const emailResendSuccessTitle = "Your email was resent to ";
2492        const emailResendSuccessDesc = "Be sure to check your spam folder and the email address provided.";
2493        const emailResendErrorTitle = "An error occurred while sending email to ";
2494        const emailResendErrorDesc = "Please try again.";
2495        const emailResendAlreadySubscribedTitle = "Your email have already been verified";
2496        const emailResendAlreadySubscribedDesc = "You can manage your preferences in the subscriber dashboard at any time.";
2497        const oneLastStepTitle = "One last step: Tell us which insights and perspectives you'd like to receive.";
2498        const oneLastStepDesc = "Personalize your Insights email by selecting the topics, authors, and news you want to follow.";
2499        const thankYouMessageTitle = "Thank you! You are now subscribed.";
2500        const thankYouMessageDesc = "You can manage your preferences in the subscriber dashboard at any time.";
2501        const successSVGUrl = 'url("/etc.clientlibs/corporatesite/clientlibs/clientlib-site/resources/icons/button-icons/Success.svg")';
2502        const alertSVGUrl = 'url("/etc.clientlibs/corporatesite/clientlibs/clientlib-site/resources/icons/button-icons/Alert.svg")';
2503
2504        let isNewUser;
2505        let returningUserSubHistory={};
2506        let ignoreDirtyForm = false;
2507
2508        // clear subscriber state on initial load of the Subscription Dashboard
2509        window.utils.removeSubscriberStatus();
2510
2511        // new email signup validation
2512        jQuery('#subDashNewUserEmail').on('focusout', () => {
2513            const isValidEmailCheckForConfMail = validateEmail('subDashNewUserEmail', 'subDashNewUserEmailError');
2514            if (isValidEmailCheckForConfMail) {
2515                $('#subDashNewUserConfirmMailMsg').show()
2516            } else {
2517                $('#subDashNewUserConfirmMailMsg').hide()
2518            }
2519        });
2520
2521        // new user industry validation
2522        jQuery('#subDashNewUserIndustry').on('change', () => {
2523            validateIndustry('subDashNewUserIndustry', 'subDashNewUserIndustryError');
2524            jQuery('#subDashNewUserIndustry').blur();
2525            jQuery('#done-btn').focus();
2526        });
2527
2528        // returning user email validation
2529        jQuery('#subDashReturnUserEmail').on('focusout', () => {
2530            const noUserErrorSpan = $('#subDashReturnUserNoUser')[0];
2531            hideErrorMessage(noUserErrorSpan)
2532            validateEmail('subDashReturnUserEmail', 'subDashReturnUserEmailError');
2533            $('#alreadySub-done-btn').focus();
2534        });
2535
2536        // update personal email validation
2537        jQuery('#subDashUpdateEmailInput').on('focusout', () => {
2538            validateEmail('subDashUpdateEmailInput', 'subDashUpdateEmailInputError');
2539        });
2540
2541        // resend verification email link event listener
2542        $("#confEmailResendLink").on('click', () => {
2543
2544            $.ajax({
2545                url: "/api/corporatesite/subscription",
2546                type: "POST",
2547                contentType: 'application/json',
2548                dataType: "JSON",
2549                data: JSON.stringify({
2550                    currentEmail: $('#subDashNewUserEmail').val(),
2551                    industryId: $("#subDashNewUserIndustry").val(),
2552                    active: true
2553                }),
2554                success: function (response) {
2555                    if (!isExistingSubscriber(response)) {
2556                        displayConfirmBannerForResendEmailSuccess();
2557                        subscriptionAnalytics.pushCallToActionEventOnResendEmail();
2558                    } else {
2559                        displayConfirmBannerForResendEmailAlreadySubscribed();
2560                    }
2561                },
2562                error: function () {
2563                    displayConfirmBannerForResendEmailError();
2564                    logClientError("ERROR", "Resend verification email failed", "subscriptionDashboard.js");
2565                },
2566            });
2567        });
2568
2569        // handle unsaved changes
2570        if ($('.cmp-subscriptionDashboard').length !== 0) {
2571            $('#subDashNewUserEmail,#subDashReturnUserEmail,#subDashNewUserIndustry').on('input', function () {
2572                addEventListener('beforeunload', handleLeaveBeforeSaving, {capture: true});
2573            });
2574        }
2575
2576        function checkboxValidation() {
2577            return getUnselectedCheckboxes() !== checkboxes.length;
2578        }
2579
2580        function checkSubscriptionDiff(oldHistory, currentHistory) {
2581            const subscribedTopicIds = $(currentHistory.subscribedTopicIds).not(oldHistory.subscribedTopicIds).get();
2582            const unsubscribedTopicIds = $(oldHistory.subscribedTopicIds).not(currentHistory.subscribedTopicIds).get();
2583            const subscribedAuthorIds = $(currentHistory.subscribedAuthorIds).not(oldHistory.subscribedAuthorIds).get();
2584            const unsubscribedAuthorIds = $(oldHistory.subscribedAuthorIds).not(currentHistory.subscribedAuthorIds).get();
2585
2586            return {
2587                subscribedTopicIds,
2588                unsubscribedTopicIds,
2589                subscribedAuthorIds,
2590                unsubscribedAuthorIds,
2591            }
2592        }
2593
2594        function showConfirmSubscriptionMessage() {
2595            dashboardLoginPage.removeClass('dashboardContainer vanguard-dashboard-login d-flex justify-content-center');
2596            dashboardLoginPage.addClass('d-none');
2597
2598            dashboardConfirmSubscriptionPage.removeClass('d-none');
2599            $('#emailConfValue').html($('#subDashNewUserEmail').val());
2600            dashboardConfirmSubscriptionPage.addClass('dashboardContainer vanguard-dashboard-login');
2601        }
2602
2603        function transitionToForm() {
2604            dashboardLoginPage.removeClass('dashboardContainer vanguard-dashboard-login d-flex justify-content-center');
2605            dashboardLoginPage.addClass('d-none');
2606
2607            dashboardMainPage.removeClass('d-none');
2608            dashboardMainPage.addClass('dashboardContainer d-flex justify-content-center');
2609        }
2610
2611        function displaySuccessMessage(message) {
2612            updateSuccessMessage.removeClass('d-none');
2613            updateSuccessMessage.addClass('dashboard-confirmation-container d-flex');
2614            updateSuccessMessage.children().first().text(message);
2615            $(window).scrollTop(0);
2616        }
2617
2618        function hideSuccessMessage() {
2619            updateSuccessMessage.removeClass('dashboard-confirmation-container d-flex');
2620            updateSuccessMessage.addClass('d-none');
2621        }
2622
2623        function gatherCheckboxData() {
2624            let authorCheckbox = [];
2625            let topicCheckbox = [];
2626
2627            $("input:checkbox[name=topicCheckbox]:checked").each(function () {
2628                topicCheckbox.push($(this).attr('id'));
2629            });
2630
2631            $("input:checkbox[name='authorCheckbox']:checked").each(function () {
2632                authorCheckbox.push($(this).attr('id'));
2633            });
2634
2635            return {
2636                authorCheckbox,
2637                topicCheckbox
2638            }
2639        }
2640
2641        function handleLeaveBeforeSaving(event) {
2642            if (ignoreDirtyForm) {
2643                return;
2644            }
2645
2646            event.preventDefault();
2647
2648            const {authorCheckbox, topicCheckbox} = gatherCheckboxData();
2649            const subscriptionChanges = checkSubscriptionDiff(returningUserSubHistory, {
2650                subscribedAuthorIds: authorCheckbox,
2651                subscribedTopicIds: topicCheckbox
2652            })
2653
2654            // iterate through diff to see if any changes are present
2655            const unsavedSubscriptions = Object.values(subscriptionChanges).map((topicSubs) => Boolean(topicSubs.length)).filter(val => val);
2656            let unsavedPersonalDetails = false;
2657
2658            if (!isNewUser) {
2659                const updateEmailInput = $('#subDashUpdateEmailInput').val();
2660                const updateIndustryInput = $('#subDashUpdateIndustrySelect').val();
2661
2662                const hasUnsavedEmail = updateEmailInput !== returningUserSubHistory.currentEmail;
2663                const hasUnsavedIndustry = updateIndustryInput !== returningUserSubHistory.industryId;
2664
2665                unsavedPersonalDetails = hasUnsavedEmail || hasUnsavedIndustry;
2666            }
2667
2668            if (unsavedSubscriptions.length || unsavedPersonalDetails) {
2669                // modern browsers no longer show custom messages, nor can we use a custom modal
2670                const browserMessage = 'There are unsaved changes. Are you sure you want to leave?'
2671                // some browsers require that event.returnValue be set to show dialogue
2672                event.returnValue = browserMessage;
2673                return browserMessage;
2674            }
2675            return;
2676        }
2677
2678        function populatePersonalData(responseData) {
2679            const currentIndustry = $(`option[value=${responseData.industryId}]`).first().text();
2680
2681            $('#subDashCurrentEmailPlaintext').text(responseData.currentEmail);
2682            $('#subDashCurrentIndustryPlaintext').text(currentIndustry);
2683            $('#subDashUpdateEmailInput').prop('value', responseData.currentEmail);
2684            $('#subDashUpdateIndustrySelect').prop('value', responseData.industryId);
2685        }
2686
2687        function populateExistingData(responseData) {
2688            returningUserSubHistory = responseData;
2689
2690            if (returningUserSubHistory.subscribedTopicIds) {
2691                $("input:checkbox[name=topicCheckbox]").each(function () {
2692                    if (returningUserSubHistory.subscribedTopicIds.find((id) => id === $(this).attr('id'))) {
2693                        $(this).prop('checked', true);
2694                    }
2695                });
2696            }
2697
2698            if (returningUserSubHistory.subscribedAuthorIds) {
2699                $("input:checkbox[name=authorCheckbox]").each(function () {
2700                    if (returningUserSubHistory.subscribedAuthorIds.find((id) => id === $(this).attr('id'))) {
2701                        $(this).prop('checked', true);
2702                    }
2703                });
2704            }
2705
2706            updateSelectAllCheckBox();
2707            populatePersonalData(responseData);
2708        }
2709
2710        function getUnselectedCheckboxes() {
2711            let notSelected = 0;
2712            for (let i = 0; i < checkboxes.length; i++) {
2713                const checkbox = checkboxes[i];
2714
2715                if (!checkbox.checked) {
2716                    notSelected++;
2717                }
2718            }
2719            return notSelected;
2720        }
2721
2722        // Info banner close action
2723        confirmBannerClsBtn.on('click', () => {
2724            confirmBannerJq.addClass('d-none')
2725            confirmBannerJq.removeClass('confirm-info-banner')
2726        });
2727        
2728        function displayConfirmBanner(title, description, messageType) {
2729            confirmBannerJq.removeClass('d-none')
2730            confirmBannerJq.addClass('confirm-info-banner')
2731            $('#ConfirmInfoBannerHeader').html(title)
2732            $('#ConfirmInfoBannerMessage').html(description)
2733            if (messageType === "Success") {
2734                 $(".confirm-info-banner__message-icon").css('content', successSVGUrl)
2735                 $(".confirm-info-banner").css({ 'background-color': '#E9F6E9', 'border-bottom': '1px solid #1D9F22' });
2736                 $("#dashboardConfirmSubscriptionPage").css('padding-top', '5%')
2737            } else {
2738                $(".confirm-info-banner__message-icon").css('content', alertSVGUrl)
2739                $(".confirm-info-banner").css({ 'background-color': '#FFF9E5', 'border-bottom': '1px solid #CC7A00' })
2740            }
2741        }
2742        
2743        function displayConfirmBannerForResendEmailSuccess() {
2744            let title = emailResendSuccessTitle + $('#subDashNewUserEmail').val();
2745            displayConfirmBanner(title, emailResendSuccessDesc, "Success")
2746        }
2747
2748        function displayConfirmBannerForResendEmailError() {
2749            let title = emailResendErrorTitle + $('#subDashNewUserEmail').val();
2750            displayConfirmBanner(title, emailResendErrorDesc, "Error")
2751        }
2752
2753        function displayConfirmBannerForResendEmailAlreadySubscribed() {
2754            displayConfirmBanner(emailResendAlreadySubscribedTitle, emailResendAlreadySubscribedDesc, "Success")
2755        }
2756
2757        function displayConfirmBannerOneLastStepMessage() {
2758            displayConfirmBanner(oneLastStepTitle, oneLastStepDesc, "Alert")
2759        }
2760
2761        function displayConfirmBannerThankYouMessage() {
2762            displayConfirmBanner(thankYouMessageTitle, thankYouMessageDesc, "Success");
2763            $(window).scrollTop(0);
2764        }
2765
2766        function displaySignUpTab() {
2767            returnUserLoginContainer.removeClass('d-block');
2768            returnUserLoginContainer.addClass('d-none');
2769            emailPrefsLogin.removeClass('d-none');
2770            emailPrefsLogin.addClass('d-block');
2771            tabSignUp.prop('class', 'dashboard-active-tab');
2772            tabAlreadySubscribed.prop('class', 'navigation-tab');
2773        }
2774
2775        function displayAlreadySubscribedTab() {
2776            emailPrefsLogin.removeClass('d-block');
2777            emailPrefsLogin.addClass('d-none');
2778            returnUserLoginContainer.removeClass('d-none');
2779            returnUserLoginContainer.addClass('d-block');
2780            tabSignUp.prop('class', 'navigation-tab');
2781            tabAlreadySubscribed.prop('class', 'dashboard-active-tab');
2782        }
2783
2784        function displaySubscriptionPreferences() {
2785            hideSuccessMessage();
2786            personalContainer.removeClass('d-block');
2787            personalContainer.addClass('d-none');
2788            emailPrefsMain.removeClass('d-none');
2789            emailPrefsMain.addClass('d-block');
2790            navPrefsMain.prop('class', 'dashboard-active-tab');
2791            navPersMain.prop('class', 'navigation-tab');
2792        }
2793
2794        function displayPersonalInformation() {
2795            hideSuccessMessage();
2796            emailPrefsMain.removeClass('d-block');
2797            emailPrefsMain.addClass('d-none');
2798            personalContainer.removeClass('d-none');
2799            personalContainer.addClass('d-block');
2800            navPrefsMain.prop('class', 'navigation-tab');
2801            navPersMain.prop('class', 'dashboard-active-tab');
2802        }
2803
2804        /********** Sign up - Next button **********/
2805
2806        doneButton.click(() => {
2807          $('.dashboard-submit-button').focus();
2808          const isValidEmail = validateEmail('subDashNewUserEmail', 'subDashNewUserEmailError');
2809          const isValidIndustry = validateIndustry('subDashNewUserIndustry', 'subDashNewUserIndustryError');
2810          if (!isValidEmail) {
2811            $('#subDashNewUserEmail').focus();
2812          } else if (!isValidIndustry) {
2813            $('#subDashNewUserIndustry').focus();
2814          }
2815          if (isValidEmail && isValidIndustry) {
2816            isNewUser = true;
2817            navPersMain.addClass('d-none');
2818
2819            $.ajax({
2820              url: "/api/corporatesite/subscription",
2821              type: "POST",
2822              contentType: 'application/json',
2823              dataType: "JSON",
2824              data: JSON.stringify({
2825                currentEmail: $('#subDashNewUserEmail').val(),
2826                industryId: $("#subDashNewUserIndustry").val(),
2827                active: true
2828              }),
2829              success: function (response) {
2830                if (isExistingSubscriber(response)) {
2831                  $('.modal-subscriptionDashboard').addClass('show-modal');
2832                  $(document.body).append('<div class="modal-backdrop show"></div>');
2833                  $(document.body).addClass('modal-open');
2834                  returningUserSubHistory = response;
2835
2836                } else {
2837                  subscriptionAnalytics.pushSubscriptionInitiatedEvent();
2838
2839                  showConfirmSubscriptionMessage();
2840
2841                  window.utils.setSubscriberStatus('verifyEmailSent');
2842                  subscriptionAnalytics.pushPageLoadedEventBySubscriberStatus();
2843                  subscriptionAnalytics.pushPageLoadCompletedEvent();
2844                }
2845              },
2846              error: function () {
2847                const verifySubscriberErrorSpan = $('#subDashVerifySubscriberError')[0];
2848                displayErrorMessage(verifySubscriberErrorSpan);
2849                jQuery('#subDashNewUserEmail').addClass( "errorMessageShow" );
2850                logClientError("ERROR", "Verify subscriber failed", "subscriptionDashboard.js");
2851              },
2852            });
2853          }
2854        });
2855
2856        dashboardEditMyPreferences.click(() => {
2857            loadFormForExistingUser();
2858        });
2859
2860        function isExistingSubscriber(response) {
2861            return (response.currentEmail != null);
2862        }
2863
2864        function loadFormForExistingUser() {
2865            isNewUser = false;
2866            navPersMain.removeClass('d-none');
2867
2868            window.utils.setSubscriberStatus('alreadySubscribed');
2869            subscriptionAnalytics.pushPageLoadedEventBySubscriberStatus();
2870            subscriptionAnalytics.pushPageLoadCompletedEvent();
2871
2872            populateExistingData(returningUserSubHistory);
2873            jQuery('.modal-subscriptionDashboard').removeClass('show-modal');
2874            jQuery('.modal-backdrop').remove();
2875            jQuery('body').removeClass('modal-open');
2876            saveButton.text('Save preferences');
2877            unsubscribe.css('display', 'block');
2878            transitionToForm();
2879        }
2880
2881        hideSubscriptionDashboardModal.click(() => {
2882            jQuery('.modal-subscriptionDashboard').removeClass('show-modal');
2883            jQuery('.modal-backdrop').remove();
2884            jQuery('body').removeClass('modal-open');
2885        });
2886
2887        /********** Already Subscribed - Next button **********/
2888
2889        alreadySubButton.click(() => {
2890            //Use to track the conversion of user
2891            if (validateEmail('subDashReturnUserEmail', 'subDashReturnUserEmailError')) {
2892                isNewUser = false;
2893                const noUserErrorSpan = $('#subDashReturnUserNoUser')[0];
2894                hideErrorMessage(noUserErrorSpan);
2895                subscriptionAnalytics.pushCallToActionEventOnAlreadySubscribedNextButtonClick();
2896
2897                $.ajax({
2898                    url: "/api/corporatesite/subscription",
2899                    type: "POST",
2900                    contentType: 'application/json',
2901                    dataType: "JSON",
2902                    data: JSON.stringify({
2903                        currentEmail: $('#subDashReturnUserEmail').val()
2904                    }),
2905                    success: function (response) {
2906                        populateExistingData(response);
2907                        saveButton.text('Save preferences');
2908                        unsubscribe.css('display', 'block');
2909                        transitionToForm();
2910                        window.utils.deleteCookie("subscriberStatus");
2911                        window.utils.deleteCookie("subscriberEmail");
2912                        window.utils.deleteCookie("subscriberIndustry");
2913
2914                        window.utils.setSubscriberStatus('alreadySubscribed');
2915                        subscriptionAnalytics.pushPageLoadedEventBySubscriberStatus();
2916                        subscriptionAnalytics.pushPageLoadCompletedEvent();
2917                    },
2918                    error: function () {
2919                        displayErrorMessage(noUserErrorSpan);
2920                        jQuery('#subDashReturnUserEmail').addClass("errorMessageShow");
2921                        logClientError("ERROR", "Already-subscribed lookup failed", "subscriptionDashboard.js");
2922                    },
2923                });
2924            }
2925        });
2926
2927        let subscriberStatus = window.utils.getCookieValue("subscriberStatus");
2928
2929        if (subscriberStatus === null || subscriberStatus === undefined){
2930            dashboardLoginPage.removeClass('d-none');
2931            dashboardLoginPage.addClass('d-flex');
2932        }
2933
2934        if ( subscriberStatus === 'confirmed' ) {
2935            returningUserSubHistory.currentEmail = window.utils.getCookieValue("subscriberEmail");
2936            returningUserSubHistory.industryId = window.utils.getCookieValue("subscriberIndustry");
2937            populatePersonalData(returningUserSubHistory);
2938            displayConfirmBannerOneLastStepMessage();
2939            window.utils.setSubscriberStatus('confirmed');
2940
2941            //select all the checkboxes by default
2942            checkboxes.prop('checked', true);
2943            updateSelectAllCheckBox();
2944            transitionToForm();
2945        } else if ( subscriberStatus === 'subscribed' ){
2946            transitionToForm();
2947            $('#subDashReturnUserEmail').val(window.utils.getCookieValue("subscriberEmail"));
2948            alreadySubButton.click();
2949        }
2950
2951        /********** Subscription preferences - Save button **********/
2952
2953        saveButton.click(() => {
2954
2955            const topicCheckbox = [];
2956            const authorCheckbox = [];
2957
2958            $("input:checkbox[name=topicCheckbox]:checked").each(function () {
2959                topicCheckbox.push($(this).attr('id'));
2960            });
2961
2962            $("input:checkbox[name='authorCheckbox']:checked").each(function () {
2963                authorCheckbox.push($(this).attr('id'));
2964            });
2965
2966            const {
2967                subscribedTopicIds,
2968                unsubscribedTopicIds,
2969                subscribedAuthorIds,
2970                unsubscribedAuthorIds,
2971            } = checkSubscriptionDiff(returningUserSubHistory, {
2972                subscribedTopicIds: topicCheckbox,
2973                subscribedAuthorIds: authorCheckbox
2974            })
2975
2976            const postParams = {
2977                currentEmail: returningUserSubHistory.currentEmail,
2978                subscribedTopicIds,
2979                unsubscribedTopicIds,
2980                subscribedAuthorIds,
2981                unsubscribedAuthorIds,
2982            };
2983
2984            $.ajax({
2985                url: "/api/corporatesite/subscription",
2986                type: "POST",
2987                contentType: 'application/json',
2988                dataType: "JSON",
2989                data: JSON.stringify(postParams),
2990                success: function (response) {
2991                    populateExistingData(response);
2992                    let subscriberStatus = window.utils.getCookieValue("subscriberStatus");
2993                    if ( subscriberStatus && subscriberStatus === 'confirmed') {
2994                        displayConfirmBannerThankYouMessage();
2995                        saveButton.text('Save preferences');
2996                        window.utils.setSubscriberStatus('subscriptionActivated');
2997
2998                        subscriptionAnalytics.pushSubscriptionActivatedEvent([...topicCheckbox, ...authorCheckbox]);
2999                        subscriptionAnalytics.pushPageLoadedEventBySubscriberStatus();
3000                        subscriptionAnalytics.pushPageLoadCompletedEvent();
3001
3002                        window.utils.deleteCookie("subscriberStatus");
3003                        window.utils.deleteCookie("subscriberEmail");
3004                        window.utils.deleteCookie("subscriberIndustry");
3005
3006                    } else {
3007                        $("#confirmBanner").hide();
3008                        displaySuccessMessage('Your subscription preferences have been saved.');
3009
3010                        let subscriberStatus = window.utils.getSubscriberStatus();
3011                        if (subscriberStatus === 'subscriptionActivated') {
3012                            window.utils.setSubscriberStatus('newSubscriptionManaged');
3013                        } else if (subscriberStatus === 'alreadySubscribed') {
3014                            window.utils.setSubscriberStatus('oldSubscriptionManaged');
3015                        }
3016
3017                        subscriptionAnalytics.pushSubscriptionManagedEvent([...subscribedTopicIds, ...subscribedAuthorIds]);
3018                        subscriptionAnalytics.pushPageLoadedEventBySubscriberStatus();
3019                        subscriptionAnalytics.pushPageLoadCompletedEvent();
3020                    }
3021                },
3022                error: function (failureResponse) {
3023                    logClientError("ERROR", "Subscription save failed: status=" + (failureResponse && failureResponse.status), "subscriptionDashboard.js");
3024                },
3025            });
3026        });
3027
3028        /********** Call to actions **********/
3029
3030        dashboardPrivacyPolicy.click(() => {
3031            subscriptionAnalytics.pushCallToActionEventOnViewPrivacyPolicy();
3032        });
3033
3034        /********** Personal information - Save button **********/
3035
3036        personalSaveButton.click((e) => {
3037            e.preventDefault();
3038
3039            const currentEmail = returningUserSubHistory.currentEmail;
3040            const newEmail = $('#subDashUpdateEmailInput').val();
3041            const industryId = $('#subDashUpdateIndustrySelect').val();
3042
3043            returningUserSubHistory.currentEmail = newEmail;
3044            returningUserSubHistory.industryId = industryId;
3045
3046            const postParams = {
3047                currentEmail,
3048                newEmail,
3049                industryId
3050            }
3051
3052            subscriptionAnalytics.pushCallToActionEventOnPersonalInformationSave();
3053
3054            $.ajax({
3055                url: "/api/corporatesite/subscription",
3056                type: "POST",
3057                contentType: 'application/json',
3058                dataType: "JSON",
3059                data: JSON.stringify(postParams),
3060                success: function (response) {
3061                    populatePersonalData(response);
3062                    displaySuccessMessage('Your personal details have been saved.');
3063                    inputPersonalInfo.removeClass('col-lg-6 col-sm-12 col-md-10 d-block');
3064                    inputPersonalInfo.addClass('col-lg-6 col-sm-12 col-md-10 d-none');
3065                    plainPersonalInfo.removeClass('d-done');
3066                    plainPersonalInfo.addClass('d-block');
3067                },
3068                error: function () {
3069                    logClientError("ERROR", "Personal details save failed", "subscriptionDashboard.js");
3070                },
3071            });
3072        });
3073
3074        unsubscribeAllDismissButton.click(() => {
3075            subscriptionAnalytics.pushCallToActionEventOnUnsubscribeFromAllDismissButtonClick();
3076            $('#unsubscribeModal').modal('hide');
3077            $(window).scrollTop(0);
3078            dashboardConfirmationContainer.removeClass('d-none');
3079            dashboardConfirmationContainer.addClass('dashboard-confirmation-container d-flex');
3080        });
3081
3082        /********** Unsubscribe from all link **********/
3083
3084        unsubscribeAllConfirmationButton.click(() => {
3085
3086            const { topicCheckbox, authorCheckbox } = gatherCheckboxData();
3087            subscriptionAnalytics.pushSubscriptionUnsubscribedEvent([...topicCheckbox, ...authorCheckbox]);
3088            window.utils.setSubscriberStatus('unsubscribedAll');
3089
3090            ignoreDirtyForm = true;
3091            let currentSubscribeEmail;
3092
3093            if ($('#subDashReturnUserEmail').val() == '') {
3094                currentSubscribeEmail = $('#subDashNewUserEmail').val()
3095            } else {
3096                currentSubscribeEmail = $('#subDashReturnUserEmail').val()
3097            }
3098            const postParams = {
3099                currentEmail: currentSubscribeEmail,
3100                active: false,
3101            };
3102
3103            $.ajax({
3104                url: "/api/corporatesite/subscription",
3105                type: "POST",
3106                contentType: 'application/json',
3107                dataType: "JSON",
3108                data: JSON.stringify(postParams),
3109                success: function () {
3110                    window.location.assign(`${origin}/content/corporatesite/us/en/corp/subscription-dashboard/unsubscribed.html`);
3111                },
3112                error: function () {
3113                    logClientError("ERROR", "Unsubscribe request failed", "subscriptionDashboard.js");
3114                    window.location.assign(`${origin}/content/corporatesite/us/en/corp/subscription-dashboard/unsubscribed.html`);
3115                },
3116            });
3117
3118        });
3119
3120        function updateSelectAllCheckBox() {
3121
3122            let unselectedCheckBoxes = getUnselectedCheckboxes();
3123
3124            // all checkboxes are checked
3125            if (unselectedCheckBoxes == 0) {
3126                selectAllCheckbox.prop('checked', true);
3127                selectAllCheckbox.addClass('checking-select-all-or-none');
3128                selectAllCheckbox.removeClass('checking-select-partial');
3129                $('input[name="authorCheckbox"]').blur();
3130                $('input[name="topicCheckbox"]').blur();
3131                return;
3132            }
3133
3134            // no checkboxes are checked
3135            if (getUnselectedCheckboxes() == checkboxes.length) {
3136                selectAllCheckbox.prop('checked', false);
3137                selectAllCheckbox.removeClass('checking-select-partial');
3138                selectAllCheckbox.addClass('checking-select-all-or-none');
3139                $('input[name="authorCheckbox"]').blur();
3140                $('input[name="topicCheckbox"]').blur();
3141                return;
3142            }
3143
3144            // some checkboxes are checked
3145            selectAllCheckbox.prop('checked', false);
3146            selectAllCheckbox.removeClass('checking-select-all-or-none');
3147            selectAllCheckbox.addClass('checking-select-partial');
3148            $('input[name="authorCheckbox"]').blur();
3149            $('input[name="topicCheckbox"]').blur();
3150        }
3151
3152        selectAllCheckbox.click(() => {
3153            // user is checking the select all checkbox
3154            if (selectAllCheckbox.prop('checked')) {
3155                checkboxes.prop('checked', true);
3156                jQuery('.dashboard-submit-button').focus();
3157            } else {
3158                // otherwise, user is unchecking the select all checkbox
3159                checkboxes.prop('checked', false);
3160            }
3161            updateSelectAllCheckBox();
3162            subscriptionAnalytics.pushCallToActionEventOnSelectAllCheckboxClick();
3163        });
3164
3165        checkboxes.click(() => {
3166            updateSelectAllCheckBox();
3167        });
3168
3169        unsubscribe.click(() => {
3170            $('#unsubscribeAllModal').modal({
3171                show: true
3172            });
3173            subscriptionAnalytics.pushCallToActionEventOnUnsubscribeFromAllLinkClick();
3174        });
3175
3176        tabSignUp.click(() => {
3177            subscriptionAnalytics.pushSubPageViewedEventOnSignUpTabClick();
3178            displaySignUpTab();
3179        });
3180
3181        tabAlreadySubscribed.click(() => {
3182            subscriptionAnalytics.pushSubPageViewedEventOnAlreadySubscribedTabClick();
3183            displayAlreadySubscribedTab();
3184        });
3185
3186        navPrefsMain.click(() => {
3187            subscriptionAnalytics.pushSubPageViewedEventOnSubscriptionPreferencesTabClick();
3188            displaySubscriptionPreferences();
3189        });
3190
3191        navPersMain.click(() => {
3192            subscriptionAnalytics.pushSubPageViewedEventOnPersonalInformationTabClick();
3193            displayPersonalInformation();
3194        });
3195
3196        dashboardEditButton.click(() => {
3197            subscriptionAnalytics.pushCallToActionEventOnPersonalInformationEdit();
3198            plainPersonalInfo.removeClass('d-block');
3199            plainPersonalInfo.addClass('d-none');
3200            inputPersonalInfo.removeClass('d-none');
3201            inputPersonalInfo.addClass('col-lg-6 col-sm-12 col-md-10 d-block');
3202        });
3203
3204        dashboardCancelButton.click(() => {
3205            subscriptionAnalytics.pushCallToActionEventOnPersonalInformationCancel();
3206
3207            // revert any unsaved changes
3208            populatePersonalData(returningUserSubHistory);
3209            hideErrorMessage($('#subDashUpdateEmailInputError')[0])
3210            hideErrorMessage($('#subDashUpdateIndustrySelectError')[0])
3211            plainPersonalInfo.removeClass('d-none');
3212            plainPersonalInfo.addClass('d-block');
3213            inputPersonalInfo.removeClass('col-lg-6 col-sm-12 col-md-10 d-block');
3214            inputPersonalInfo.addClass('d-none');
3215        });
3216    })
3217
3218
3219    $(window).scroll(function () {
3220        if ($('.dashboard-submit-button').length > 0) {
3221            var top_of_element = $(".dashboard-submit-button").offset().top;
3222            var bottom_of_element = $(".dashboard-submit-button").offset().top + $(".dashboard-submit-button").outerHeight();
3223            var bottom_of_screen = $(window).scrollTop() + $(window).innerHeight();
3224            var top_of_screen = $(window).scrollTop();
3225
3226            if ((bottom_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
3227                $('.dashboard-submit-button').focus();
3228            }
3229        }
3230    });
3231    
3232})();
3233$(document).ready(function(){
3234    $('.cmp-socialShare-list-item a, .cmp-socialShare-list-item button').on('click', function(){
3235        //Use to track the sharing of content
3236        window.dataLayer = window.dataLayer || [];
3237        window.dataLayer.push({
3238            "event" : "contentShared", //e13
3239            "medium": {
3240                "shareType":$(this).attr('data-shareType') , //v73
3241                "linkName": $(this).attr('data-pageUrl') //v72
3242            }
3243        });
3244
3245        if($(this).attr('data-socialShareURL') != u
3245ndefined){
3246            window.open($(this).attr('data-socialShareURL'), '_blank');
3247        }
3248    });
3249});
3250
3251(function() {
3252    'use strict';
3253
3254    $(".relatedLinkReferenceClass").on("click", function() {
3255        let targetURL = $(this).attr('href');
3256        if ( targetURL && !targetURL.endsWith(".pdf") ) {
3257            window.dataLayer = window.dataLayer || [];
3258            let relatedLinksId = $(this).attr("id");
3259            relatedLinksId = relatedLinksId.substring(0, relatedLinksId.length-1) +" "+ (parseInt(relatedLinksId.substring((relatedLinksId.length-1)))+1)
3260            let relatedLinksName = $(this).html().split("<");
3261            let cleanRelatedLinksName = relatedLinksName[0];
3262
3263
3264            window.dataLayer.push({
3265                "event": "callToAction", //e111
3266                "callToAction": {
3267                    "location": $(document).find("title")[0].text, //v103
3268                    "ctaName": relatedLinksId +"-"+ cleanRelatedLinksName, //v104
3269                    "ctaType": "Text Link" //v111
3270                }
3271            });
3272        }
3273
3274    });
3275
3276})();
3277(function () {
3278
3279    "use strict";
3280    var fontColors = {}; 
3281
3282    let onLight = (resource.properties["bgColor"] == '#FFFFFF' || resource.properties["bgColor"] == '#E6E6E6'
3283                || resource.properties["bgColor"] == '#DEF5F0' || resource.properties["bgColor"] == '#FFEDED' 
3284                || resource.properties["bgColor"] == '#FFF7DE' || resource.properties["bgColor"] == '#FFAD00' 
3285                || resource.properties["bgColor"] == '#F9F9F9') ?  true : false;
3286
3287    let redColorSelected = resource.properties["fontColor"] == '#C20029' ? true : false;
3288
3289    if( onLight && !redColorSelected ){
3290		fontColors.quoteAndHR = '#040505';
3291        fontColors.sourceAndTitle = '#232323';
3292
3293    } else if( onLight && redColorSelected ){
3294		fontColors.quoteAndHR = '#C20029';
3295        fontColors.sourceAndTitle = '#232323';
3296    }
3297
3298	if( !onLight ){
3299		fontColors.quoteAndHR = '#FFFFFF';
3300        fontColors.sourceAndTitle = '#FFFFFF';
3301    }
3302    
3303    return fontColors;
3304
3305});
3306// bind an event handler to the "click" event for each person container
3307
3308$('.cmp-peoplenoimages__person-container--enabledlink').on('click', function() {
3309    const link = $(this).find('.cmp-peoplenoimages__name');
3310    const url = link.attr('href');
3311    if (url) {
3312        window.open(url, '_self');
3313    }
3314});
3315// bind an event handler to the "click" event for each person container
3316
3317$('.cmp-people__person-container--enabledlink').on('click', function() {
3318    const link = $(this).find('.cmp-people__name');
3319    const url = link.attr('href');
3320    if (url) {
3321        window.open(url, '_self');
3322    }
3323});
3324(function() {
3325    'use strict';
3326
3327    const ASSET_CLASS_EQUITIES = ['Global Equity', 'US Equity', 'International Equity'];
3328    const ASSET_CLASS_FIXED_INCOME = 'Fixed Income';
3329    const ASSET_CLASS_OTHER = 'Other';
3330
3331    $(document).ready(function() {
3332
3333        let firstNameField = $('#firstNameField'),
3334            lastNameField = $('#lastNameField'),
3335            titleField = $('#titleField'),
3336            emailField = $('#emailField'),
3337            phoneField = $('#phoneField'),
3338            firmNameField = $('#firmNameField'),
3339            firmAddressField = $('#firmAddressField'),
3340            firmAddressField2 = $('#firmAddressField2'),
3341            firmCityField = $('#firmCityField'),
3342            firmStateField = $('#firmStateField'),
3343            firmZipField = $('#firmZipField'),
3344            firmWebsiteField = $('#firmWebsiteField'),
3345            firmAssetsField = $('#firmAssetsField'),
3346            firmMinorityWomenRadio = $('input:radio[name=firmMinorityWomenRadio]'),
3347            firmMinorityWomenRadioYes = firmMinorityWomenRadio[0],
3348            firmMinorityWomenRadioNo = firmMinorityWomenRadio[1],
3349            strategyNameField = $('#strategyNameField'),
3350            assetClassDropdown = $('#assetClassDropdown'),
3351            equityCapitalizationDropdown = $('#equityCapitalizationDropdown'),
3352            equityCapitalizationDiv = $('.cmp-managerPortal__equity-capitalization'),
3353            equityStyleDropdown = $('#equityStyleDropdown'),
3354            equityStyleDiv = $('.cmp-managerPortal__equity-style'),
3355            fixedIncomeStyleDropdown = $('#fixedIncomeStyleDropdown'),
3356            fixedIncomeStyleDiv = $('.cmp-managerPortal__fixed-income-style'),
3357            fixedIncomeDurationDropdown = $('#fixedIncomeDurationDropdown'),
3358            fixedIncomeDurationDiv = $('.cmp-managerPortal__fixed-income-duration'),
3359            otherDescribeField = $('#otherDescribeField'),
3360            otherDescribeDiv = $('.cmp-managerPortal__other-describe'),
3361            marketStrategyField = $('#marketStrategyField'),
3362            submitButton = $('#submitButton'),
3363            submitErrorMessage = $('#submitErrorMessage')[0],
3364            confirmationModal = $('#confirmationModal'),
3365            confirmationModalButton = $('#confirmationModalButton');
3366
3367        firstNameField.on('focusout', function() {
3368            validateName('firstNameField', 'firstNameFieldErrorMessage');
3369        });
3370
3371        lastNameField.on('focusout', function() {
3372            validateName('lastNameField', 'lastNameFieldErrorMessage');
3373        });
3374
3375        titleField.on('focusout', function() {
3376            validateRequiredField('titleField', 'titleFieldErrorMessage');
3377        });
3378
3379        emailField.on('focusout', function() {
3380            validateEmail('emailField', 'emailFieldErrorMessage');
3381        });
3382
3383        phoneField.on('focusout', function() {
3384            validateOptionalPhone('phoneField', 'phoneFieldErrorMessage');
3385        });
3386
3387        phoneField.on('input', function() {
3388            this.value = formatPhoneInput(this.value);
3389        });
3390
3391        firmNameField.on('focusout', function() {
3392            validateRequiredField('firmNameField', 'firmNameFieldErrorMessage');
3393        });
3394
3395        firmAddressField.on('focusout', function() {
3396            validateRequiredField('firmAddressField', 'firmAddressFieldErrorMessage');
3397        });
3398
3399        firmCityField.on('focusout', function() {
3400            validateRequiredField('firmCityField', 'firmCityFieldErrorMessage');
3401        });
3402
3403        firmStateField.on('focusout', function() {
3404            validateRequiredState('firmStateField', 'firmStateFieldErrorMessage');
3405        });
3406
3407        firmStateField.on('input', function() {
3408            this.value = formatAlphabeticInput(this.value).toUpperCase();
3409        });
3410
3411        firmZipField.on('focusout', function() {
3412            validateRequiredZipCode('firmZipField', 'firmZipFieldErrorMessage');
3413        });
3414
3415        firmZipField.on('input', function() {
3416            this.value = formatNumericInput(this.value);
3417        });
3418
3419        firmAssetsField.on('input', function() {
3420            this.value = formatDollarAmountInput(this.value);
3421        });
3422
3423        strategyNameField.on('focusout', function() {
3424            validateRequiredField('strategyNameField', 'strategyNameFieldErrorMessage');
3425        });
3426
3427        assetClassDropdown.on('change', function() {
3428            validateRequiredDropdown('assetClassDropdown', 'assetClassDropdownErrorMessage');
3429
3430            if (ASSET_CLASS_EQUITIES.some(str => this.value.includes(str))) {
3431                equityCapitalizationDiv.show();
3432                equityStyleDiv.show();
3433            } else {
3434                equityCapitalizationDiv.hide();
3435                equityStyleDiv.hide();
3436            }
3437
3438            if (this.value.includes(ASSET_CLASS_FIXED_INCOME)) {
3439                fixedIncomeStyleDiv.show();
3440                fixedIncomeDurationDiv.show();
3441            } else {
3442                fixedIncomeStyleDiv.hide();
3443                fixedIncomeDurationDiv.hide();
3444            }
3445
3446            if (this.value.includes(ASSET_CLASS_OTHER)) {
3447                otherDescribeDiv.show();
3448            } else {
3449                otherDescribeDiv.hide();
3450            }
3451        });
3452
3453        equityCapitalizationDropdown.on('change', function() {
3454            validateRequiredDropdown('equityCapitalizationDropdown', 'equityCapitalizationDropdownErrorMessage');
3455        });
3456
3457        equityStyleDropdown.on('change', function() {
3458            validateRequiredDropdown('equityStyleDropdown', 'equityStyleDropdownErrorMessage');
3459        });
3460
3461        fixedIncomeStyleDropdown.on('change', function() {
3462            validateRequiredDropdown('fixedIncomeStyleDropdown', 'fixedIncomeStyleDropdownErrorMessage');
3463        });
3464
3465        fixedIncomeDurationDropdown.on('change', function() {
3466            validateRequiredDropdown('fixedIncomeDurationDropdown', 'fixedIncomeDurationDropdownErrorMessage');
3467        });
3468
3469        otherDescribeField.on('focusout', function() {
3470            validateRequiredField('otherDescribeField', 'otherDescribeFieldErrorMessage');
3471        });
3472
3473        marketStrategyField.on('focusout', function() {
3474            validateRequiredField('marketStrategyField', 'marketStrategyFieldErrorMessage');
3475        });
3476
3477        submitButton.on('click', function() {
3478            if (validateManagerPortalForm()) {
3479                pushEventCTA();
3480                callManagerPortalService();
3481            } else {
3482                $('.errorMessageShow').first().focus();
3483            }
3484        });
3485
3486        confirmationModalButton.on('click', function() {
3487            confirmationModal.modal('hide');
3488            resetForm();
3489        });
3490
3491        confirmationModal.on('hide.bs.modal', function() {
3492            resetForm();
3493        });
3494
3495        function validateManagerPortalForm() {
3496            const isValidFirstName = validateRequiredField('firstNameField', 'firstNameFieldErrorMessage');
3497            const isValidLastName = validateRequiredField('lastNameField', 'lastNameFieldErrorMessage');
3498            const isValidTitle = validateRequiredField('titleField', 'titleFieldErrorMessage');
3499            const isValidEmail = validateEmail('emailField', 'emailFieldErrorMessage');
3500            const isValidPhone = validateOptionalPhone('phoneField', 'phoneFieldErrorMessage');
3501            const isValidFirmName = validateRequiredField('firmNameField', 'firmNameFieldErrorMessage');
3502            const isValidFirmAddress = validateRequiredField('firmAddressField', 'firmAddressFieldErrorMessage');
3503            const isValidFirmCity = validateRequiredField('firmCityField', 'firmCityFieldErrorMessage');
3504            const isValidFirmState = validateRequiredState('firmStateField', 'firmStateFieldErrorMessage');
3505            const isValidFirmZip = validateRequiredZipCode('firmZipField', 'firmZipFieldErrorMessage');
3506            const isValidStrategyName = validateRequiredField('strategyNameField', 'strategyNameFieldErrorMessage');
3507            const isValidAssetClass = validateRequiredDropdown('assetClassDropdown', 'assetClassDropdownErrorMessage');
3508            const isValidEquityCapitalization = validateRequiredDropdown('equityCapitalizationDropdown', 'equityCapitalizationDropdownErrorMessage');
3509            const isValidEquityStyle = validateRequiredDropdown('equityStyleDropdown', 'equityStyleDropdownErrorMessage');
3510            const isValidFixedIncomeStyle = validateRequiredDropdown('fixedIncomeStyleDropdown', 'fixedIncomeStyleDropdownErrorMessage');
3511            const isValidFixedIncomeDuration = validateRequiredDropdown('fixedIncomeDurationDropdown', 'fixedIncomeDurationDropdownErrorMessage');
3512            const isValidOtherDescribe = validateRequiredField('otherDescribeField', 'otherDescribeFieldErrorMessage');
3513            const isValidMarketStrategy = validateRequiredField('marketStrategyField', 'marketStrategyFieldErrorMessage');
3514
3515            if (isValidFirstName && isValidLastName && isValidTitle && isValidEmail && isValidPhone &&
3516                isValidFirmName && isValidFirmAddress && isValidFirmCity && isValidFirmState && isValidFirmZip &&
3517                isValidStrategyName && isValidAssetClass && isValidMarketStrategy) {
3518
3519                if (ASSET_CLASS_EQUITIES.some(str => assetClassDropdown.val().includes(str))) {
3520                    if (isValidEquityCapitalization && isValidEquityStyle) {
3521                        return true;
3522                    }
3523                } else if (assetClassDropdown.val().includes(ASSET_CLASS_FIXED_INCOME)) {
3524                    if (isValidFixedIncomeStyle && isValidFixedIncomeDuration) {
3525                        return true;
3526                    }
3527                } else if (assetClassDropdown.val().includes(ASSET_CLASS_OTHER)) {
3528                    if (isValidOtherDescribe) {
3529                        return true;
3530                    }
3531                } else {
3532                    return true;
3533                }
3534            }
3535            return false;
3536        }
3537
3538        function callManagerPortalService() {
3539            hideErrorMessage(submitErrorMessage);
3540
3541            $.ajax({
3542                url: "/api/corporatesite/managerportalservice",
3543                type: "POST",
3544                contentType: 'application/json',
3545                dataType: "JSON",
3546                data: JSON.stringify(getFormJsonData()),
3547                success: function (response) {
3548                    confirmationModal.modal('show');
3549                },
3550                error: function () {
3551                    displayErrorMessage(submitErrorMessage);
3552                    logClientError("ERROR", "Manager portal form submission failed", "managerPortalForm.js");
3553                }
3554            });
3555
3556        }
3557
3558        function getFormJsonData() {
3559            return {
3560                'PRGMgrPortal': {
3561                    'Firm': {
3562                        'FirmName': firmNameField.val(),
3563                        'Website': firmWebsiteField.val(),
3564                        'Address1': firmAddressField.val(),
3565                        'Address2': firmAddressField2.val(),
3566                        'City': firmCityField.val(),
3567                        'State': firmStateField.val(),
3568                        'ZipCode': firmZipField.val(),
3569                        'AUM': firmAssetsField.val(),
3570                        'Minority-Women-flag': getSelectedRadioValue(firmMinorityWomenRadio)
3571                    },
3572                    'Contact': {
3573                        'FirstName': firstNameField.val(),
3574                        'LastName': lastNameField.val(),
3575                        'Title': titleField.val(),
3576                        'Phone': phoneField.val(),
3577                        'Email': emailField.val()
3578                    },
3579                    'Strategy': {
3580                        'StrategyName': strategyNameField.val(),
3581                        'AssetClass': getDropdownValue(assetClassDropdown),
3582                        'EquityFocus': getDropdownValue(equityCapitalizationDropdown),
3583                        'StyleFocus': getDropdownValue(equityStyleDropdown),
3584                        'FixedIncStyle': getDropdownValue(fixedIncomeStyleDropdown),
3585                        'FixedIncDur': getDropdownValue(fixedIncomeDurationDropdown),
3586                        'StratDesc': marketStrategyField.val(),
3587                        'AssetDesc': otherDescribeField.val()
3588                    }
3589                }
3590            };
3591        }
3592
3593        function getSelectedRadioValue(radio) {
3594            return radio[0].checked ? radio[0].value : radio[1].value;
3595        }
3596
3597        function getDropdownValue(dropdown) {
3598            return dropdown.val() !== 'Choose' ? dropdown.val() : '';
3599        }
3600
3601        function resetForm() {
3602            clearAllFormFields();
3603            hideAssetClassDetailsFields();
3604            window.scrollTo(0, 0);
3605        }
3606
3607        function clearAllFormFields() {
3608            firstNameField.val('');
3609            lastNameField.val('');
3610            titleField.val('');
3611            emailField.val('');
3612            phoneField.val('');
3613            firmNameField.val('');
3614            firmAddressField.val('');
3615            firmAddressField2.val('');
3616            firmCityField.val('');
3617            firmStateField.val('');
3618            firmZipField.val('');
3619            firmWebsiteField.val('');
3620            firmAssetsField.val('');
3621            firmMinorityWomenRadioYes.checked = false;
3622            firmMinorityWomenRadioNo.checked = true;
3623            strategyNameField.val('');
3624            assetClassDropdown.val('Choose');
3625            equityCapitalizationDropdown.val('Choose');
3626            equityStyleDropdown.val('Choose');
3627            fixedIncomeStyleDropdown.val('Choose');
3628            fixedIncomeDurationDropdown.val('Choose');
3629            otherDescribeField.val('');
3630            marketStrategyField.val('');
3631        }
3632
3633        function hideAssetClassDetailsFields() {
3634            equityCapitalizationDiv.hide();
3635            equityStyleDiv.hide();
3636            fixedIncomeStyleDiv.hide();
3637            fixedIncomeDurationDiv.hide();
3638            otherDescribeDiv.hide();
3639        }
3640
3641        function pushEventCTA() {
3642            window.dataLayer = window.dataLayer || [];
3643            window.dataLayer.push({
3644                "event": "callToAction", //e111
3645                "callToAction": {
3646                    "location": submitButton.data('ctalocation'), //v103
3647                    "ctaName": "submit", //v104
3648                    "ctaType": "primary button" //v111
3649                }
3650            });
3651        }
3652    });
3653})()
3654$(document).ready(function () {
3655
3656    //applying the aria-hidden attribute to all the elements that are not visible on the screen 
3657    document.querySelectorAll('.legalnote .readmore .legal-note-text').forEach((note) => {
3658        let count = 0;
3659        note.children.forEach((child) => {
3660            if (count > 0) {
3661                child.setAttribute('aria-hidden', 'true');
3662            }
3663            count++;
3664        });
3665    });
3666
3667    $(".readmore-link").on("keypress", function(event) {
3668        if ( event.which === 13 || event.which === 32) {
3669            $(event.target).click();
3670        }
3671    });
3672
3673    $(".readmore-link").click(function (e) {
3674        let $el, $currentLegalNote, $up;
3675        let totalHeight = 0;
3676        let childrenOfNotesVisible = false;
3677        let isExpanded = $(e.target).hasClass("expand");
3678
3679        $el = $(this);
3680        $currentLegalNote = $el.parent();
3681        $up = $currentLegalNote.closest(".readmore");
3682
3683        let $notes = $currentLegalNote.find(".legal-note-text");
3684        let observed = $notes[0];
3685
3686        $up.toggleClass('active');
3687
3688
3689        let observer = createResizeObserver($notes, isExpanded);
3690
3691        /*checking if the .readmore div has the expand class and if it doesn't add it otherwise remove it*/
3692        if (!isExpanded) {
3693            $(e.target).parent(".readmore").addClass("expand");
3694            $(e.target).addClass("expand");
3695            $(e.target).html("Read less");
3696            $currentLegalNote.find(".readmore-gradient").css('opacity', '0');
3697
3698            childrenOfNotesVisible = true;
3699            childElementsOfNotesVisible($notes, childrenOfNotesVisible);
3700
3701            observer.observe(observed);
3702            window.scroll($(e.target).closest(".legal-container-border").parent().offset());
3703        } else {
3704            $(e.target).parent(".readmore").removeClass("expand");
3705            $(e.target).removeClass("expand");
3706            $(e.target).html("Read more");
3707            $currentLegalNote.find(".readmore-gradient").css('opacity', '1');
3708
3709            childElementsOfNotesVisible($notes, childrenOfNotesVisible);
3710
3711            observer.observe(observed);
3712            window.scroll($(e.target).closest(".legal-container-border").parent().offset());
3713        }
3714    });
3715
3716    function createResizeObserver($notes, isExpanded) {
3717        //setting up the ResizeObserver
3718        let resizeObserver = new ResizeObserver(() => {
3719            let totalHeightNotes = 0;
3720            //getting the height of the .legal-note-text children
3721            $notes.children().each(function () {
3722                totalHeightNotes = totalHeightNotes + $(this).outerHeight(true);
3723            });
3724            totalHeight = totalHeightNotes;
3725            if (!isExpanded) {
3726                $notes.css("height", totalHeight);
3727            } else {
3728                $notes.css("height", 50);
3729            }
3730        });
3731        return resizeObserver;
3732    }
3733
3734    function childElementsOfNotesVisible($notes, childrenOfNotesVisible) {
3735        let count = 0;
3736        $notes.children().each(function () {
3737            if (!childrenOfNotesVisible && count > 0) {
3738                $(this).attr('aria-hidden', 'true');
3739            } else {
3740                $(this).removeAttr('aria-hidden');
3741            }
3742            count++;
3743        });
3744    }
3745});
3746$(document).ready(function () {
3747    let pictureArray = document.querySelectorAll('.image-container');
3748    if (pictureArray.length > 0) {
3749        let imageLazyLoading = document.querySelectorAll('.image_loading_lazy');
3750        let checkForGif = document.querySelectorAll('.image_gif_true');
3751        if(checkForGif.length > 0 || imageLazyLoading.length > 0 ){
3752            pictureArray.forEach((pictureEntry) => {
3753                const options = {
3754                    root: null,
3755                    threshold: 0.75
3756                };
3757
3758                /*handle load of animation by using the IntersectionObserver */
3759                let gifImage = pictureEntry.querySelector('.image_gif_true');
3760                let gifLoaded = pictureEntry.querySelector('.image_gif_true.image-loaded');
3761                if ( gifImage && !gifLoaded ) {
3762                    const observer = new IntersectionObserver((entries , entryObserver) => {
3763                        entries.forEach(entry => {
3764                            if ( entry.isIntersecting ) {
3765                                let defaultImage = pictureEntry.querySelector('.image-container__picture-Image');
3766                                gifImage.classList.add('image-loaded');
3767                                defaultImage.setAttribute('src', defaultImage.getAttribute('data-image'));
3768                                defaultImage.setAttribute('alt', defaultImage.getAttribute('data-gif-image-alternate'));
3769                                pictureEntry.querySelectorAll('.image-container__picture-Source').forEach((source) => {
3770                                    source.setAttribute('srcset', defaultImage.getAttribute('data-image'));
3771                                });
3772                                entryObserver.disconnect();
3773                            }
3774                        });
3775                    }, options);
3776                    observer.observe(pictureEntry);
3777                }
3778            });
3779        }
3780    }
3781
3782});
3783(() => {
3784    $(document).ready(function () {
3785        'use strict';
3786   
3787        document.addEventListener("click", function (event) {
3788            const link = event.target.closest(".cmp-hrztimelineslide .aemvc-v2-videoplayer .aemvc-v2-video__accessible .c11n-link");
3789            const bodyElement = document.querySelector("body");
3790                     
3791            if (link) {
3792                // Disable default View Transcript functionality
3793                event.stopImmediatePropagation();
3794                event.preventDefault();
3795
3796                // Get ID of the closest slide component
3797                let slideComponent = link.closest('.cmp-hrztimelineslide');
3798                let currentEventId = slideComponent ? slideComponent.id : `slide-${new Date().getTime()}`;
3799
3800                // Get transcript text from the video player component
3801                let transcriptText = "Transcript not available";
3802
3803                if (slideComponent) {
3804                    const videoPlayer = slideComponent.querySelector("c11n-aem-el-videoplayer-v2"); 
3805
3806                    if (videoPlayer) {
3807                        transcriptText = videoPlayer.getAttribute("transcripttext") || "Transcript not available";
3808                    } else {
3809                        console.warn(`No video player found inside slide ${currentEventId}`);
3810                    }
3811                }
3812
3813                // Check if modal exists, otherwise inject it
3814                let modal = document.querySelector(`#modal-event-${currentEventId}`);
3815                if (!modal) {
3816                    modal = document.createElement("dialog");
3817                    modal.id = `modal-event-${currentEventId}`;
3818                    modal.classList.add("cmp-hrztimelineslide-transcript-modal");
3819                    modal.innerHTML = `
3820                        <div class="cmp-hrztimelineslide-transcript-modal__container">
3821                            <div class="cmp-hrztimelineslide-transcript-modal__btnContainer--close">
3822                                <button class="cmp-hrztimelineslide-transcript-modal--closeBtn"
3823                                        title="Close the transcript modal"
3824                                        aria-label="Close the transcript modal">
3825                                    <span class="cmp-hrztimelineslide-transcript-modal--closeBtnIcon" aria-hidden="true"></span>
3826                                </button>
3827                            </div>
3828                            <div class="cmp-hrztimelineslide-transcript-modal__contentContainer">
3829                                <div class="cmp-hrztimelineslide-transcript-modal__copy">
3830                                    ${transcriptText}
3831                                </div>                          
3832                            </div>
3833                        </div>
3834                    `;
3835                    document.body.appendChild(modal);
3836                } else {
3837                    // Update transcript content if modal already exists
3838                    let transcriptContainer = modal.querySelector("cmp-hrztimelineslide-transcript-modal__copy");
3839                    if (transcriptContainer) {
3840                        transcriptContainer.innerHTML = transcriptText;
3841                    }
3842                }
3843
3844                modal.style.opacity = "0";
3845                modal.style.display = "block";
3846
3847                setTimeout(() => {
3848                    modal.showModal();
3849                    modal.style.opacity = "1";
3850                }, 50);
3851
3852                bodyElement.style.overflow = 'hidden';
3853                handleClickAndEscapeModal(`#modal-event-${currentEventId}`);
3854
3855            }
3856        }, true);
3857
3858        //need to extract this function as a global function so it can be reused without creating code duplication
3859        const handleClickAndEscapeModal = function handleEscapeModal(targetModalSelector) {
3860            const targetModal = document.querySelector(targetModalSelector);
3861
3862            if (!targetModal) return;
3863
3864            $(document).keydown(function (event) {             
3865                if (event.key === "Escape" && targetModal.hasAttribute("open")) {
3866                    closeModal(targetModal);
3867                }
3868            });
3869
3870            $(targetModal).click(function (event) {
3871                if (event.target.nodeName === 'DIALOG') {
3872                    closeModal(targetModal);
3873                }
3874            });
3875        };
3876
3877        function closeModal(modal) {
3878            modal.style.opacity = "0";
3879            setTimeout(() => {
3880                modal.close();
3881                modal.style.display = 'none';
3882                modal.removeAttribute("open");
3883                document.body.style.overflow = 'visible';
3884            }, 700);
3885        }
3886
3887        $(document).on("click", ".cmp-hrztimelineslide-transcript-modal--closeBtn", function () {
3888            const modal = $(this).closest("dialog")[0];
3889            
3890            if(modal) {
3891                closeModal(modal);
3892            } 
3893        });
3894    });
3895})();
3896
3897
3898let skipRecordSubPageViewed = false;
3899
3900(function () {
3901    "use strict";
3902
3903    // set up timeline slide navigation
3904    const htlSlideList = $('.cmp-hrztimelineslide');
3905    const firstTimelineSlide = $('.htl-content > div.aem-Grid > .horizontalTimelineSlide').not('.vanguardEffectSlide').first();
3906    let earliestSlideYear = $('[data-vg-htl-year]:first').data('vg-htl-year');
3907    let latestSlideYear = $('[data-vg-htl-year]:last').data('vg-htl-year');
3908
3909    let currentYear = earliestSlideYear;
3910    let currentSlideIndex = 0;
3911
3912    //console.log('num of slides: ' + htlSlideList.length);
3913
3914    // Position timeline bar in correct spot
3915    updateTimelineBarPosition();
3916    $(window).on('resize', updateTimelineBarPosition);
3917
3918    // loop through years adding markers
3919    htlSlideList.each(function() {
3920        let markerYear = parseInt($(this).attr('data-vg-htl-year'));
3921        if (!Number.isNaN(markerYear) && $('#htl-marker-' + markerYear).length === 0) {
3922            let marker = $('<div/>').addClass("htl-progress-marker").attr('id', 'htl-marker-' + markerYear).css('marginLeft', ((markerYear - earliestSlideYear) * 2).toString() + '%');
3923            $('.htl-progress').append(marker);
3924        }
3925    });
3926
3927    function newSlide() {
3928        if ($('.htl-progress-container').css('display') != 'none' && $('.htl-progress-indicator').length) {
3929            $('.htl-progress-indicator').css('margin-left', ((parseInt(currentYear) - earliestSlideYear) * 2).toString() + '%');
3930            $('.htl-progress').attr('aria-valuenow', currentYear.toString());
3931        }
3932
3933        $('.htl-tve-button').attr('data-event-id', currentYear.toString());
3934        $('.htl-year').html(currentYear.toString());
3935    }
3936
3937    $('#htl-controls_previous_button').on('click', function() {
3938        // add analytics call
3939        recordAnalytics(document.title.split("|")[0], 'Previous Year', 'custom button');
3940    });
3941
3942    $('#htl-controls_previous_button').on('keydown', function(e) {
3943        if (e.keyCode === 32 || e.keyCode === 13) { //hit SPACE or ENTER
3944            e.preventDefault();
3945            $('#htl-controls_previous').trigger('click');
3946        }
3947    });
3948
3949    $('#htl-controls_next_button').on('click', function() {
3950        // add analytics call
3951        recordAnalytics(document.title.split("|")[0], 'Next Year', 'custom button');
3952    });
3953    $('#htl-controls_next_button').on('keydown', function(e) {
3954        if (e.keyCode === 32 || e.keyCode === 13) { //hit SPACE or ENTER
3955            e.preventDefault();
3956            $('#htl-controls_next').trigger('click');
3957        }
3958    });
3959    $('.htl-next-slide-link').on('keydown', function(e) {
3960        if (e.keyCode === 32 || e.keyCode === 13) { //hit SPACE or ENTER
3961            e.preventDefault();
3962            $('#htl-controls_next').trigger('click');
3963        }
3964    });
3965
3966    $('#htl-controls_beginning_button').on('click', function() {
3967        goToStart();
3968    });
3969    $('#htl-controls_beginning_button').on('keydown', function(e) {
3970        if (e.keyCode === 32 || e.keyCode === 13) { //hit SPACE or ENTER
3971            e.preventDefault();
3972            $('#htl-controls_beginning_button').trigger('click');
3973        }
3974    });
3975
3976    $(document).on('keydown', function(e) {
3977        if (e.key === 'ArrowRight' || e.keyCode === 39) {
3978            e.preventDefault();
3979            $('#htl-controls_next').trigger('click');
3980        } else if (e.key === 'ArrowLeft' || e.keyCode === 37) {
3981            e.preventDefault();
3982            $('#htl-controls_previous').trigger('click');
3983        }
3984    });
3985
3986    function goToStart() {
3987        skipRecordSubPageViewed = true;
3988        
3989        // scroll to start of timeline or start of page if scroll container not found
3990        let scrollContainer = document.querySelector('.cmp-horizontaltimeline__container');
3991        let beginningOffset = scrollContainer ? scrollContainer.offsetTop : 0;
3992
3993        window.scrollTo({
3994            top: beginningOffset,
3995            behavior: 'smooth'
3996        });
3997        
3998        // add analytics call
3999        recordAnalytics(document.title.split("|")[0], 'Back to start', 'custom button');
4000
4001        // force current slide to start
4002        currentYear = earliestSlideYear;
4003        newSlide();
4004        
4005        // Wait until the scroll has finished
4006        setTimeout(function() {
4007            skipRecordSubPageViewed = false;
4008        }, 2000);
4009    }
4010
4011    $('#htl-controls_end_button').on('click', function() {
4012        goToEnd();
4013    });
4014    $('#htl-controls_end_button').on('keydown', function(e) {
4015        if (e.keyCode === 32 || e.keyCode === 13) { //hit SPACE or ENTER
4016            e.preventDefault();
4017            $('#htl-controls_end_button').trigger('click');
4018        }
4019    });
4020
4021    function goToEnd() {
4022        skipRecordSubPageViewed = true;
4023        
4024        // scroll to final slide or end of page if scroll container not found
4025        let scrollContainer = document.querySelector('.cmp-horizontaltimeline__container');
4026        let endOffset = scrollContainer ? scrollContainer.getBoundingClientRect().bottom + window.scrollY - window.innerHeight : document.documentElement.scrollHeight;
4027
4028        window.scrollTo({ 
4029            top: endOffset,
4030            behavior: 'smooth'
4031        });
4032
4033        // add analytics call
4034        recordAnalytics(document.title.split("|")[0], 'Forward to end', 'custom button');
4035
4036        // force current slide to end
4037        currentYear = latestSlideYear;
4038        newSlide();
4039        
4040        // Wait until the scroll has finished
4041        setTimeout(function() {
4042            skipRecordSubPageViewed = false;
4043        }, 2000);
4044    }
4045
4046
4047    // hide the slides toolbar
4048    let slideObserver2 = new IntersectionObserver(
4049        function (entries) {
4050            entries.forEach(entry => {
4051                if (entry.isIntersecting) {
4052                    if ($('.htl-footer').hasClass('show')) {
4053                        $('.htl-footer').removeClass('show');
4054                    }
4055                }
4056            });
4057        },
4058        { root: document.querySelector('.cmp-horizontaltimeline'), threshold: 0.5 }
4059    );
4060    const noHTLToolbar = document.querySelectorAll('.horizontalTimelineSlide');
4061    if (noHTLToolbar && noHTLToolbar.length > 1) {
4062        slideObserver2.observe(noHTLToolbar[0]);
4063        slideObserver2.observe(noHTLToolbar[noHTLToolbar.length - 1]);
4064    }
4065
4066    // show the slides toolbar, change the year, and move the indicator as the slide changes
4067    let slideObserverYear = new IntersectionObserver(
4068        function (entries) {
4069            entries.forEach(entry => {
4070                if (entry.isIntersecting) {
4071                    //console.log('isIntersecting:' + entry.target.id);
4072                    if (!$('.htl-footer').hasClass('show') && !$(entry.target).hasClass('htl-exit')) {
4073                        $('.htl-footer').addClass('show');
4074                    }
4075                    const newYear = $(entry.target).data('vg-htl-year');
4076                    if (newYear) {
4077                        currentYear = newYear;
4078                        currentSlideIndex = htlSlideList.index(htlSlideList.filter("[data-vg-htl-year='" + currentYear + "']"));
4079
4080                        newSlide();
4081                        let headline = entry.target.getAttribute("data-vg-htl-analytics-headline") ? "_" + entry.target.getAttribute("data-vg-htl-analytics-headline").trim() : "";
4082                        recordSubPageViewed(currentYear.toString() + headline, "true");
4083                    } else {
4084                        //send event for slides that do not have the data-vg-htl-year attribute like the Vanguard effect ones
4085                        let headline = entry.target.getAttribute("data-vg-htl-analytics-headline") ? "Slide_" + entry.target.getAttribute("data-vg-htl-analytics-headline").trim() : null;
4086                        if(headline){
4087                            recordSubPageViewed(headline, "true");
4088                        }
4089                    }
4090                }
4091            });
4092        },
4093        { root: null, threshold: 0.25 }
4094    );
4095    document.querySelectorAll('*[data-vg-slide]:not(.htl-intro)').forEach(item => {
4096        slideObserverYear.observe(item);
4097    });
4098
4099    $('.htl-intro-cta').on('click', function(e) {
4100        recordAnalytics(document.title.split("|")[0], 'Start exploring', 'custom button');
4101    });
4102    $('.htl-intro-cta').keydown(function(e) {
4103        if (e.key === " " || e.key === 'Enter' || e.keyCode === 32 || e.keyCode === 13) { //hit SPACE or ENTER
4104            $(this).trigger('click');
4105        }
4106    });
4107
4108    // Repositioning timeline nav bar based on mobile and desktop
4109    function updateTimelineBarPosition() {
4110        const timelineBar = $('#htl-footer');
4111        if(window.matchMedia('(max-width: 991px), (min-width:992px) and (max-width:1199px) and (orientation: portrait)').matches) {
4112            if (!$(timelineBar).parent().hasClass('aem-grid')) {
4113                timelineBar.insertBefore(firstTimelineSlide);
4114            }
4115        } else {
4116            if (!$(timelineBar).parent().hasClass('horizontalTimeline')){
4117                timelineBar.insertBefore($('.horizontalTimeline >
4117 div.cmp-horizontaltimeline'));
4118            }
4119        }
4120    }
4121
4122    function recordAnalytics(location, name, type) {
4123        // add analytics call
4124        window.dataLayer = window.dataLayer || [];
4125        window.dataLayer.push({
4126            "event": "callToAction", //e111
4127            "callToAction": {
4128                "location": location.replace("’", "'").trim(), //v103
4129                "ctaName": name, //v104
4130                "ctaType": type //v111
4131            }
4132        });
4133    }
4134
4135    function recordSubPageViewed(subPageID, clicked) {
4136        if (skipRecordSubPageViewed) {
4137            //skipRecordSubPageViewed = false;
4138            return;
4139        }
4140        //Used to signify that a subPage was activated/viewed
4141        window.dataLayer = window.dataLayer || [];
4142        window.dataLayer.push({
4143            "event": "subPageViewed",//e166
4144            "subPage": {
4145                "subPageID": subPageID, //v105
4146                "spClicked": clicked
4147            }
4148        });
4149    }
4150
4151
4152    $(document).ready(function () {
4153        // prevents browser from automatically restoring the scroll position when navigating back
4154        if ('scrollRestoration' in history) {
4155            history.scrollRestoration = 'manual';
4156        }
4157
4158        let maskSelectors = [{
4159            mask: "#slantMask",
4160            container: '.cmp-hrztimelineslide .cmp-hrztimelineslide__background[data-vg-slide-mask="slant"]',
4161            transformTemplate: (containerHeight) => `scale(${containerHeight / 1080})`
4162
4163        }, {
4164            mask: "#endSlideMask",
4165            container: '.htl-exit-mask-layer',
4166            transformTemplate: (containerHeight) => {
4167                //aspect ration of the part we want to hide
4168                let offset = containerHeight * .89;
4169				return `translateX(-${offset}px) scale(${containerHeight / 1080})`
4170            }
4171        }];
4172
4173        let ticking = false;
4174        function scaleSlantSvg() {            
4175            //Read before write
4176            let maskUpdates = maskSelectors.map((maskSelector) => {
4177                const container = document.querySelector(maskSelector.container);
4178                const clipPath = document.querySelector(maskSelector.mask);
4179
4180                if (!container || !clipPath) return;
4181
4182                return { clipPath: clipPath.querySelector('path:only-child') || clipPath, transform: maskSelector.transformTemplate(container.clientHeight) };
4183            });
4184
4185            //Update in one frame
4186            ticking = true;
4187            requestAnimationFrame(() => {
4188                maskUpdates.forEach((update) => {
4189                    if (update) {
4190                        update.clipPath.style.transform = update.transform;
4191                    }
4192                });
4193                ticking = false;
4194            });
4195        }
4196
4197        function initialize() {
4198            maskSelectors.forEach(maskSelector => {
4199                document
4200                    .querySelectorAll(maskSelector.container)
4201                    .forEach((e) => e.style.clipPath = `url(${maskSelector.mask})`);
4202            });
4203        }
4204
4205        window.addEventListener('resize', scaleSlantSvg);
4206
4207        initialize();
4208        scaleSlantSvg();
4209    });
4210
4211    //Handle tab/focus events to scroll to element
4212    $(document).ready(() => {
4213        const scrollContainer = document.querySelector('.cmp-horizontaltimeline__container');
4214        const carousel = document.querySelector('.htl-content');
4215        let isWindowFocus = false;
4216        let windowFocusTimeout;
4217
4218        window.addEventListener('focus', () => {
4219            isWindowFocus = true;
4220            clearTimeout(windowFocusTimeout);
4221            windowFocusTimeout = setTimeout(() => isWindowFocus = false, 0);
4222        });
4223
4224        //On all focused elements in the timeline
4225        $(scrollContainer).on('focus', '*', (e) => {
4226            if (window.innerWidth < 992 || isWindowFocus) return;
4227            e.stopPropagation();
4228            e.preventDefault();
4229            
4230            //Fix Safari early focus event
4231            setTimeout(() => focusElementInTimeline(e.target), 0);
4232        });
4233
4234        function focusElementInTimeline(t) {
4235            if (!isElementInViewport(t)) {
4236                const relativeLeft = getPositionRelativeToContainer(t, scrollContainer) + (t.offsetWidth / 2) - (window.innerWidth / 2);
4237                const percentWithinCarousel = relativeLeft / (carou
4237sel.scrollWidth - window.innerWidth);
4238                const scrollHeight = percentWithinCarousel * (scrollContainer.offsetHeight - window.innerHeight)
4239                const scrollTop = scrollHeight + scrollContainer.offsetTop;
4240                window.scrollTo({
4241                    top: scrollTop,
4242                    left: 0
4243                });
4244            }
4245        }
4246
4247        function getPositionRelativeToContainer(current) {
4248            let totalOffset = 0;
4249
4250            while (!!current && current !== scrollContainer && current !== document) {
4251                totalOffset += current.offsetLeft;
4252                current = current.offsetParent;
4253            }
4254
4255            return totalOffset;
4256        }
4257
4258        function isElementInViewport(t) {
4259            const rect = t.getBoundingClientRect();
4260            return (
4261                // rect.top >= 0 &&
4262                rect.left >= 0 &&
4263                //Ignore bottom since this is horizontal scrolling at 100vh
4264                // rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
4265                rect.right <= (window.innerWidth || document.documentElement.clientWidth)
4266            )
4267        }
4268    });
4269
4270    //Handle initial mobile animation
4271    $(document).ready(() => {
4272        //Prevent user from arriving at different height
4273        // if (window.scrollY > 0) {
4274        //     window.scrollY = window.scrollY + $('.htl-intro-video').outerHeight();
4275        // }
4276
4277        setTimeout(() => {
4278            $('.htl-intro').addClass('animate-slide-up')
4279        }, 3000);
4280    })
4281})();
4282$(document).ready(function(){
4283    var secondaryDownloadButton = $('.button-secondary-download');
4284    secondaryDownloadButton.text().trim().length > 5 ? secondaryDownloadButton.removeClass('button-padding-41').addClass('button-padding-26'):secondaryDownloadButton.removeClass('button-padding-26').addClass('button-padding-41');
4285    
4286    $(".btn-secondary-download-container a").on('click' ,  function(e){
4287        e.preventDefault();
4288        window.open($(this).data('ctapath'), '_blank');
4289    });
4290});
4291$(document).ready(function(){
4292var secondaryButton = $('.button-secondary');
4293secondaryButton.text().trim().length > 7 ? secondaryButton.removeClass('button-padding-41').addClass('button-padding-26'):secondaryButton.removeClass('button-padding-26').addClass('button-padding-41');
4294
4295});
4296$(document).ready(function(){
4297    var primaryDownloadButton = $('.button-primary-download');
4298    primaryDownloadButton.text().trim().length > 5 ? primaryDownloadButton.removeClass('button-padding-41').addClass('button-padding-26'):primaryDownloadButton.removeClass('button-padding-26').addClass('button-padding-41');
4299    
4300    $(".btn-primary-download-container a").on('click' ,  function(e){
4301        e.preventDefault();
4302        window.open($(this).data('ctapath'), '_blank');
4303    });
4304});
4305$(document).ready(function(){
4306var primaryButton = $('.button-primary');
4307primaryButton.text().trim().length > 7 ? primaryButton.removeClass('button-padding-41').addClass('button-padding-26'):primaryButton.removeClass('button-padding-26').addClass('button-padding-41');
4308
4309});
4310$(document).ready(function() {
4311
4312    let isSafari = navigator.vendor.match(/apple/i) &&
4313        !navigator.userAgent.match(/crios/i) &&
4314        !navigator.userAgent.match(/fxios/i) &&
4315        !navigator.userAgent.match(/Opera|OPT\//);
4316
4317    if (isSafari) {
4318        $('.btn-primary-container .cta-link-event').each(function() {
4319            let current = $(this);
4320            setTargetAttribute(current);
4321        });
4322        $('.cta-link-arrow-container .cta-link-event').each(function() {
4323            let current = $(this);
4324            setTargetAttribute(current);
4325        });
4326    }
4327
4328    function setTargetAttribute(current){
4329        let ctaPath = current.data('ctapath');
4330        if (ctaPath) {
4331            if (ctaPath.indexOf('/content/corporatesite/') === -1) {
4332                current.attr('target','_blank').attr('rel','external');
4333            }
4334        }
4335    }
4336
4337    $('.cta-link-event').on('click', function(e) {
4338        e.preventDefault();
4339        let ctaPath = $(this).data('ctapath');
4340        if (ctaPath) {
4341            let isPdfContent = ctaPath.endsWith('.pdf');
4342            let isCorpSiteContent = ctaPath.indexOf('/content/corporatesite/') !== -1;
4343            let ctaName = $(this).data('ctaname');
4344
4345            if (ctaName === "Subscribe") {
4346                ctaName = "Subscribe CTA Banner"
4347            }
4348
4349            if (!isPdfContent) {
4350                window.dataLayer = window.dataLayer || [];
4351                window.dataLayer.push({
4352                    "event": "callToAction", //e111
4353                    "callToAction": {
4354                        "location": $(this).data('ctalocation'), //v103
4355                        "ctaName": ctaName, //v104
4356                        "ctaType": $(this).data('ctatype') //v111
4357                    }
4358                });
4359            }
4360            if (isCorpSiteContent) {
4361                window.open(ctaPath, '_self');
4362            } else {
4363                window.open(ctaPath, '_blank');
4364            }
4365        }
4366    });
4367
4368});
4369$(document).ready(function () {
4370    var contentListPrimaryDownloadButton = $('.button-contentListPrimary-download-container');
4371    contentListPrimaryDownloadButton.text().trim().length > 5 ? contentListPrimaryDownloadButton.removeClass('button-padding-43').addClass('button-padding-38') : contentListPrimaryDownloadButton.removeClass('button-padding-38').addClass('button-padding-43');
4372
4373    $(".btn-contentListPrimary-download-container a").on('click', function (e) {
4374        e.preventDefault();
4375        let filePath = $(this).data('ctapath');
4376        let linkTarget = $(this).data('linktarget').toString();
4377        let linkTargetDefault = '_self' == linkTarget? '_self': '_blank';
4378        window.open(filePath, linkTargetDefault);
4379    });
4380});
4381$(document).ready(function(){
4382var primaryButton = $('.contentList-button-primary');
4383primaryButton.text().trim().length > 7 ? primaryButton.removeClass('button-padding-43').addClass('button-padding-38') : primaryButton.removeClass('button-padding-38').addClass('button-padding-43');
4384
4385});
4386$(document).ready(function() {
4387    let alertButton = $('.button-urgent,.button-non-urgent');
4388    alertButton.text().trim().length > 7 ? alertButton.removeClass('button-padding-41').addClass('button-padding-26') : alertButton.removeClass('button-padding-26').addClass('button-padding-41');
4389});
4390(() => {
4391  
4392})();
4393
4394$(document).ready(function() {
4395    if ($('.cmp-collapsibledrawer__container--topic').length > 0) {
4396		$('.cmp-collapsibledrawer__container--topic').find(".ffacts").removeClass().addClass( "col-12 col-md-6 col-lg-4 ff-padding");
4397    }
4398    if ($('.cmp-fastfacts').length > 0) {
4399		$('.cmp-fastfacts').find(".number-dark-grey.fast-facts-line.fast-facts-margin-top").addClass( "fast-facts-line-number-font");
4400        $('.cmp-fastfacts').find(".h3-heavy.fast-facts-line.fast-facts-margin-top").addClass( "fast-facts-line-text-font");
4401
4402        if ( $('.cmp-fastfacts').parent().closest('div').parent().closest('div').parent().closest('div').attr('class').indexOf('root responsivegrid') >= 0 ) {
4403			$('.cmp-fastfacts').addClass( "fast-facts-margin");
4404            $('.ffrow').addClass( "ff-width-87");
4405			$('.fastfactsTitleRow').addClass( "ff-width-87");
4406            $('.fastfacts-container').addClass( "ff-container-margin");
4407        }
4408    }
4409    $('.ffrow').each(function() {
4410        if ($(this).children().length == 2) {
4411			$(this).addClass('justify-content-center');
4412            $(this).prev('.fastfactsTitleRow').addClass('justify-content-center');
4413        } else if ($(this).children().length == 1) {
4414            $(this).removeClass('justify-content-center');
4415            $(this).prev('.fastfactsTitleRow').children('.ffactsheader').removeClass().addClass('ffactsheader col-12 col-md-5 ff-padding offset-md-2');
4416			$(this).children('.ffacts').addClass('offset-md-2');
4417        }
4418    });
4419
4420});
4421(function () {
4422    "use strict";
4423
4424    // dynamically adjust iframe height to ensure chart isn't cutoff on mobile,
4425    // doesn't have scrollbars, and doesn't have too much whitespace underneath on desktop
4426
4427    window.addEventListener('message', function(event) {
4428        if (typeof event.data['datawrapper-height'] !== 'undefined') {
4429            let iframes = document.querySelectorAll('iframe');
4430            for (let chartId in event.data['datawrapper-height']) {
4431                for (let i = 0; i < iframes.length; i++) {
4432                    if (iframes[i].contentWindow === event.source) {
4433                        let iframe = iframes[i];
4434                        iframe.style.height = event.data['datawrapper-height'][chartId] + 'px';
4435                        adjustContainerHeight(iframe);
4436                    }
4437                }
4438            }
4439        }
4440    });
4441
4442    // adjust height on window resize, if side-by-side chart exists
4443    window.addEventListener('resize', function() {
4444        let iframes = document.querySelectorAll('iframe[id^="datawrapper-chart"]');
4445        iframes.forEach(function(iframe) {
4446            adjustContainerHeight(iframe);
4447        });
4448    });
4449
4450    // adjust container height, if there is a side-by-side chart, so both charts are same height
4451    function adjustContainerHeight(iframe) {
4452        let sideBySideChart = getSideBySideChart(iframe);
4453        if (sideBySideChart) {
4454            let iframeHeight = parseInt(iframe.style.height, 10);
4455            let sideBySideHeight = parseInt(window.getComputedStyle(sideBySideChart).height, 10);
4456            let container = iframe.closest('.cmp-dwcharts__container');
4457            if (container) {
4458                container.style.height = Math.max(iframeHeight, sideBySideHeight) + 'px';
4459            }
4460        }
4461    }
4462
4463    // returns side-by-side chart, if it exists
4464    function getSideBySideChart(iframe) {
4465        if (!iframe) return;
4466        let parent = iframe.closest('.cmp-container');
4467        if (parent) {
4468            let grandParent = parent.parentElement?.closest('.cmp-container:not([id="' + parent.id + '"])');
4469            if (grandParent) {
4470                let otherChart = grandParent.querySelector('[id^="datawrapper-chart"]:not([id="' + iframe.id + '"])');
4471                if (otherChart) {
4472                    return otherChart;
4473                }
4474            }
4475        }
4476        return;
4477    }
4478
4479})();
4480
4481$(document).ready(function() {
4482    setDeepLinksPrettyTitles();
4483});
4484
4485const titles = {
4486    "amazonMusic": "Amazon Music",
4487    "applePodcasts": "Apple Podcasts",
4488    "iHeart": "iHeart",
4489    "spotify": "Spotify",
4490    "tuneIn": "TuneIn",
4491    "youtube": "YouTube",
4492    "reddit": "Reddit"
4493}
4494
4495function setDeepLinksPrettyTitles() {
4496    let deepLinks = document.getElementsByClassName("cmp-deepLinks__link");
4497    Array.from(deepLinks).forEach(function (deepLink) {
4498        let icon = deepLink.dataset.icon;
4499        let prettyIconName = titles[icon];
4500        deepLink.title = "Open " + prettyIconName + " in a new tab";
4501    });
4502}
4503"use strict";
4504
4505const contentListArray = document.querySelectorAll(".cmp-contentList");
4506
4507let contentListListElements = [];
4508let contentListFilteredList = [];
4509let contentListUnfilteredList = [];
4510let contentListSelectOptionsList = [];
4511let contentListInvestmentStewYearSelectOptionsList = [];
4512
4513let contentListInvestmentStewTopicSelected = false;
4514let contentListInvestmentStewYearSelected = false;
4515
4516let contentListFilterSelectors = {};
4517
4518let contentListPressroomFiltering = null;
4519let contentListInvestmentStewFiltering = null;
4520let contentListElement = null;
4521let contentListLoadMore = null;
4522
4523let contentListCurrentItems = 0;
4524let contentListShowingElements = 10;
4525let contentListNextToShow = 10;
4526
4527let contentListWCMMode = "";
4528
4529const clearOptions = function clearOptions(element) {
4530    let length = element.options.length;
4531    for (let i = length; i > 0; i--) {
4532        element.remove(i);
4533    }
4534};
4535
4536const allResults = function allResults() {
4537    contentListUnfilteredList.forEach((element) => {
4538        if (contentListPressroomFiltering) {
4539            if (element.querySelector('.cmp-contentListEntry__date')) {
4540                element.querySelector('.cmp-contentListEntry').style.display = "block";
4541                contentListElement.querySelector('.cmp-contentList__resultsEntries').appendChild(element);
4542            }
4543        } else if (element.querySelector('.cmp-contentListEntry__contentType')) {
4544            element.querySelector('.cmp-contentListEntry').style.display = "block";
4545            contentListElement.querySelector('.cmp-contentList__resultsEntries').appendChild(element);
4546        }
4547    });
4548};
4549
4550const addOptionsToSelect = function addOptionsToSelect(classOfOption, classOfSelect, array) {
4551    array.forEach((optionElement) => {
4552        let option = document.createElement("option");
4553        option.setAttribute("value", optionElement);
4554        let optionDisplayText = document.createTextNode(optionElement);
4555        option.appendChild(optionDisplayText);
4556        option.classList.add(classOfOption);
4557        contentListElement.querySelector(classOfSelect).appendChild(option);
4558    });
4559};
4560
4561const hideElements = function hideElements(listToHideElements) {
4562    if (contentListWCMMode === 'false') {
4563        let count = 1;
4564        listToHideElements.forEach(function (entry) {
4565            if (count <= contentListShowingElements) {
4566                entry.querySelector('.cmp-contentListEntry').style.display = "block";
4567            } else {
4568                entry.querySelector('.cmp-contentListEntry').style.display = "none";
4569            }
4570            count++;
4571        });
4572    }
4573};
4574
4575const displayNumberOfResults = function displayNumberOfResults(listOfElementsPresent) {
4576    if (contentListElement.querySelector('.cmp-contentList__results') != null) {
4577        if (listOfElementsPresent.length === 1) {
4578            contentListElement.querySelector('.cmp-contentList__results').textContent = listOfElementsPresent.length + " result";
4579        } else if (listOfElementsPresent.length > 1) {
4580            contentListElement.querySelector('.cmp-contentList__results').textContent = listOfElementsPresent.length + " results";
4581        } else if (listOfElementsPresent.length === 0) {
4582            contentListElement.querySelector('.cmp-contentList__results').textContent = "No results found.";
4583        }
4584    }
4585};
4586
4587const createFilterDropdown = function createFilterDropdown(array) {
4588    array.forEach((element) => {
4589        if (contentListPressroomFiltering != null) {
4590            if (element.querySelector('.cmp-contentListEntry__date') &&
4591                !contentListSelectOptionsList.includes(new Date(element.querySelector('.cmp-contentListEntry__date').getAttribute('data-result-date')).getFullYear())) {
4592                contentListSelectOptionsList.push(new Date(element.querySelector('.cmp-contentListEntry__date').getAttribute('data-result-date')).getFullYear());
4593                contentListSelectOptionsList.sort((oldest, newest) => {
4594                    return newest - oldest;
4595                });
4596            }
4597        } else if (contentListInvestmentStewFiltering != null) {
4598            if (element.querySelector('.cmp-contentListEntry__contentType') &&
4599                !contentListSelectOptionsList.includes(element.querySelector('.cmp-contentListEntry__contentType').getAttribute('data-result-contentType'))) {
4600                contentListSelectOptionsList.push(element.querySelector('.cmp-contentListEntry__contentType').getAttribute('data-result-contentType'));
4601                contentListSelectOptionsList.sort();
4602            }
4603            if (element.querySelector('.cmp-contentListEntry__date') &&
4604                !contentListInvestmentStewYearSelectOptionsList.includes(new Date(element.querySelector('.cmp-contentListEntry__date').getAttribute('data-result-date')).getFullYear())) {
4605                contentListInvestmentStewYearSelectOptionsList.push(new Date(element.querySelector('.cmp-contentListEntry__date').getAttribute('data-result-date')).getFullYear());
4606                contentListInvestmentStewYearSelectOptionsList.sort((oldest, newest) => {
4607                    return newest - oldest;
4608                });
4609            }
4610        } else if (element.querySelector('.cmp-contentListEntry__contentType') &&
4611            !contentListSelectOptionsList.includes(element.querySelector('.cmp-contentListEntry__contentType').getAttribute('data-result-contentType'))) {
4612            contentListSelectOptionsList.push(element.querySelector('.cmp-contentListEntry__contentType').getAttribute('data-result-contentType'));
4613            contentListSelectOptionsList.sort();
4614        }
4615    });
4616};
4617
4618/*******************************************************************************************************************************/
4619/************************************************ LOAD MORE ********************************************************************/
4620/*******************************************************************************************************************************/
4621const loadMoreResultsHandler = function loadMoreResultsHandler(arrayReferenced) {
4622    let articleToBeFocused = contentListCurrentItems;
4623    for (let i = contentListCurrentItems; i < contentListCurrentItems + contentListNextToShow; i++) {
4624        if (arrayReferenced[i] != null) {
4625            arrayReferenced[i].querySelector('.cmp-contentListEntry').style.display = 'block';
4626        }
4627    }
4628    contentListCurrentItems += contentListNextToShow;
4629    if (contentListArray.length === 1 && contentListCurrentItems >= arrayReferenced.length) {
4630        contentListLoadMore.style.display = 'none';
4631    }
4632    if (arrayReferenced[articleToBeFocused].querySelector('.cmp-contentListEntry__headlineLink') != null) {
4633        arrayReferenced[articleToBeFocused].querySelector('.cmp-contentListEntry__headlineLink').focus();
4634    }
4635};
4636
4637const showLoadMoreButton = function showLoadMoreButton(listToCheckForLoadMore) {
4638    if (listToCheckForLoadMore.length > contentListShowingElements) {
4639        contentListElement.querySelector('.cmp-contentList__loadM
4639oreButtonContainer').style.display = 'block';
4640    } else {
4641        contentListElement.querySelector('.cmp-contentList__loadMoreButtonContainer').style.display = 'none';
4642    }
4643};
4644
4645const loadMoreResultsOnClick = function loadMoreResultsOnClick() {
4646
4647    if (contentListLoadMore != null) {
4648        contentListLoadMore.addEventListener('click', (event) => {
4649            loadMoreResults();
4650        });
4651        contentListLoadMore.addEventListener('keypress', function (e) {
4652            if (e.key === 'Enter') {
4653                loadMoreResults();
4654            }
4655        });
4656    }
4657};
4658
4659const loadMoreResults = function loadMoreResults() {
4660
4661    let noOfContentLists = contentListArray.length;
4662    let selectedLoadmore;
4663    if (noOfContentLists > 1) {
4664        let currentContentList = $(event.target).closest(".cmp-contentList")[0];
4665        selectedLoadmore = currentContentList.querySelectorAll(".cmp-contentList__loadMoreButton");
4666        let selectedListItems = currentContentList.querySelectorAll('.cmp-contentList__resultsEntries .contentListEntry');
4667        let selectedVisibleItemsSize = selectedLoadmore[0].getAttribute('data-contentlistcurrentitems');
4668        if (selectedVisibleItemsSize) {
4669            contentListCurrentItems = parseInt(selectedVisibleItemsSize);
4670        } else {
4671            contentListCurrentItems = 10;
4672        }
4673        contentListFilteredList = selectedListItems;
4674    }
4675
4676    pushEventCTA();
4677    if (contentListFilteredList.length > 0) {
4678        loadMoreResultsHandler(contentListFilteredList);
4679    } else {
4680        loadMoreResultsHandler(contentListUnfilteredList);
4681    }
4682
4683    if (noOfContentLists > 1) {
4684        selectedLoadmore[0].setAttribute('data-contentlistcurrentitems', contentListCurrentItems);
4685        if (contentListCurrentItems >= contentListFilteredList.length) {
4686            $(event.target).closest(".cmp-contentList__loadMoreButtonContainer")[0].style.display = 'none';
4687        }
4688    }
4689}
4690
4691const pushEventCTA = function pushEventCTA() {
4692    window.dataLayer = window.dataLayer || [];
4693    window.dataLayer.push({
4694        "event": "callToAction", //e111
4695        "callToAction": {
4696            "location": contentListLoadMore.getAttribute('data-ctalocation'), //v103
4697            "ctaName": "Load more", //v104
4698            "ctaType": "primary button" //v111
4699        }
4700    });
4701}
4702/*******************************************************************************************************************************/
4703/**************************************** FILTER RESULTS ***********************************************************************/
4704/*******************************************************************************************************************************/
4705const resetResultsCount = function resetResultsCount(array) {
4706    contentListCurrentItems = contentListShowingElements;
4707    hideElements(array);
4708    displayNumberOfResults(array);
4709    showLoadMoreButton(array);
4710    setTimeout(() => {
4711        contentListElement.querySelector('.cmp-contentList__resultsEntries').style.height = contentListElement.querySelector('.cmp-contentList__resultsEntries').outerHeight + "px";
4712        contentListElement.querySelector('.cmp-contentList__resultsEntries').classList.toggle('slideUp');
4713    }, 500);
4714};
4715
4716const filteredResults = function filteredResult(arrayToFilter, filterSelected) {
4717    contentListFilteredList = [];
4718    contentListElement.querySelector('.cmp-contentList__resultsEntries').classList.toggle('slideUp');
4719    while (contentListElement.querySelector('.cmp-contentList__resultsEntries').firstChild) {
4720        contentListElement.querySelector('.cmp-contentList__resultsEntries').removeChild(contentListElement.querySelector('.cmp-contentList__resultsEntries').firstChild);
4721    }
4722    arrayToFilter.forEach((element) => {
4723        if (contentListPressroomFiltering &&
4724            parseInt(filterSelected) === new Date(element.querySelector('.cmp-contentListEntry__date').getAttribute('data-result-date')).getFullYear()) {
4725            contentListElement.querySelector('.cmp-contentList__resultsEntries').appendChild(element);
4726            contentListFilteredList.push(element);
4727        } else if (element.querySelector('.cmp-contentListEntry__contentType') &&
4728            filterSelected === element.querySelector('.cmp-contentListEntry__contentType').getAttribute('data-result-contentType')) {
4729            contentListElement.querySelector('.cmp-contentList__resultsEntries').appendChild(element);
4730            contentListFilteredList.push(element);
4731        }
4732    });
4733    resetResultsCount(contentListFilteredList);
4734};
4735
4736const filterResults = function filterResults(e) {
4737    if (e === "allFiltering") {
4738        contentListFilteredList = [];
4739        contentListElement.querySelector('.cmp-contentList__resultsEntries').classList.toggle('slideUp');
4740        while (contentListElement.querySelector('.cmp-contentList__resultsEntries').firstChild) {
4741            contentListElement.querySelector('.cmp-contentList__resultsEntries').removeChild(contentListElement.querySelector('.cmp-contentList__resultsEntries').firstChild);
4742        }
4743        contentListUnfilteredList = contentListListElements;
4744        sessionStorage.setItem("previousResults", contentListUnfilteredList);
4745        allResults();
4746        resetResultsCount(contentListUnfilteredList);
4747    } else {
4748        filteredResults(contentListUnfilteredList, e);
4749    }
4750};
4751
4752/*******************************************************************************************************************************/
4753/*********************************************** FILTERS ***********************************************************************/
4754/*******************************************************************************************************************************/
4755const filterFunctionality = function filterFunctionality() {
4756    if (contentListElement.querySelector('.cmp-contentList__filterOptions') != null) {
4757        contentListElement.querySelector('.cmp-contentList__filterOptions').addEventListener('change', (e) => {
4758            filterResults(e.target.value);
4759        });
4760    }
4761};
4762
4763const filterPressroomFunctionality = function filterPressroomFunctionality() {
4764    if (contentListElement.querySelector('.cmp-contentList__filterPressroomOptions') != null) {
4765        contentListElement.querySelector('.cmp-contentList__filterPressroomOptions').addEventListener('change', (e) => {
4766            filterResults(e.target.value);
4767        });
4768    }
4769};
4770
4771
4772const filterResultsInvestmentStew = function filterResultsInvestmentStew() {
4773    contentListFilteredList = [];
4774    contentListElement.querySelector('.cmp-contentList__resultsEntries').classList.toggle('slideUp');
4775    while (contentListElement.querySelector('.cmp-contentList__resultsEntries').firstChild) {
4776        contentListElement.querySelector('.cmp-contentList__resultsEntries').removeChild(contentListElement.querySelector('.cmp-contentList__resultsEntries').firstChild);
4777    }
4778    contentListFilteredList = contentListUnfilteredList.filter(element => {
4779        if (contentListInvestmentStewTopicSelected && !contentListInvestmentStewYearSelected) {
4780            return contentListFilterSelectors.topicSelected === element.querySelector('.cmp-contentListEntry__contentType').getAttribute('data-result-contentType');
4781        } else if (contentListInvestmentStewTopicSelected && contentListInvestmentStewYearSelected) {
4782            return contentListFilterSelectors.topicSelected === element.querySelector('.cmp-contentListEntry__contentType').getAttribute('data-result-contentType') &&
4783                contentListFilterSelectors.yearSelected === new Date(element.querySelector('.cmp-contentListEntry__date').getAttribute('data-result-date')).getFullYear();
4784        } else if (!contentListInvestmentStewTopicSelected && contentListInvestmentStewYearSelected) {
4785            return contentListFilterSelectors.yearSelected === new Date(element.querySelector('.cmp-contentListEntry__date').getAttribute('data-result-date')).getFullYear();
4786        } else if (!contentListInvestmentStewTopicSelected && !contentListInvestmentStewYearSelected) {
4787            return true;
4788        }
4789    });
4790    contentListFilteredList.forEach(element => {
4791        contentListElement.querySelector('.cmp-contentList__resultsEntries').appendChild(element);
4792    });
4793    contentListCurrentItems = contentListShowingElements;
4794    hideElements(contentListFilteredList);
4795    displayNumberOfResults(contentListFilteredList);
4796    showLoadMoreButton(contentListFilteredList);
4797    setTimeout(() => {
4798        contentListElement.querySelector('.cmp-contentList__resultsEntries').style.height = contentListElement.querySelector('.cmp-contentList__resultsEntries').outerHeight + "px";
4799        contentListElement.querySelector('.cmp-contentList__resultsEntries').classList.toggle('slideUp');
4800    }, 500);
4801};
4802
4803const filterInvestmentStewTopicFunctionality = function filterInvestmentStewTopicFunctionality() {
4804    if (contentListElement.querySelector('.cmp-contentList__filterInvestmentStewTopicOptions ') != null) {
4805        contentListElement.querySelector('.cmp-contentList__filterInvestmentStewTopicOptions ').addEventListener('change', (e) => {
4806            contentListFilterSelectors.topicSelected = e.target.value;
4807            if (!contentListInvestmentStewYearSelected) {
4808                contentListFilterSelectors.firstSelected = "topic-" + e.target.value;
4809            }
4810            contentListInvestmentStewTopicSelected = true;
4811            if (e.target.value === "allFiltering") {
4812                delete contentListFilterSelectors['topicSelected'];
4813                contentListInvestmentStewTopicSelected = false;
4814            }
4815            filterResultsInvestmentStew();
4816        });
4817    }
4818};
4819
4820const filterInvestmentStewYearFunctionality = function filterInvestmentStewYearFunctionality() {
4821    if (contentListElement.querySelector('.cmp-contentList__filterInvestmentStewYearOptions') != null) {
4822        contentListElement.querySelector('.cmp-contentList__filterInvestmentStewYearOptions').addEventListener('change', (e) => {
4823            contentListFilterSelectors.yearSelected = parseInt(e.target.value);
4824            if (!contentListInvestmentStewTopicSelected) {
4825                contentListFilterSelectors.firstSelected = "year-" + parseInt(e.target.value);
4826            }
4827            contentListInvestmentStewYearSelected = true;
4828            if (e.target.value === "allFiltering") {
4829                delete contentListFilterSelectors['yearSelected'];
4830                contentListInvestmentStewYearSelected = false;
4831            }
4832            filterResultsInvestmentStew();
4833        });
4834    }
4835};
4836/*******************************************************************************************************************************/
4837/************************************************ SORT *************************************************************************/
4838/*******************************************************************************************************************************/
4839const sortContent = function sortContent(arrayToSort, sortOrder) {
4840    contentListCurrentItems = contentListShowingElements;
4841    contentListElement.querySelector('.cmp-contentList__resultsEntries').classList.toggle('slideUp');
4842    while (contentListElement.querySelector('.cmp-contentList__resultsEntries').firstChild) {
4843        contentListElement.querySelector('.cmp-contentList__resultsEntries').removeChild(contentListElement.querySelector('.cmp-contentList__resultsEntries').firstChild);
4844    }
4845    if (sortOrder === "newest") {
4846        arrayToSort.sort(function (a, b) {
4847            let dateFirst = new Date(a.querySelector('.cmp-contentListEntry__date').getAttribute('data-result-date')).getTime();
4848            let dateSecond = new Date(b.querySelector('.cmp-contentListEntry__date').getAttribute('data-result-date')).getTime();
4849            return dateSecond - dateFirst;
4850        });
4851    } else if (sortOrder === "oldest") {
4852        arrayToSort.sort(function (a, b) {
4853            let dateFirst = new Date(a.querySelector('.cmp-contentListEntry__date').getAttribute('data-result-date')).getTime();
4854            let dateSecond = new Date(b.querySelector('.cmp-contentListEntry__date').getAttribute('data-result-date')).getTime();
4855            return dateFirst - dateSecond;
4856        });
4857    }
4858    arrayToSort.forEach((element) => {
4859        element.querySelector('.cmp-contentListEntry').style.display = "block";
4860        contentListElement.querySelector('.cmp-contentList__resultsEntries').appendChild(element);
4861    });
4862    hideElements(arrayToSort);
4863    showLoadMoreButton(arrayToSort);
4864    setTimeout(() => {
4865        contentListElement.querySelector('.cmp-contentList__resultsEntries').style.height = contentListElement.querySelector('.cmp-contentList__resultsEntries').outerHeight + +"px";
4866        contentListElement.querySelector('.cmp-contentList__resultsEntries').classList.toggle('slideUp');
4867    }, 500);
4868};
4869
4870const sortFunctionality = function sortFunctionality() {
4871    if (contentListElement.querySelector('.cmp-contentList__sortOptions') != null) {
4872        contentListElement.querySelector('.cmp-contentList__sortOptions').addEventListener('change', (e) => {
4873            let updatedOrder;
4874            if (contentListFilteredList.length > 0) {
4875                updatedOrder = contentListFilteredList;
4876                sortContent(updatedOrder, e.target.value);
4877            } else {
4878                updatedOrder = contentListUnfilteredList;
4879                sortContent(updatedOrder, e.target.value);
4880            }
4881        });
4882    }
4883};
4884
4885if (contentListArray != null) {
4886    contentListArray.forEach((contentListEntry) => {
4887        //initialize variables
4888        contentListElement = contentListEntry;
4889        contentListListElements = [...contentListElement.querySelectorAll('.cmp-contentList__resultsEntries .contentListEntry')];
4890        contentListUnfilteredList = contentListListElements;
4891        contentListPressroomFiltering = contentListElement.querySelector('.cmp-contentList__filteringPressroomContainer');
4892        contentListInvestmentStewFiltering = contentListElement.querySelector('.cmp-contentList__filteringSortingInvestmentStewContainer');
4893
4894        // Get configured items per page (default to 10 for backward compatibility)
4895        let resultsEntryElement = contentListElement.querySelector('.cmp-contentList__resultsEntries');
4896        if (resultsEntryElement != null) {
4897            let itemsPerPage = resultsEntryElement.getAttribute('data-items-per-page');
4898            contentListShowingElements = itemsPerPage ? parseInt(itemsPerPage) : 10;
4899            contentListNextToShow = contentListShowingElements;
4900        }
4901
4902        // order the results by the newest date
4903        contentListUnfilteredList.sort(function (a, b) {
4904            let dateFirst = new Date(a.querySelector('.cmp-contentListEntry__date').getAttribute('data-result-date')).getTime();
4905            let dateSecond = new Date(b.querySelector('.cmp-contentListEntry__date').getAttribute('data-result-date')).getTime();
4906            return dateSecond - dateFirst;
4907        });
4908        allResults();
4909        //create the filtering by topic dropdown and sorting it alphabetically
4910        createFilterDropdown(contentListUnfilteredList);
4911        let resultsEntry = contentListElement.querySelector('.cmp-contentList__resultsEntries');
4912        if (resultsEntry != null) {
4913            contentListWCMMode = resultsEntry.getAttribute('data-wcmMode-edit');
4914            contentListLoadMore = contentListElement.querySelector('.cmp-contentList__loadM
4914oreButtonContainer');
4915            contentListCurrentItems = contentListShowingElements;
4916            loadMoreResultsOnClick();
4917            //hide all the elements except the first 10
4918            hideElements(contentListUnfilteredList);
4919            displayNumberOfResults(contentListUnfilteredList);
4920            showLoadMoreButton(contentListUnfilteredList);
4921            if (contentListElement.querySelector('.cmp-contentList__filteringSortingContainer') != null) {
4922                addOptionsToSelect('cmp-contentList__filterOptions-option', '.cmp-contentList__filterOptions', contentListSelectOptionsList);
4923                contentListElement.querySelector('.cmp-contentList__filterOptionsContainer').classList.remove('cmp-contentList__hideElements');
4924                contentListElement.querySelector('.cmp-contentList__sortOptionsContainer').classList.remove('cmp-contentList__hideElements');
4925            }
4926            //Pressroom
4927            if (contentListElement.querySelector('.cmp-contentList__filteringPressroomContainer') != null) {
4928                addOptionsToSelect('cmp-contentList__filterPressroomOptions-option', '.cmp-contentList__filterPressroomOptions', contentListSelectOptionsList);
4929                contentListElement.querySelector('.cmp-contentList__filterPressroomOptionsContainer').classList.remove('cmp-contentList__hideElements');
4930            }
4931            //Investment stewardship filtering
4932            if (contentListElement.querySelector('.cmp-contentList__filteringSortingInvestmentStewContainer') != null) {
4933                addOptionsToSelect('.cmp-contentList__filterInvestmentStewTopicOptions-option', '.cmp-contentList__filterInvestmentStewTopicOptions', contentListSelectOptionsList);
4934                addOptionsToSelect('.cmp-contentList__filterInvestmentStewYearOptions-option', '.cmp-contentList__filterInvestmentStewYearOptions', contentListInvestmentStewYearSelectOptionsList);
4935                contentListElement.querySelector('.cmp-contentList__filterOptionsYearContainer').classList.remove('cmp-contentList__hideElements');
4936                contentListElement.querySelector('.cmp-contentList__filterOptionsTopicContainer').classList.remove('cmp-contentList__hideElements');
4937            }
4938            if (contentListElement.querySelector('.cmp-contentList__resultsContainer') != null) {
4939                contentListElement.querySelector('.cmp-contentList__resultsContainer').classList.remove('cmp-contentList__hideElements');
4940            }
4941            contentListElement.querySelector('.cmp-contentList__resultsEntries').classList.remove('cmp-contentList__hideElements');
4942            contentListElement.querySelector('.cmp-contentList__loadMoreButtonContainer').classList.remove('cmp-contentList__hideElements');
4943            /*********************************************** FILTER *********************************************/
4944            filterFunctionality();
4945            /*********************************************** FILTER PRESSROOM*********************************************/
4946            filterPressroomFunctionality();
4947            /*********************************************** FILTER Investment Stew***************************************/
4948            filterInvestmentStewTopicFunctionality();
4949            filterInvestmentStewYearFunctionality();
4950            /*************************************** SORT **********************************************************/
4951            sortFunctionality();
4952        }
4953    });
4954}
4955
4956const reapplySelector = function reapplyFiltering(selector) {
4957    if (document.querySelector(selector).value != 'allFiltering' && document.querySelector(selector) != null) {
4958        contentListFilteredList = [...contentListElement.querySelectorAll('.cmp-contentList__resultsEntries .contentListEntry')];
4959        while (contentListElement.querySelector('.cmp-contentList__resultsEntries').firstChild) {
4960            contentListElement.querySelector('.cmp-contentList__resultsEntries').removeChild(contentListElement.querySelector('.cmp-contentList__resultsEntries').firstChild);
4961        }
4962        filteredResults(contentListFilteredList, document.querySelector(selector).value);
4963    }
4964}
4965
4966const reapplyInvestmentStewardshipFilters = function reapplyInvestStewFilter() {
4967    contentListUnfilteredList = [...contentListElement.querySelectorAll('.cmp-contentList__resultsEntries .contentListEntry')];
4968    if (document.querySelector('.cmp-contentList__filterInvestmentStewTopicOptions').value != 'allFiltering' && document.querySelector('.cmp-contentList__filterInvestmentStewYearOptions').value == 'allFiltering') {
4969        contentListInvestmentStewTopicSelected = true;
4970        contentListFilterSelectors.topicSelected = document.querySelector('.cmp-contentList__filterInvestmentStewTopicOptions').value;
4971        filterResultsInvestmentStew();
4972    }
4973    if (document.querySelector('.cmp-contentList__filterInvestmentStewYearOptions').value != 'allFiltering' && document.querySelector('.cmp-contentList__filterInvestmentStewTopicOptions').value == 'allFiltering') {
4974        contentListInvestmentStewYearSelected = true;
4975        contentListFilterSelectors.yearSelected = parseInt(document.querySelector('.cmp-contentList__filterInvestmentStewYearOptions').value);
4976        filterResultsInvestmentStew();
4977    }
4978
4979    if (document.querySelector('.cmp-contentList__filterInvestmentStewTopicOptions').value != 'allFiltering' &&
4980        document.querySelector('.cmp-contentList__filterInvestmentStewYearOptions').value != 'allFiltering') {
4981        contentListInvestmentStewTopicSelected = true;
4982        contentListFilterSelectors.topicSelected = document.querySelector('.cmp-contentList__filterInvestmentStewTopicOptions').value;
4983        contentListInvestmentStewYearSelected = true;
4984        contentListFilterSelectors.yearSelected = parseInt(document.querySelector('.cmp-contentList__filterInvestmentStewYearOptions').value);
4985        filterResultsInvestmentStew();
4986    }
4987}
4988
4989$(document).ready(() => {
4990    if (document.querySelector('.cmp-contentList__filterOptions') != null) {
4991        reapplySelector('.cmp-contentList__filterOptions');
4992    }
4993    if (document.querySelector('.cmp-contentList__filterPressroomOptions') != null) {
4994        reapplySelector('.cmp-contentList__filterPressroomOptions');
4995    }
4996
4997    if (document.querySelector('.cmp-contentList__filterInvestmentStewTopicOptions') != null && document.querySelector('.cmp-contentList__filterInvestmentStewYearOptions') != null) {
4998        reapplyInvestmentStewardshipFilters();
4999    }
5000});
5001
5002
5003
5004(function () {
5005    "use strict";
5006
5007    var showHideElements = {};
5008
5009    showHideElements.contactsPosition = resource.properties["contactModuleSectionTitle"] == null ? "col-sm-12 col-md-5 offset-md-6" : "col-sm-12 col-md-5 offset-md-1";
5010
5011    if(resource.properties["stackContacts"] == 'true') {
5012		showHideElements.stackContacts = "col-12 col-sm-6 col-md-12 col-lg-6 contacts-two-col-padding";
5013    }else if(resource.properties["stackContacts"] == 'false') {
5014        showHideElements.stackContacts = "col-12 col-sm-12";
5015    }
5016    return showHideElements;
5017});
5018(function() {
5019    'use strict';
5020
5021    const PANEL_EXPANDED_CLASS = 'cmp-collapsibledrawer__panel--expanded';
5022    const PANEL_HIDDEN_CLASS = 'cmp-collapsibledrawer__panel--hidden';
5023    const PANEL_ICON_EXPANDED_CLASS = 'cmp-collapsibledrawer__panel-icon--expanded';
5024    const PANEL_ICON_HOVER_CLASS = 'cmp-collapsibledrawer__panel-icon--hover';
5025
5026    $(document).ready(function() {
5027        if ($('.cmp-collapsibledrawer').length > 0) {
5028            if ($('.cmp-collapsibledrawer').data('wcm-mode') != 'EDIT') {
5029                initializePanels();
5030                scrollToPanel(getUrlParameter('drawer'));
5031            }
5032        }
5033    });
5034
5035    $('.cmp-collapsibledrawer__button').hover(
5036        function handlerIn() {
5037            if (isPanelExpanded($(this))) {
5038                $(this).find('.cmp-collapsibledrawer__panel-icon').removeClass(PANEL_ICON_EXPANDED_CLASS);
5039                $(this).find('.cmp-collapsibledrawer__panel-icon').addClass(PANEL_ICON_HOVER_CLASS);
5040            } else {
5041                addPanelNumberHover($(this));
5042            }
5043        },
5044        function handlerOut() {
5045            $(this).find('.cmp-collapsibledrawer__panel-icon').removeClass(PANEL_ICON_HOVER_CLASS);
5046            if (isPanelExpanded($(this))) {
5047                $(this).find('.cmp-collapsibledrawer__panel-icon').addClass(PANEL_ICON_EXPANDED_CLASS);
5048            } else {
5049                $(this).find('.cmp-collapsibledrawer__panel-icon').removeClass(PANEL_ICON_EXPANDED_CLASS);
5050                removePanelNumberHover($(this));
5051            }
5052        }
5053    );
5054
5055    $('.cmp-collapsibledrawer__panel').on('transitionend', function() {
5056        if (isPanelCollapsed($(this))) {
5057            $(this).parent().prev().find('.cmp-collapsibledrawer__panel-icon').removeClass(PANEL_ICON_EXPANDED_CLASS);
5058        } else {
5059            $(this).parent().prev().find('.cmp-collapsibledrawer__panel-icon').addClass(PANEL_ICON_EXPANDED_CLASS);
5060        }
5061    });
5062
5063    $('.cmp-collapsibledrawer__button').click(function(clickEvent) {
5064        if (isPanelExpanded($(this))) {
5065            collapsePanel($(this));
5066        } else {
5067            scrollToButton($(this));
5068            expandPanel($(this));
5069        }
5070    });
5071
5072    function initializePanels() {
5073        // set height of each expanded panel for transition to work when collapsing drawer for first time
5074        document.querySelectorAll('.cmp-collapsibledrawer__panel--expanded').forEach((element) => {
5075            element.style.height = element.querySelector('.aem-Grid').offsetHeight + 'px';
5076            let observed = element.querySelector('.aem-Grid');
5077            let resizeObserver = new ResizeObserver(() => {
5078                element.style.height = observed.offsetHeight + 'px';
5079            });
5080            resizeObserver.observe(observed);
5081
5082        });
5083
5084        document.querySelectorAll('.cmp-collapsibledrawer__panel--hidden').forEach((element) => {
5085            element.hidden = true;
5086        });
5087
5088    }
5089
5090    function subpageViewedEventonPageLoad(document) {
5091        let panelTitleOnPageLoad = document.find('.cmp-collapsibledrawer__panel-title').html();
5092        window.dataLayer = window.dataLayer || [];
5093        window.dataLayer.push({
5094            "event": "subPageViewed", //e166
5095            "subPage": {
5096                "subPageID": panelTitleOnPageLoad, //v105
5097                "spClicked": "false"
5098            }
5099        });
5100    }
5101
5102    function addPanelNumberHover(collapsibleDrawerButton) {
5103        let panelNumber = collapsibleDrawerButton.find('.cmp-collapsibledrawer__panel-number');
5104        if (panelNumber.length > 0) {
5105            panelNumber.attr('class', panelNumber.attr('class') + '--hover');
5106        }
5107    }
5108
5109    function removePanelNumberHover(collapsibleDrawerButton) {
5110        let panelNumber = collapsibleDrawerButton.find('.cmp-collapsibledrawer__panel-number');
5111        if (panelNumber.length > 0) {
5112            panelNumber.attr('class', panelNumber.attr('class').replace('--hover', ''));
5113        }
5114    }
5115
5116    function addPanelNumberExpanded(collapsibleDrawerButton) {
5117        let panelNumber = collapsibleDrawerButton.find('.cmp-collapsibledrawer__panel-number');
5118        if (panelNumber.length > 0) {
5119            panelNumber.attr('class', panelNumber.attr('class').replace('--hover', '') + '--expanded');
5120        }
5121    }
5122
5123    function removePanelNumberExpanded(collapsibleDrawerButton) {
5124        let panelNumber = collapsibleDrawerButton.find('.cmp-collapsibledrawer__panel-number');
5125        if (panelNumber.length > 0) {
5126            panelNumber.attr('class', panelNumber.attr('class').replace('--expanded', ''));
5127        }
5128    }
5129
5130    function isPanelExpanded(collapsibleDrawerButton) {
5131        return collapsibleDrawerButton.parent().parent().next().css('height') != '0px';
5132    }
5133
5134    function isPanelCollapsed(collapsibleDrawerPanel) {
5135        return collapsibleDrawerPanel.css('height') == '0px';
5136    }
5137
5138    function collapsePanel(collapsibleDrawerButton) {
5139        let panel = collapsibleDrawerButton.parent().parent().next().children();
5140        panel.css('height', 0 + 'px');
5141        panel.removeClass(PANEL_EXPANDED_CLASS);
5142        panel.addClass(PANEL_HIDDEN_CLASS);
5143        collapsibleDrawerButton.find('.cmp-collapsibledrawer__panel-icon').removeClass(PANEL_ICON_EXPANDED_CLASS);
5144        removePanelNumberExpanded(collapsibleDrawerButton);
5145        setTimeout(() => {
5146            panel[0].hidden = true;
5147        }, 800);
5148    }
5149
5150    function expandPanel(collapsibleDrawerButton) {
5151        let panel = collapsibleDrawerButton.parent().parent().next().children();
5152        panel[0].hidden = false;
5153        let observed = panel[0].querySelector('.aem-Grid');
5154        let resizeObserver = new ResizeObserver(() => {
5155            panel[0].style.height = observed.offsetHeight + 'px';
5156        });
5157        resizeObserver.observe(observed);
5158        panel.addClass(PANEL_EXPANDED_CLASS);
5159        panel.removeClass(PANEL_HIDDEN_CLASS);
5160        collapsibleDrawerButton.find('.cmp-collapsibledrawer__panel-icon').addClass(PANEL_ICON_EXPANDED_CLASS);
5161        addPanelNumberExpanded(collapsibleDrawerButton);
5162        subpageViewedLog(collapsibleDrawerButton);
5163    }
5164
5165    function subpageViewedLog(collapsibleDrawerButton) {
5166        let panelTitleOnExpand = collapsibleDrawerButton.find('.cmp-collapsibledrawer__panel-title').html();
5167        window.dataLayer = window.dataLayer || [];
5168        window.dataLayer.push({
5169            "event": "subPageViewed", //e112
5170            "subPage": {
5171                "subPageID": panelTitleOnExpand, //v105
5172                "spClicked": "true"
5173            }
5174        });
5175    }
5176
5177    function scrollToButton(collapsibleDrawerButton) {
5178        let scrollToPosition = collapsibleDrawerButton.prev().offset().top;
5179        $('html,body').animate({
5180            scrollTop: scrollToPosition
5181        }, 500);
5182    }
5183
5184    function scrollToPanel(panelNumber) {
5185        if (panelNumber != null) {
5186            let panelNumberCount = 1;
5187            $('.cmp-collapsibledrawer__button').each(function() {
5188                if (isVisible($(this))) {
5189                    if (panelNumberCount == panelNumber) {
5190                        scrollToButton($(this));
5191                        if (!isPanelExpanded($(this))) {
5192                            expandPanel($(this));
5193                        }
5194                    }
5195                    panelNumberCount++;
5196                }
5197            });
5198        }
5199    }
5200
5201    function getUrlParameter(parameter) {
5202        let urlParams = window.location.search;
5203        if (urlParams != null) {
5204            let paramsList = urlParams.substring(1).split('&');
5205            for (let i = 0; i < paramsList.length; i++) {
5206                let param = paramsList[i].split('=');
5207                if (param[0] === parameter) {
5208                    return param[1];
5209                }
5210            }
5211        }
5212    }
5213
5214    function isVisible(element) {
5215        return element.css('height') !== '0px';
5216    }
5217})()
5218const PANEL_NUMBER_EXPANDED_CLASS = 'cmp-accordion__panel-number--expanded';
5219const PANEL_ICON_EXPANDED_CLASS = 'cmp-accordion__panel-icon--expanded';
5220const PANEL_ICON_HOVER_CLASS = 'cmp-accordion__panel-icon--hover';
5221
5222$('.cmp-accordion__button').click(function(clickEvent) {
5223    if (isPanelExpanded($(this))) {
5224        collapseAllPanels();
5225    } else {
5226        scrollToButton($(this), clickEvent);
5227        collapseAllPanels();
5228        expandPanel($(this));
5229    }
5230});
5231
5232$('.cmp-accordion__button').hover(
5233    function handlerIn() {
5234        if (isPanelExpanded($(this))) {
5235            $(this).find('.cmp-accordion__panel-icon').removeClass(PANEL_ICON_EXPANDED_CLASS);
5236            $(this).find('.cmp-accordion__panel-icon').addClass(PANEL_ICON_HOVER_CLASS);
5237        }
5238    },
5239    function handlerOut() {
5240        $(this).find('.cmp-accordion__panel-icon').removeClass(PANEL_ICON_HOVER_CLASS);
5241        if (isPanelExpanded($(this))) {
5242            $(this).find('.cmp-accordion__panel-icon').addClass(PANEL_ICON_EXPANDED_CLASS);
5243        } else {
5244            $(this).find('.cmp-accordion__panel-icon').removeClass(PANEL_ICON_EXPANDED_CLASS);
5245        }
5246    }
5247);
5248
5249$('.cmp-accordion__panel').on('transitionend', function() {
5250    if (isPanelCollapsed($(this))) {
5251        $(this).prev().find('.cmp-accordion__panel-icon').removeClass(PANEL_ICON_EXPANDED_CLASS);
5252    } else {
5253        $(this).prev().find('.cmp-accordion__panel-icon').addClass(PANEL_ICON_EXPANDED_CLASS);
5254    }
5255});
5256
5257function isPanelExpanded(accordionButton) {
5258    return accordionButton.parent().next().css('height') != '0px';
5259}
5260
5261function isPanelCollapsed(accordionPanel) {
5262    return accordionPanel.css('height') == '0px';
5263}
5264
5265function isAnyPanelExpanded() {
5266    return $('.cmp-accordion__panel').filter(
5267        function () {
5268            return (this.style.height != '0px') ? this : null;
5269        }).length > 0;
5270}
5271
5272function collapseAllPanels() {
5273    $('.cmp-accordion__panel').attr('style','height: 0px');
5274    $('.cmp-accordion__panel-number').removeClass(PANEL_NUMBER_EXPANDED_CLASS);
5275    $('.cmp-accordion__panel-icon').removeClass(PANEL_ICON_EXPANDED_CLASS);
5276}
5277
5278function expandPanel(accordionButton) {
5279    let panelHeight = accordionButton.parent().next()[0].scrollHeight + 'px';
5280    accordionButton.parent().next().css('height', panelHeight);
5281    accordionButton.find('.cmp-accordion__panel-number').addClass(PANEL_NUMBER_EXPANDED_CLASS);
5282    accordionButton.find('.cmp-accordion__panel-icon').addClass(PANEL_ICON_EXPANDED_CLASS);
5283}
5284
5285function scrollToButton(accordionButton, clickEvent) {
5286    let scrollToPosition = accordionButton.prev().offset().top;
5287    if (isAnyPanelExpanded()) {
5288        let clickPositionY = clickEvent.pageY;
5289        let expandedPanelPositionY = $('.cmp-accordion__panel--expanded')[0].offsetTop;
5290        if (clickPositionY > expandedPanelPositionY) {
5291            let openPanelHeight = $('.cmp-accordion__panel--expanded')[0].scrollHeight;
5292            scrollToPosition = scrollToPosition - openPanelHeight;
5293        }
5294    }
5295    $('html,body').animate({
5296        scrollTop: scrollToPosition
5297    }, 500);
5298}
5299(function () {
5300    'use strict';
5301
5302    let chartsMap = new Map();
5303
5304    $(document).ready(function() {
5305        renderCharts();
5306    })
5307
5308    function renderCharts() {
5309        $("[id^='chart-").not('[id$="legend"]').each(function(index) {
5310            let chart;
5311            let chartId = $(this).attr('id');
5312            let legendId = $(this).attr('id') + '-legend';
5313            let chartConfig = $(this).data('chartjson');
5314            let isLazyLoadEnabled = $(this).data('enable-lazy-load');
5315            let isDynamicDateAxisRangeEnabled = $(this).data('enable-dynamic-date-axis-range');
5316
5317            if (chart) {
5318                chart.dispose();
5319            }
5320            if (chartConfig != null) {
5321                am4core.addLicense("CH274465790");
5322                am4core.useTheme(am4themes_animated);
5323
5324                if (isLazyLoadEnabled) {
5325                    am4core.options.queue = true;
5326                    am4core.options.onlyShowOnViewport = true;
5327                } else {
5328                    am4core.options.queue = false;
5329                    am4core.options.onlyShowOnViewport = false;
5330                }
5331
5332                chart = am4core.createFromConfig(JSON.parse(JSON.stringify(chartConfig)), chartId);
5333
5334                if (isDynamicDateAxisRangeEnabled) {
5335                    addDynamicDateAxisRange(chart);
5336                }
5337                if (chart.legend != null) {
5338                    configureLegend(chart, legendId);
5339                }
5340                chartsMap.set(chartId, chart);
5341            }
5342        });
5343    }
5344
5345    function addDynamicDateAxisRange(chart) {
5346        let dateAxis = chart.xAxes.values[0];
5347
5348        let minRange = dateAxis.axisRanges.create();
5349        minRange.maxPosition = 1;
5350        minRange.minPosition = 0;
5351        minRange.label.horizontalCenter = "left"
5352        minRange.label.paddingLeft = 0;
5353
5354        let maxRange = dateAxis.axisRanges.create();
5355        maxRange.maxPosition = 1;
5356        maxRange.minPosition = 0;
5357        maxRange.label.horizontalCenter = "right"
5358        maxRange.label.paddingRight = 0;
5359
5360        dateAxis.events.on("inited",updateAxisRanges);
5361        dateAxis.events.on("startendchanged", updateAxisRanges);
5362
5363        function updateAxisRanges() {
5364            let labelFormat = "MMM yyyy";
5365
5366            minRange.value = dateAxis.min + dateAxis.start * (dateAxis.max - dateAxis.min);
5367            minRange.label.text = dateAxis.dateFormatter.format(minRange.value, labelFormat);
5368
5369            maxRange.value = dateAxis.min + dateAxis.end * (dateAxis.max - dateAxis.min - 1000);
5370            maxRange.label.text = dateAxis.dateFormatter.format(maxRange.value, labelFormat);
5371        }
5372    }
5373
5374    function addLegendCheckmarks(chart) {
5375        let marker = chart.legend.markers.template;
5376        let checkbox = marker.createChild(am4core.Image);
5377        checkbox.width = marker.width - 6;
5378        checkbox.height = marker.height - 6;
5379        checkbox.dx = 3;
5380        checkbox.dy = 3;
5381        checkbox.href = "/etc.clientlibs/corporatesite/clientlibs/clientlib-site/resources/icons/button-icons/check.svg";
5382
5383        let checkboxActiveState = checkbox.states.create("active");
5384        checkboxActiveState.properties.opacity = 0;
5385    }
5386
5387    function configureLegend(chart, legendId) {
5388        addLegendCheckmarks(chart);
5389
5390        let legendContainer = am4core.create(legendId, am4core.Container);
5391        legendContainer.width = am4core.percent(100);
5392        legendContainer.height = am4core.percent(100);
5393        chart.legend.parent = legendContainer;
5394
5395        chart.events.on("datavalidated", resizeLegends);
5396        chart.events.on("maxsizechanged", resizeLegends);
5397        chart.legend.events.on("datavalidated", resizeLegends);
5398        chart.legend.events.on("maxsizechanged", resizeLegends);
5399
5400        function resizeLegends(event) {
5401            chartsMap.forEach((chart, chartId) => {
5402                let newHeight = chart.legend.contentHeight + 'px';
5403                $('#' + chartId + '-legend').css('height', newHeight);
5404            });
5405        }
5406    }
5407})();
5408const carousel = $('.cmp-corpCarousel-tile__carouselContainer');
5409const settings = {
5410    slidesToShow: 4,
5411    centerMode: true,
5412    centerPadding: 30,
5413    draggable: false,
5414    responsive: [
5415        {
5416            breakpoint: 1023,
5417            settings: {
5418                centerMode: true,
5419                centerPadding: 20,
5420                slidesToShow: 3,
5421            }
5422        },
5423        {
5424            breakpoint: 600,
5425            settings: "unslick"
5426        }
5427    ]
5428}
5429const sl = carousel.slick(settings);
5430
5431function loadCarousel() {
5432    carousel.not('.slick-initialized').slick(settings);
5433}
5434
5435$(document).ready(function() {
5436    loadCarousel();
5437});
5438
5439// Reinitialize slick after unslick breakpoint
5440$(window).on('resize', function() {
5441    if( $(window).width() > 600 &&  !sl.hasClass('slick-initialized')) {
5442        loadCarousel();
5443    }
5444});
5445(function() {
5446    'use strict';
5447
5448    const PLAY_ICON = 'cmp-callOut__playIcon';
5449    const PAUSE_ICON = 'cmp-callOut__pauseIcon';
5450
5451    $('.cmp-callOut__playButton').click(function() {
5452        let callOutImage = $(this).parent().siblings('.cmp-callOut-img');
5453        let staticImg = callOutImage.data('static-img-src');
5454        let animatedGif = callOutImage.data('animated-gif-src');
5455        $(this).toggleClass(PAUSE_ICON + ' ' + PLAY_ICON);
5456        if (callOutImage.attr('src') === staticImg) {
5457            callOutImage.attr('src', animatedGif);
5458            $(this).attr('aria-pressed', 'true');
5459        } else {
5460            callOutImage.attr('src', staticImg);
5461            $(this).attr('aria-pressed', 'false');
5462        }
5463    });
5464
5465    $('.cmp-callOut-img').click(function() {
5466        let staticImg = $(this).data('static-img-src');
5467        let animatedGif = $(this).data('animated-gif-src');
5468        let playButton = $(this).siblings().children();
5469        playButton.toggleClass(PAUSE_ICON + ' ' + PLAY_ICON);
5470        if ($(this).attr('src') === staticImg) {
5471            $(this).attr('src', animatedGif);
5472            playButton.attr('aria-pressed', 'true');
5473        } else {
5474            $(this).attr('src', staticImg);
5475            playButton.attr('aria-pressed', 'false');
5476        }
5477    });
5478
5479    $('[data-animated-gif-src]').mouseenter(function() {
5480        $(this).css('cursor','pointer');
5481        $(this).siblings().children().css('box-shadow','0 0 0 2px #FFFFFF, 0 0 0 4px #0074C9, 0 0 0 6px #FFFFFF');
5482    });
5483
5484    $('[data-animated-gif-src]').mouseleave(function() {
5485        $(this).siblings().children().css('box-shadow','none');
5486    });
5487})();
5488
5489
5490(function () {
5491    "use strict";
5492
5493    var showHideElements = {}; 
5494
5495    showHideElements.showLogo = resource.properties["showVanguardLogo"] == 'true' ?  "display: block" : "display: none";
5496    showHideElements.showCTA = resource.properties["showCTA"] == 'true' ?  "display: block" : "display: none";
5497
5498    if(resource.properties["showVanguardLogo"] == 'true') {
5499		showHideElements.showLogo = "display: block" ;
5500        showHideElements.brandPosition = "col-sm-4 offset-sm-2 offset-md-2";
5501    }else if(resource.properties["showVanguardLogo"] == 'false') {
5502		showHideElements.showLogo = "display: none";
5503		showHideElements.brandPosition = "col-sm-4"
5504    }
5505
5506    return showHideElements;
5507
5508});
5509(function() {
5510    'use strict';
5511    $("#subscriptionLinkAuthorsDisplayComponentOdd").on("click", () => {
5512        dataLayerUploadAuthorDisplay();
5513    });
5514
5515    $("#subscriptionLinkAuthorsDisplayComponentEven").on("click", () => {
5516        //Use to track the conversion of user
5517        dataLayerUploadAuthorDisplay();
5518    });
5519
5520
5521    $(".authorReferencePageLinkClass").on("click", (event) => {
5522        window.dataLayer = window.dataLayer || [];
5523
5524        const doc = event.currentTarget;
5525        let authorId = doc.id;
5526        authorId = authorId.substring(0, authorId.length-1) +" "+ (parseInt(authorId.substring((authorId.length-1)))+1)
5527
5528        window.dataLayer.push({
5529            "event": "callToAction", //e111
5530            "callToAction": {
5531                "location": $(document).find("title")[0].text, //v103
5532                "ctaName":  authorId +"-"+ doc.innerText, //v104
5533                "ctaType": "Text Link" //v111
5534            }
5535        });
5536    });
5537
5538    function dataLayerUploadAuthorDisplay() {
5539        window.dataLayer = window.dataLayer || [];
5540        window.dataLayer.push({
5541            "event": "callToAction", //e111
5542            "callToAction": {
5543                "location": $(document).find("title")[0].text, //v103
5544                "ctaName": "Subscribe now", //v104
5545                "ctaType": "Text Link" //v111
5546            }
5547        });
5548    }
5549})();
5550(() => {
5551
5552  $("#articleSubscriptionPrivacyPolicyLink").on("click", () => {
5553    //Use to track the conversion of user 
5554    window.dataLayer = window.dataLayer || [];
5555    window.dataLayer.push({
5556      "event" : "callToAction", //e111
5557      "callToAction": {
5558        "location": $(document).find("title")[0].text, //v103
5559        "ctaName": "Privacy Policy", //v104
5560        "ctaType": "Text Link" //v111
5561      }
5562    });
5563  });
5564
5565  $("#articleSubscriptionSubscribeButton").click(() => {
5566
5567    window.dataLayer = window.dataLayer || [];
5568    window.dataLayer.push({
5569      "event" : "callToAction", //e111
5570      "callToAction": {
5571        "location": $(document).find("title")[0].text, //v103
5572        "ctaName": 'Subscribe Footer', //v104
5573        "ctaType": 'custom button' //v111
5574      }
5575    });
5576
5577
5578    window.location.assign(`${origin}/content/corporatesite/us/en/corp/subscription-dashboard.html`);
5579  });
5580
5581})();
5582
5583$(document).ready(function(){
5584    $('.close-alert').click(function(){
5585        $(this).parent('.alert-title-row').parent('.alert-container').parent('.cmp-alert').parent('.alert').hide();
5586    });
5587
5588    let isAuthorMode = $('.alert-title-row').data('isauthor');
5589    let alertEndDate = $('.cmp-alert').data('alertenddate');
5590
5591    if (isAuthorMode === false) {
5592		let todayDate = new Date();
5593        let dd = String(todayDate. getDate()). padStart(2, '0');
5594        let mm = String(todayDate. getMonth() + 1). padStart(2, '0'); //January is 0!
5595        let yyyy = todayDate.getFullYear();
5596        todayDate = yyyy + '-' + mm + '-' + dd;
5597        if (todayDate > alertEndDate) {
5598            $('.cmp-alert').remove();
5599        }
5600    }
5601});
5602const fetchAlerts = function fetchAlerts(){
5603    try {
5604        $.ajax({
5605            url:'/api/corporatesite/alerts',
5606            method:'GET',
5607            dataType: 'json',
5608            success: function (data){
5609                if (data.status === 401 || !data || data.noResource) {
5610                    return;
5611                }
5612                let alertsComponent = document.querySelector('.alerts');
5613
5614                let alertExpired = data.isExpired;
5615                let existingAlert = alertsComponent.querySelector('.cmp-alerts');
5616
5617                // if the alert does not exist in the dom and it is not expired, create the alert component
5618                if (!existingAlert && !alertExpired){
5619                    createAlertHTML(data, alertsComponent);
5620                }
5621
5622                if (existingAlert && !alertExpired){
5623
5624                    //update alert component with new data
5625                    updateAlertComponent(existingAlert, data);
5626                }
5627
5628                // When the content author deletes the alert Component, remove alert from page
5629                if(existingAlert && (data.noResource || data.isExpired)) {
5630                    existingAlert.remove();
5631                }
5632            },
5633            error:function (xhr,status,error){
5634                console.debug("Error: " + error + "with status :" + status + "xhr : " + xhr);
5635                logClientError("WARN", "Alerts API fetch failed: " + status + " " + error, "dynamicAlerts.js");
5636            }
5637        });
5638    }catch (e){
5639        console.debug(e);
5640        logClientError("WARN", "Alerts fetch exception: " + (e && e.message ? e.message : String(e)), "dynamicAlerts.js");
5641    }
5642}
5643
5644const createActionContainer = function createActionContainer(ctaPath, ctaName, ctaArrowStyling) {
5645    let ctaLocation = document.title.split(' | ')[0].trim();
5646    return `<div class="cta-link-arrow-container">
5647                    <div class="row">
5648                        <div class="col-12">
5649                            <a href="${ctaPath}" class="${ctaArrowStyling}" data-ctaname="${ctaName}" data-ctalocation="${ctaLocation}" data-ctatype="Text Link" data-ctapath="${ctaPath}">
5650                                <span class="link-text">${ctaName}</span>
5651                                <i class="arrow p-ctalink-arrow-14"></i>
5652                            </a>
5653                        </div>
5654                    </div>
5655                </div>
5656           `;
5657}
5658
5659const createAlertHTML = function createAlertHTML(data, alertsComponent) {
5660    alertsComponent.innerHTML = `
5661        <div class="cmp-alerts" style="background-color: ${data.alertBackgroundColor};">
5662            <div class="container-fluid alert-container">
5663                <div class="row alert-title-row" data-isauthor="false">
5664                   <div class="col-11 col-md-10">
5665                       <p class="alert-title-copy" style="color: ${data.fontColor}">
5666                            ${data.alertHeadline}
5667                       </p>
5668                   </div>
5669                </div>
5670            </div>
5671        </div>
5672    `;
5673
5674    if (data.alertSubHeadline != null && data.alertSubHeadline !== '') {
5675        let alertSubHeadline = document.createElement('p');
5676        alertSubHeadline.className = 'alert-sub-copy';
5677        alertSubHeadline.innerHTML = data.alertSubHeadline;
5678        alertSubHeadline.style.color = data.fontColor;
5679        alertsComponent.querySelector('.alert-title-row .col-11').appendChild(alertSubHeadline);
5680    }
5681
5682    if(data.alertCtaToggle === 'true' ) {
5683        let actionContainer = document.createElement('div');
5684        actionContainer.className = 'alert-butn';
5685        actionContainer.innerHTML = createActionContainer(data.alertCtaPath, data.alertCtaLabel, data.ctaArrowStyling)
5686        alertsComponent.querySelector('.alert-title-row .col-11').appendChild(actionContainer);
5687    }
5688}
5689
5690const updateAlertComponent = function updateAlertComponent(existingAlert, data) {
5691    if(existingAlert.style.backgroundColor === data.alertBackgroun
5691dColor){
5692        existingAlert.style.backgroundColor = data.alertBackgroundColor;
5693    }
5694    let fontColor = existingAlert.getAttribute('data-fontColor') === data.fontColor ? existingAlert.getAttribute('data-fontColor') : data.fontColor;
5695    existingAlert.style.backgroundColor = data.alertBackgroundColor;
5696    let alertHeadline = existingAlert.querySelector('.alert-title-copy');
5697    if (alertHeadline && alertHeadline.textContent !== data.alertHeadline) {
5698        alertHeadline.textContent = data.alertHeadline;
5699        alertHeadline.style.color = fontColor;
5700    }
5701
5702    let alertSubHeadline = existingAlert.querySelector('.alert-sub-copy');
5703    if (alertSubHeadline && alertSubHeadline.textContent !== data.alertSubHeadline) {
5704        alertSubHeadline.textContent = data.alertSubHeadline;
5705        alertSubHeadline.style.color = fontColor;
5706    }
5707
5708    let actionContainer = existingAlert.querySelector('.alert-butn');
5709    if (data.alertCtaToggle === 'true') {
5710        if (!actionContainer) {
5711            actionContainer = document.createElement('div');
5712            actionContainer.className = 'alert-butn';
5713            existingAlert.querySelector('.alert-title-row .col-11').appendChild(actionContainer);
5714        }
5715        actionContainer.innerHTML = createActionContainer(data.alertCtaPath, data.alertCtaLabel, data.ctaArrowStyling);
5716    } else if(data.alertCtaToggle === 'false' && actionContainer) {
5717        actionContainer.remove();
5718    }
5719}
5720
5721
5722
5723$(document).ready(function () {
5724    if( document.querySelector(".alerts div:first-child").getAttribute("data-isauthor") === 'false'   ) {
5725        setInterval(function () {
5726            fetchAlerts();
5727        }, 5 * 60 * 1000); // Refresh every 5 minutes
5728    }
5729});
5730$(document).ready(function(){
5731    $('.cmp-text__phoneCall').on('click', function(event){
5732         event.preventDefault();
5733         if($(this).attr('data-phonenumber') != undefined){
5734             window.open("tel: " + $(this).attr('data-phonenumber'), '_self');
5735         }
5736     });
5737 });
5738$(document).ready(function () {
5739    $('.cmp-teaser--cardRegularWithBorder, .cmp-teaser--cardImageLeftWithBorder, .cmp-teaser--cardTeaserListWithBorder, .cmp-teaser__withoutActions').click((e) => {
5740        if (e.currentTarget.dataset.linkurl) {
5741            window.location.href = e.currentTarget.dataset.linkurl;
5742        }
5743    });
5744});
5745
5746document.addEventListener('keydown', (event) => {
5747    const focusedElement = document.activeElement;
5748    const isTeaserLink = focusedElement.classList.contains('cmp-teaser__withLink');
5749
5750    if (isTeaserLink && event.key === 'Enter') {
5751        const linkUrl = focusedElement.getAttribute('data-linkurl');
5752        if (linkUrl) {
5753            window.location.href = linkUrl;
5754        }
5755    }
5756});
5757function buildThresholdList() {
5758    let thresholds = [];
5759    let numSteps = 100;
5760    for (let i = 1.0; i <= numSteps; i++) {
5761        let ratio = i / numSteps;
5762        thresholds.push(ratio);
5763    }
5764    thresholds.push(0);
5765    return thresholds;
5766}
5767
5768$(document).ready(function () {
5769
5770    let optionsAnimation = {
5771        root: null,
5772        rootMargin: '0px',
5773        threshold: buildThresholdList()
5774
5775    };
5776
5777    let cardVisible = document.querySelector('#teaserFadeInAnimation');
5778    let oldValue = 0;
5779    let newValue = 0;
5780    let up = false;
5781    window.addEventListener('scroll', (e) => {
5782        newValue = window.pageYOffset;
5783        if (oldValue > newValue) {
5784            up = true;
5785        } else if (oldValue < newValue) {
5786            up = false;
5787        }
5788        oldValue = newValue;
5789    });
5790
5791    let reachedHundredScrollingDown = false;
5792    let reachedHundredScrollingUp = true;
5793    const observerForAnimation = new IntersectionObserver((entries) => {
5794        entries.forEach(entry => {
5795
5796            let parentTwoColumn = entry.target.closest('#twoColumnContainer');
5797            if (parentTwoColumn) {
5798                let teaser = parentTwoColumn.querySelector('#leftRailContainer .cmp-teaser--cardAnimatedFadeIn .cmp-teaser');
5799                if (!entry.isIntersecting && !up && reachedHundredScrollingDown) {
5800                    teaser.style.opacity = 1;
5801                    reachedHundredScrollingDown = false;
5802                } else if (!entry.isIntersecting && up && (reachedHundredScrollingDown || !reachedHundredScrollingUp)) {
5803                    teaser.style.opacity = 0;
5804                    reachedHundredScrollingUp = true;
5805                    reachedHundredScrollingDown = false;
5806                } else if (entry.isIntersecting && !up) {
5807                    if (entry.intersectionRatio >= 0 && entry.intersectionRatio < 1 && !reachedHundredScrollingDown) {
5808                        teaser.style.opacity = entry.intersectionRatio;
5809                    } else if (entry.intersectionRatio == 1) {
5810                        teaser.style.opacity = 1;
5811                        reachedHundredScrollingDown = true;
5812                    }
5813                } else if (entry.isIntersecting && up) {
5814                    if (entry.intersectionRatio >= 0 && entry.intersectionRatio < 1 && !reachedHundredScrollingUp) {
5815                        teaser.style.opacity = entry.intersectionRatio;
5816                    } else if (entry.intersectionRatio >= 0 && entry.intersectionRatio < 1 && reachedHundredScrollingDown) {
5817                        teaser.style.opacity = entry.intersectionRatio;
5818                        reachedHundredScrollingDown = true;
5819                    } else if (entry.intersectionRatio == 1) {
5820                        teaser.style.opacity = 1;
5821                        reachedHundredScrollingUp = false;
5822                    }
5823                }
5824            }
5825        });
5826    }, optionsAnimation);
5827    if (cardVisible) {
5828        observerForAnimation.observe(cardVisible);
5829    }
5830});
5831
5832const truncateToMaxCharacters = function truncateToMaxCharacters(target, maxCharacters) {
5833    let targetArray = document.querySelectorAll(target);
5834    if (targetArray.length > 0) {
5835        targetArray.forEach((targetItem) => {
5836            let stringValue = targetItem.textContent.toString().trim();
5837            if (stringValue.length > parseInt(maxCharacters)) {
5838                targetItem.textContent = stringValue.substring(0, parseInt(maxCharacters) - 1) + "...";
5839            }
5840        });
5841    }
5842}
5843
5844$(document).ready(function () {
5845    const teaserSelector = ".cmp-teaser--cardImageLeft > .cmp-teaser__withImage > .cmp-teaser__content"
5846    const titleSelector = teaserSelector + " .cmp-teaser__title";
5847    const titleSelectorWithLink = titleSelector + " > a.cmp-teaser__title-link";
5848    const descriptionSelector = teaserSelector + " .cmp-teaser__description";
5849
5850    truncateToMaxCharacters(titleSelectorWithLink, 67);
5851    truncateToMaxCharacters(titleSelector, 67);
5852
5853    truncateToMaxCharacters(descriptionSelector, 117);
5854});
5855
5856$(document).ready(function () {
5857    $('.cmp-teaser__action-link').on('click', function(e) {
5858        e.preventDefault();
5859        let ctaPath =$(this).attr('href');
5860        if (ctaPath) {
5861            let isPdfContent = ctaPath.endsWith('.pdf');
5862            let isCorpSiteContent = ctaPath.indexOf('/content/corporatesite/') !== -1;
5863
5864            if (!isPdfContent) {
5865                window.dataLayer = window.dataLayer || [];
5866                window.dataLayer.push({
5867                    "event": "callToAction", //e111
5868                    "callToAction": {
5869                        "location": $(document).find("title")[0].text, //v103
5870                        "ctaName": $(this).find('.cmp-button__text').text(), //v104
5871                        "ctaType": "Text Link" //v111
5872                    }
5873                });
5874            }
5875            if (isCorpSiteContent && !isPdfContent) {
5876                window.open(ctaPath, '_self');
5877            } else {
5878                window.open(ctaPath, '_blank');
5879            }
5880        }
5881    });
5882});
5883$(document).ready(function () {
5884    let teasers = document.getElementsByClassName("cmp-teaser");
5885    Array.from(teasers).forEach(function (teaser) {
5886        let altText = teaser.dataset.alttext;
5887        if (altText) {
5888            let teaserImage = teaser.querySelector('.cmp-image__image');
5889            teaserImage.alt = altText;
5890        }
5891    });
5892});
5893
5894var BUTTON_CLASSES = [
5895  { parent: 'cmp-teaser--primaryButton', button: 'button-primary' },
5896  { parent: 'cmp-teaser--secondaryButton', button: 'button-secondary' }
5897];
5898
5899document.addEventListener('DOMContentLoaded', function() {
5900  document.querySelectorAll('.cmp-teaser__action-link').forEach(function(link) {
5901    var parent = link.closest('.teaser');
5902    if (parent) {
5903      BUTTON_CLASSES.forEach(function(cfg) {
5904        if (parent.classList.contains(cfg.parent)) {
5905          link.classList.add(cfg.button);
5906        }
5907      });
5908    }
5909  });
5910});
5911$(document).ready(function(){
5912    if($('.cmp-tabs__tab').length>0){
5913        subpageViewedEventonPageLoad(($(this))); 
5914    	$('.cmp-tabs__tab').on('click', function() {
5915			subpageViewedEventOnClick($(this));
5916		});
5917    }
5918});
5919
5920function subpageViewedEventonPageLoad(document) {
5921    let tabTitleActiveOnPageLoad = document.find('.cmp-tabs__tab--active')[0].innerHTML;
5922    window.dataLayer = window.dataLayer || [];
5923    window.dataLayer.push({
5924        "event": "subPageViewed",//e166
5925        "subPage": {
5926            "subPageID": tabTitleActiveOnPageLoad, //v105
5927            "spClicked": "false" 
5928        }
5929    });
5930}
5931
5932function subpageViewedEventOnClick(tab) {
5933    let tabTitleActiveOnClickEvent = (tab)[0].innerHTML;
5934    window.dataLayer = window.dataLayer || [];
5935    window.dataLayer.push({
5936        "event": "subPageViewed", //e112
5937        "subPage": {
5938            "subPageID": tabTitleActiveOnClickEvent, //v105
5939            "spClicked": "true"
5940        }
5941    });
5942}
5943(function() {
5944    'use strict';
5945
5946    $(document).ready(function() {
5947        if ($('.cmp-tabs').length > 0) {
5948            initializeDropdown();
5949            addEventListenerForTabs();
5950            addEventListenerForDropdown();
5951        }
5952    });
5953
5954    function initializeDropdown() {
5955        let activeTab = document.querySelectorAll('.cmp-tabs__tab--active')[0].innerText;
5956        $("#cmp-tabs__dropdown option:contains('" + activeTab + "')").prop('selected', true);
5957    }
5958
5959    function addEventListenerForTabs() {
5960        document.querySelectorAll('.cmp-tabs__tab').forEach((tab) => {
5961            tab.addEventListener('click', function(event) {
5962                $('#cmp-tabs__dropdown option[selected]').prop('selected', false)
5963                $("#cmp-tabs__dropdown option:contains('" + tab.innerText + "')").prop('selected', true);
5964            });
5965        });
5966    }
5967
5968    function addEventListenerForDropdown() {
5969        const dropdown = document.getElementById('cmp-tabs__dropdown');
5970        if (!dropdown) {
5971            return;
5972        }
5973        dropdown.addEventListener('change', function(event) {
5974            $("ol.cmp-tabs__tablist li:contains('" + event.target.value + "')").trigger('click');
5975        });
5976    }
5977})();
5978(function() {
5979    'use strict';
5980
5981    $(document).ready(function() {
5982        if ($('.cmp-tabs').length > 0) {
5983            EnhancedTabs.init();
5984        }
5985    });
5986
5987    const EnhancedTabs = {
5988        activateTabFromHashInProgress: false,
5989
5990        init() {
5991            this.addTabClickListeners();
5992            this.addKeyboardNavigationListeners();
5993            this.addMouseEventListeners();
5994            this.addDropdownClickListeners();
5995            this.synchronizeTabsAndDropdown();
5996            this.addHashChangeListener();
5997            this.activateTabFromHash();
5998            this.pushSubpageViewedEventOnPageLoad();
5999        },
6000
6001        addTabClickListeners() {
6002            document.addEventListener('click', (e) => {
6003                const tab = e.target.closest('[data-cmp-hook-tabs="tab"]');
6004                if (!tab) return;
6005
6006                const tablist = tab.closest('[role="tablist"]');
6007                if (!tablist) return;
6008
6009                const isVertical = tablist.closest('.cmp-enhancedtabs--vertical');
6010                const activeClass = isVertical ? 'cmp-enhancedtabs__tab-vertical--active' : 'cmp-enhancedtabs__tab--active';
6011
6012                // Ensure only the clicked tab has the 'active' class
6013                tablist.querySelectorAll('[data-cmp-hook-tabs="tab"]').forEach((el) => {
6014                    el.classList.remove('cmp-enhancedtabs__tab--active', 'cmp-e
6014nhancedtabs__tab-vertical--active');
6015                    if (el === tab) {
6016                        el.classList.add(activeClass);
6017                    }
6018                });
6019
6020                // Push analytics event for tab click
6021                if (!EnhancedTabs.activateTabFromHashInProgress) {
6022                    EnhancedTabs.pushSubpageViewedEventOnClick(tab);
6023                }
6024            });
6025        },
6026
6027        addKeyboardNavigationListeners() {
6028            document.addEventListener('keydown', (e) => {
6029                this.setKeyboardMode();
6030
6031                const tab = e.target.closest('[data-cmp-hook-tabs="tab"]');
6032                if (!tab) return;
6033
6034                const tablist = tab.closest('[role="tablist"]');
6035                if (!tablist) return;
6036
6037                const isVertical = tablist.getAttribute('aria-orientation') === 'vertical';
6038                const key = e.key;
6039
6040                let isPrev, isNext;
6041                if (isVertical) {
6042                    isPrev = key === 'ArrowUp';
6043                    isNext = key === 'ArrowDown';
6044                } else {
6045                    isPrev = key === 'ArrowLeft' || key === 'ArrowUp';
6046                    isNext = key === 'ArrowRight' || key === 'ArrowDown';
6047                }
6048
6049                const isHome = key === 'Home';
6050                const isEnd = key === 'End';
6051
6052                if (!isPrev && !isNext && !isHome && !isEnd) return;
6053
6054                const tabs = Array.from(tablist.querySelectorAll('[data-cmp-hook-tabs="tab"]'));
6055                const currentIndex = tabs.indexOf(tab);
6056                if (currentIndex === -1) return;
6057
6058                e.preventDefault();
6059
6060                let nextIndex = currentIndex;
6061                if (isPrev) {
6062                    nextIndex = (currentIndex - 1 + tabs.length) % tabs.length;
6063                } else if (isNext) {
6064                    nextIndex = (currentIndex + 1) % tabs.length;
6065                } else if (isHome) {
6066                    nextIndex = 0;
6067                } else if (isEnd) {
6068                    nextIndex = tabs.length - 1;
6069                }
6070
6071                const nextTab = tabs[nextIndex];
6072                if (nextTab) {
6073                    nextTab.focus();
6074                    nextTab.click();
6075                }
6076            });
6077        },
6078
6079        addMouseEventListeners() {
6080            window.addEventListener("mousedown", (event) => this.setMouseMode(event), true);
6081            window.addEventListener("pointerdown", (event) => this.setMouseMode(event), true);
6082            window.addEventListener("touchstart", (event) => this.setMouseMode(event), true);
6083        },
6084
6085        addDropdownClickListeners() {
6086            // Select all dropdown buttons, to support multiple enhanced tabs components on a page
6087            $('[id$="__dropdown-button"]').each(function () {
6088                const $button = $(this);
6089                const tabsId = this.id.replace('__dropdown-button', '');
6090                const $listbox = $(`#${tabsId}__listbox`);
6091                if ($listbox.length === 0) return;
6092
6093                $button.on('click', function () {
6094                    const expanded = $button.attr('aria-expanded') === 'true';
6095                    $button.attr('aria-expanded', !expanded);
6096                    $listbox.prop('hidden', expanded);
6097                    if (!expanded) $listbox.focus();
6098                });
6099
6100                $listbox.on('click', '[role="option"]', function () {
6101                    $listbox.find('[role="option"]').attr('aria-selected', 'false');
6102                    $(this).attr('aria-selected', 'true');
6103                    $button.text($(this).text());
6104                    $button.attr('aria-expanded', 'false');
6105                    $listbox.prop('hidden', true);
6106                });
6107            });
6108        },
6109
6110        synchronizeTabsAndDropdown() {
6111            // Select all enhanced tabs components on the page
6112            $('.cmp-tabs').each(function () {
6113                const $tabCmp = $(this);
6114                const isVertical = $tabCmp.hasClass('cmp-enhancedtabs--vertical');
6115                const $tabs = $tabCmp.find('[data-cmp-hook-tabs="tab"]');
6116                const $listbox = $tabCmp.find('.cmp-enhancedtabs__listbox');
6117                const $button = $tabCmp.find('.cmp-enhancedtabs__dropdown-button');
6118                const $options = $listbox.find('[role="option"]');
6119
6120                if ($tabs.length !== $options.length) {
6121                    return;
6122                }
6123
6124                // Initial sync: determine active tab (fallback to first if none)
6125                const activeClass = isVertical ? '.cmp-enhancedtabs__tab-vertical--active' : '.cmp-e
6125nhancedtabs__tab--active';
6126                let $activeTab = $tabs.filter(activeClass);
6127                if ($activeTab.length === 0) {
6128                    $activeTab = $tabs.first();
6129                }
6130                const activeIndex = $tabs.index($activeTab);
6131
6132                // Update listbox options selection
6133                $options.attr('aria-selected', 'false');
6134                const $activeOption = $options.eq(activeIndex).attr('aria-selected', 'true');
6135
6136                // Update dropdown button text
6137                if ($button.length && $activeOption.length) {
6138                    $button.text($activeOption.text());
6139                }
6140
6141                // When a tab is selected, update the dropdown
6142                $tabs.off('.enhancedTabs').on('click.enhancedTabs', function (e) {
6143                    const index = $tabs.index(this);
6144                    $options.attr('aria-selected', 'false');
6145                    const $selectedOption = $options.eq(index).attr('aria-selected', 'true');
6146                    if ($button.length) {
6147                        $button.text($selectedOption.text());
6148                    }
6149                });
6150
6151                // When a listbox option is selected, update the selected tab
6152                $listbox.off('.enhancedTabs').on('click.enhancedTabs', '[role="option"]', function () {
6153                    const index = $options.index(this);
6154                    const $selectedTab = $tabs.eq(index);
6155                    if ($selectedTab.length) {
6156                        $selectedTab.trigger('click');
6157                    }
6158                });
6159            });
6160        },
6161
6162        activateTabFromHash() {
6163            const anchor = window.location.hash.substring(1);
6164            if (!anchor) return;
6165
6166            const $tab = $('#' + anchor);
6167            if ($tab.length) {
6168                const hasActiveClass = $tab.hasClass('cmp-enhancedtabs__tab--active') ||
6169                                      $tab.hasClass('cmp-enhancedtabs__tab-vertical--active');
6170                if (!hasActiveClass) {
6171                    EnhancedTabs.activateTabFromHashInProgress = true;
6172                    try {
6173                        $tab.trigger('click');
6174                    } finally {
6175                        EnhancedTabs.activateTabFromHashInProgress = false;
6176                    }
6177                }
6178                $tab.get(0).scrollIntoView({
6179                    behavior: 'smooth',
6180                    block: 'center'
6181                });
6182            }
6183        },
6184
6185        addHashChangeListener() {
6186            window.addEventListener("hashchange", this.activateTabFromHash);
6187        },
6188
6189        pushSubpageViewedEventOnPageLoad() {
6190            $('.cmp-enhancedtabs__tab--active, .cmp-enhancedtabs__tab-vertical--active').each(function () {
6191                let normalizedTabTitle = EnhancedTabs.normalizeString(this.innerHTML);
6192                window.dataLayer = window.dataLayer || [];
6193                window.dataLayer.push({
6194                    "event": "subPageViewed", //e166
6195                    "subPage": {
6196                        "subPageID": normalizedTabTitle, //v105
6197                        "spClicked": "false"
6198                    }
6199                });
6200            });
6201        },
6202
6203        pushSubpageViewedEventOnClick(tab) {
6204            let normalizedTabTitle = EnhancedTabs.normalizeString(tab.innerHTML);
6205            window.dataLayer = window.dataLayer || [];
6206            window.dataLayer.push({
6207                "event": "subPageViewed", //e112
6208                "subPage": {
6209                    "subPageID": normalizedTabTitle, //v105
6210                    "spClicked": "true"
6211                }
6212            });
6213        },
6214
6215        normalizeString(str) {
6216            return typeof str === 'string' ? str.replace(/&amp;/g, 'and') : '';
6217        },
6218
6219        setKeyboardMode() {
6220            document.body.setAttribute("data-focus-source", "keyboard");
6221        },
6222
6223        setMouseMode() {
6224            document.body.removeAttribute("data-focus-source");
6225        },
6226    };
6227})();
6228window.addEventListener('load', () => {
6229    document.querySelectorAll('.cmp-tabs').forEach(initIndicator);
6230});
6231
6232function initIndicator(root) {
6233    const isVertical = root.classList.contains('cmp-enhancedtabs--vertical');
6234    const tablist = root.querySelector('.cmp-tabs__tablist');
6235    const hr = isVertical
6236        ? root.querySelector('.cmp-enhancedtabs__hr-vertical')
6237        : root.querySelector('.cmp-enhancedtabs__hr');
6238
6239    if (!tablist || !hr) return;
6240
6241    const slideIndicatorToActiveTab = () => {
6242        const activeTab = isVertical
6243            ? (tablist.querySelector('.cmp-enhancedtabs__tab-vertical--active') ||
6244               tablist.querySelector('.cmp-enhancedtabs__tab-vertical[aria-selected="true"]'))
6245            : (tablist.querySelector('.cmp-enhancedtabs__tab--active') ||
6246               tablist.querySelector('.cmp-enhancedtabs__tab[aria-selected="true"]'));
6247
6248        if (!activeTab) return;
6249
6250        const tabRect = activeTab.getBoundingClientRect();
6251        const hrRect = hr.getBoundingClientRect();
6252
6253        if (isVertical) {
6254            const top = Math.round(tabRect.top - hrRect.top);
6255            const height = Math.round(tabRect.height);
6256            hr.style.setProperty('--indicator-top', `${top}px`);
6257            hr.style.setProperty('--indicator-height', `${height}px`);
6258        } else {
6259            const left = Math.round(tabRect.left - hrRect.left);
6260            const width = Math.round(tabRect.width);
6261            hr.style.setProperty('--indicator-left', `${left}px`);
6262            hr.style.setProperty('--indicator-width', `${width}px`);
6263        }
6264    };
6265
6266    // Initialize the sliding indicator (defer to the next frame to ensure tab activation/layout are settled)
6267    window.requestAnimationFrame(() => {
6268        window.requestAnimationFrame(slideIndicatorToActiveTab);
6269    });
6270
6271    // Add click and key listeners
6272    tablist.addEventListener('click', () => window.requestAnimationFrame(slideIndicatorToActiveTab));
6273    tablist.addEventListener('keydown', (e) => {
6274        const keysToWatch = isVertical
6275            ? ['Enter', 'ArrowUp', 'ArrowDown', 'Home', 'End']
6276            : ['Enter', 'ArrowLeft', 'ArrowRight', 'Home', 'End'];
6277
6278        if (keysToWatch.includes(e.key)) {
6279            window.requestAnimationFrame(slideIndicatorToActiveTab);
6280        }
6281    });
6282
6283    // Keep in sync with class/ARIA changes
6284    const mo = new MutationObserver(() => window.requestAnimationFrame(slideIndicatorToActiveTab));
6285    mo.observe(tablist, {
6286        subtree: true,
6287        attributes: true,
6288        attributeFilter: ['class', 'aria-selected']
6289    });
6290
6291    // Recalculate on resize
6292    window.addEventListener('resize', () => window.requestAnimationFrame(slideIndicatorToActiveTab));
6293}
6294
6295$(".cmp-container--contentRowItem .cmp-container .aem-Grid .cmp-container").hover(
6296    function () {
6297        $(this).addClass("applyHoverToSingle");
6298    }, function () {
6299        $(this).removeClass("applyHoverToSingle");
6300    }
6301);
6302
6303const updateStickyTop = function updateStickyTop(selectorClass) {
6304    if (document.querySelectorAll(selectorClass).length > 0) {
6305        document.querySelectorAll(selectorClass).forEach((element) => {
6306            element.style.top = element.firstElementChild.getAttribute('data-stickytop') + "px";
6307        });
6308	}
6309};
6310
6311$(document).ready( function() {
6312    "use strict";
6313    updateStickyTop(".cmp-container--stickyBeyondS");
6314    updateStickyTop(".cmp-container--stickyBeyondM");
6315    updateStickyTop(".cmp-container--stickyBeyondL");
6316});
6317
6318
6319
6320
6321/******/ (() => { // webpackBootstrap
6322/******/ 	"use strict";
6323/******/ 	var __webpack_modules__ = ({
6324
6325/***/ "./src/js/app.js":
6326/*!***********************!*\
6327  !*** ./src/js/app.js ***!
6328  \***********************/
6329/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
6330
6331  __webpack_require__.r(__webpack_exports__);
6332  /* harmony import */ var _observers__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./observers */ "./src/js/observers.js");
6333  /* harmony import */ var _movable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./movable */ "./src/js/movable.js");
6334  /* harmony import */ var _vg_carousel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./vg-carousel */ "./src/js/vg-carousel.js");
6335  /* harmony import */ var _vg_animate__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./vg-animate */ "./src/js/vg-animate.js");
6336  /* harmony import */ var _vg_parallaxv2__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./vg-parallaxv2 */ "./src/js/vg-parallaxv2.js");
6337  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
6338  
vendor: 9,050 bytes, line 6338
6338function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function define(t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == _typeof(h) && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function value(t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator["return"] && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(_typeof(e) + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function reset(e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function stop() { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function dispatchException(e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function abrupt(t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function complete(t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function finish(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, "catch": function _catch(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw Error("illegal catch attempt"); }, delegateYield: function delegateYield(e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; }
6339  function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
6340  function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
6341  // import jquery from 'jquery';
6342  // window.$ = window.jQuery = jquery;
6343  
6344  
6345  
6346  
6347  
6348  
6349  if (window.CQ && window.CQ.WCM) {
6350    window.disableOnResize = true;
6351    window.parallaxDisabled = true;
6352    window.animationDisabled = true;
6353  }
6354  var animateContainerSelector = '*[data-vg-slide]',
6355    carouselContainerSelector = '*[data-vg-carousel]',
6356    animateMutationObserver = new _observers__WEBPACK_IMPORTED_MODULE_0__.VgMutationObserver(animateContainerSelector, animationStart, {
6357      debug: false
6358    }),
6359    parallaxMutationObserver = new _observers__WEBPACK_IMPORTED_MODULE_0__.VgMutationObserver(carouselContainerSelector, carouselStart, {
6360      debug: false
6361    });
6362  var carouselFound = false,
6363    slidesFound = false;
6364  animateMutationObserver.init();
6365  parallaxMutationObserver.init();
6366  function animationStart(_x, _x2) {
6367    return _animationStart.apply(this, arguments);
6368  }
6369  function _animationStart() {
6370    _animationStart = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee(container, found) {
6371      var myAnimate;
6372      return _regeneratorRuntime().wrap(function _callee$(_context) {
6373        while (1) switch (_context.prev = _context.next) {
6374          case 0:
6375            if (!(!found || slidesFound)) {
6376              _context.next = 2;
6377              break;
6378            }
6379            return _context.abrupt("return");
6380          case 2:
6381            slidesFound = true;
6382            myAnimate = new _vg_animate__WEBPACK_IMPORTED_MODULE_3__.VgAnimate(_movable__WEBPACK_IMPORTED_MODULE_1__.Movable);
6383            _context.next = 6;
6384            return myAnimate.init({
6385              container: null,
6386              debug: false,
6387              inViewPercent: 0.4,
6388              containerElSelector: '*[data-vg-slide]',
6389              exitSlideContainerElSelector: '*[data-vg-slide].htl-exit',
6390              animatablesSelector: '*[data-vg-animatable]',
6391              disabled: window.animationDisabled || false
6392            });
6393          case 6:
6394          case "end":
6395            return _context.stop();
6396        }
6397      }, _callee);
6398    }));
6399    return _animationStart.apply(this, arguments);
6400  }
6401  function carouselStart(container, found) {
6402    if (!found || carouselFound) return;
6403    var carousels = document.querySelectorAll(carouselContainerSelector);
6404    if (!carousels.length) return false;
6405    carouselFound = true;
6406    carousels.forEach(function (carousel) {
6407      var myMovable = new _movable__WEBPACK_IMPORTED_MODULE_1__.Movable();
6408      var myParallax = new _vg_parallaxv2__WEBPACK_IMPORTED_MODULE_4__.VgParallax(myMovable);
6409      myParallax.init({
6410        debug: false,
6411        inViewPercent: 0.25,
6412        parallaxablesSelector: '*[data-vg-parallaxable]',
6413        disabled: window.parallaxDisabled || false
6414      });
6415      var myCarousel = new _vg_carousel__WEBPACK_IMPORTED_MODULE_2__.VgCarousel(myMovable, myParallax);
6416      myCarousel.init({
6417        debug: false,
6418        carousel: carousel,
6419        carouselSelector: carouselContainerSelector,
6420        carouselScrollContainerSelector: '*[data-vg-carousel-scroll-container]',
6421        slideSelector: '*[data-vg-slide]',
6422        disableOnResize: window.disableOnResize || false,
6423        nextSelector: '#htl-controls_next',
6424        previousSelector: '#htl-controls_previous',
6425        mobileAutoAdvanceDelay: 3000,
6426        mobileAutoAdvanceElement: '.htl-intro-text-container',
6427        introCtaSelector: '.htl-intro-cta',
6428        throttleMax: 4
6429      });
6430    });
6431  }
6432  
6433  /***/ }),
6434  
6435  
6435/***/ "./src/js/movable.js":
6436  /*!***************************!*\
6437    !*** ./src/js/movable.js ***!
6438    \***************************/
6439  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
6440  
6441  __webpack_require__.r(__webpack_exports__);
6442  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
6443  /* harmony export */   Movable: () => (/* binding */ Movable)
6444  /* harmony export */ });
6445  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
6446  function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
6447  function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
6448  function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
6449  function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
6450  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
6451  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
6452  var Movable = /*#__PURE__*/function () {
6453    function Movable() {
6454      _classCallCheck(this, Movable);
6455      _defineProperty(this, "disableWidth", void 0);
6456      _defineProperty(this, "debug", void 0);
6457      this.debug = false;
6458    }
6459  
6460    /**
6461     * 
6462     */
6463    return _createClass(Movable, [{
6464      key: "log",
6465      value: function log() {
6466        if (this.debug) {
6467          var _console;
6468          (_console = console).log.apply(_console, arguments);
6469        }
6470      }
6471    }, {
6472      key: "setDebug",
6473      value: function setDebug() {
6474        var debug = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
6475        this.debug = debug;
6476      }
6477    }, {
6478      key: "setDisableWidth",
6479      value: function setDisableWidth() {
6480        var width = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 992;
6481        this.disableWidth = width;
6482      }
6483  
6484      /**
6485       * 
6486       * @returns BOOLEAN
6487       */
6488    }, {
6489      key: "isMobileWidth",
6490      value: function isMobileWidth() {
6491        this.log(this.disableWidth);
6492        return window.innerWidth < this.disableWidth;
6493      }
6494  
6495      /**
6496       * 
6497       * @param {HTMLElement} element 
6498       * @param {FLOAT} inView 
6499       * @returns NULL
6500       */
6501    }, {
6502      key: "isInViewport",
6503      value: function isInViewport(element) {
6504        var inView = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.1;
6505        var rect = element.getBoundingClientRect();
6506        var threshold = rect.width * inView; // % of the slide's width  
6507  
6508        return rect.right - threshold > 0 && rect.left + threshold < (window.innerWidth || document.documentElement.clientWidth);
6509      }
6510    }]);
6511  }();
6512  
6513  /***/ }),
6514  
6515  
vendor: 4,878 bytes, lines 6515-6565
6515/***/ "./src/js/observers.js":
6516  /*!*****************************!*\
6517    !*** ./src/js/observers.js ***!
6518    \*****************************/
6519  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
6520  
6521  __webpack_require__.r(__webpack_exports__);
6522  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
6523  /* harmony export */   VgIntersectionObserver: () => (/* binding */ VgIntersectionObserver),
6524  /* harmony export */   VgMutationObserver: () => (/* binding */ VgMutationObserver)
6525  /* harmony export */ });
6526  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
6527  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
6528  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
6529  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
6530  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
6531  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
6532  function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
6533  function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
6534  function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
6535  function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
6536  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
6537  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
6538  var VgIntersectionObserver = /*#__PURE__*/function () {
6539    /**
6540     * 
6541     */
6542    function VgIntersectionObserver() {
6543      _classCallCheck(this, VgIntersectionObserver);
6544      _defineProperty(this, "observer", void 0);
6545      _defineProperty(this, "callback", void 0);
6546      _defineProperty(this, "defaults", void 0);
6547      _defineProperty(this, "settings", void 0);
6548      this.defaults = {
6549        debug: true
6550      };
6551      this.observer = new IntersectionObserver(this.vgCallback.bind(this), {
6552        root: null,
6553        rootMargin: '50px',
6554        threshold: 1.0
6555      });
6556    }
6557  
6558    /**
6559     * 
6560     */
6561    return _createClass(VgIntersectionObserver, [{
6562      key: "log",
6563      value: function log() {
6564        if (this.settings.debug) {
6565          var _console;
6566          (_console = console).log.apply(_console, arguments);
6567        }
6568      }
6569  
6570      /**
6571       * 
6572       * @param {*} selector 
6573       * @param {*} callback 
6574       * @param {*} params 
6575       */
6576    }, {
6577      key: "init",
6578      value: function init(selector, callback) {
6579        var params = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
6580        this.settings = _objectSpread(_objectSpread({}, this.defaults), params);
6581        this.callback = callback;
6582        this.observer.observe(selector);
6583      }
6584  
6585      /**
6586       * 
6587       * @param {*} entries 
6588       * @param {*} observer 
6589       */
6590    }, {
6591      key: "vgCallback",
6592      value: function vgCallback(entries, observer) {
6593        var _this = this;
6594        entries.forEach(function (entry) {
6595          if (entry.isIntersecting) {
6596            observer.unobserve(entry.target);
6597            _this.callback.call(null, {
6598              el: entry.target
6599            });
6600          } else {
6601            _this.callback.call(null, {
6602              error: 'Not intersecting',
6603              el: entry.target
6604            });
6605          }
6606        });
6607      }
6608    }]);
6609  }();
6610  var VgMutationObserver = /*#__PURE__*/function () {
6611    /**
6612     * 
6613     * @param {*} targetSelector 
6614     * @param {*} callBack 
6615     * @param {*} params 
6616     */
6617    function VgMutationObserver(targetSelector, callBack) {
6618      var params = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
6619      _classCallCheck(this, VgMutationObserver);
6620      _defineProperty(this, "options", void 0);
6621      _defineProperty(this, "target", void 0);
6622      _defineProperty(this, "targetSelector", void 0);
6623      _defineProperty(this, "mutationObserver", void 0);
6624      _defineProperty(this, "callBack", void 0);
6625      _defineProperty(this, "defaults", void 0);
6626      _defineProperty(this, "settings", void 0);
6627      this.defaults = {
6628        debug: true
6629      };
6630      this.settings = _objectSpread(_objectSpread({}, this.defaults), params);
6631      this.targetSelector = targetSelector;
6632      // this.log(this.targetSelector);
6633      this.callBack = callBack;
6634      this.init();
6635    }
6636  
6637    /**
6638     * 
6639     */
6640    return _createClass(VgMutationObserver, [{
6641      key: "log",
6642      value: function log() {
6643        if (this.settings.debug) {
6644          var _console2;
6645          (_console2 = console).log.apply(_console2, arguments);
6646        }
6647      }
6648  
6649      /**
6650       * 
6651       */
6652    }, {
6653      key: "init",
6654      value: function init() {
6655        this.options = {
6656          childList: true,
6657          attributes: true,
6658          subtree: true
6659        };
6660        this.target = document.querySelector(this.targetSelector);
6661        this.mutationObserver = new MutationObserver(this.vgCallback.bind(this));
6662        this.mutationObserver.observe(document.documentElement, this.options);
6663      }
6664  
6665      /**
6666       * 
6667       * @param {MutationRecord} mutationRecord 
6668       * @param {MutationObserver} mutationObserver 
6669       */
6670    }, {
6671      key: "vgCallback",
6672      value: function vgCallback(mutationRecord, mutationObserver) {
6673        // this.log(`watchMe Function: in here`);
6674        var _iterator = _createForOfIteratorHelper(mutationRecord),
6675          _step;
6676        try {
6677          for (_iterator.s(); !(_step = _iterator.n()).done;) {
6678            var record = _step.value;
6679            // this.log(`Record Info: ${mutationRecord}`);
6680            if (record.type === "childList") {
6681              this.target = document.querySelector(this.targetSelector);
6682              // this.log(`Target Info: ${this.targetSelector} ${this.target}`);
6683  
6684              if (this.target) {
6685                // console.log(this.target, 'Callback');
6686                mutationObserver.disconnect();
6687                this.log("".concat(this.targetSelector, " has been added and is ready for action"));
6688                this.callBack(this.target, true);
6689              } else {
6690                this.log("".concat(this.targetSelector, " has been deleted or was never present"));
6691                this.callBack(this.target, false);
6692              }
6693              break;
6694            }
6695          }
6696        } catch (err) {
6697          _iterator.e(err);
6698        } finally {
6699          _iterator.f();
6700        }
6701      }
6702    }]);
6703  }();
6704  
6705  /***/ }),
6706  
6707  
6707/***/ "./src/js/vg-animate.js":
6708  /*!******************************!*\
6709    !*** ./src/js/vg-animate.js ***!
6710    \******************************/
6711  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
6712  
6713  __webpack_require__.r(__webpack_exports__);
6714  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
6715  /* harmony export */   VgAnimate: () => (/* binding */ VgAnimate)
6716  /* harmony export */ });
6717  
vendor: 9,050 bytes, line 6717
6717function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function define(t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == _typeof(h) && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function value(t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator["return"] && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(_typeof(e) + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function reset(e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function stop() { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function dispatchException(e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function abrupt(t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function complete(t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function finish(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, "catch": function _catch(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw Error("illegal catch attempt"); }, delegateYield: function delegateYield(e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; }
vendor: 2,191 bytes, lines 6718-6726
6718  function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
6719  function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
6720  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
6721  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
6722  function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
6723  function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
6724  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
6725  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
6726  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {};
6726 r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
6727  function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
6728  function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
6729  function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
6730  function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
6731  function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
6732  function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
6733  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
6734  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
6735  var VgAnimate = /*#__PURE__*/function () {
6736    /**
6737     * 
6738     */
6739    function VgAnimate(base) {
6740      _classCallCheck(this, VgAnimate);
6741      _defineProperty(this, "base", void 0);
6742      _defineProperty(this, "containers", void 0);
6743      _defineProperty(this, "animatablesMap", void 0);
6744      // animatables;
6745      _defineProperty(this, "settings", void 0);
6746      _defineProperty(this, "defaults", void 0);
6747      _defineProperty(this, "animaitionMap", void 0);
6748      this.base = new base();
6749      this.animatablesMap = new Map();
6750  
6751      // Map animation types to classnames
6752      this.animaitionMap = new Map();
6753      this.animaitionMap.set('slideInTop', 'vg-animation-slide-in-top');
6754      this.animaitionMap.set('slideInRight', 'vg-animation-slide-in-right');
6755      this.animaitionMap.set('slideInBottom', 'vg-animation-slide-in-bottom');
6756      this.animaitionMap.set('slideInLeft', 'vg-animation-slide-in-left');
6757      this.animaitionMap.set('fadeIn', 'vg-animation-fadein');
6758      this.animaitionMap.set('fadeOut', 'vg-animation-fadeout');
6759      this.animaitionMap.set('zoomIn', 'vg-animation-zoomin');
6760      this.animaitionMap.set('zoomOut', 'vg-animation-zoomout');
6761      var containerEl = document.createElement('div');
6762      containerEl.classList.add('slide');
6763      this.defaults = {
6764        container: containerEl,
6765        containerElSelector: '.slide',
6766        exitSlideContainerElSelector: '.exit-slide',
6767        debug: true,
6768        animatablesSelector: '*[data-vg-animatable]',
6769        inViewPercent: 0.25,
6770        disableWidth: 992,
6771        disabled: false
6772      };
6773    }
6774  
6775    /**
6776     * 
6777     * @param {*} params 
6778     * @returns 
6779     */
6780    return _createClass(VgAnimate, [{
6781      key: "init",
6782      value: (function () {
6783        var _init = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
6784          var _this2 = this;
6785          var params,
6786            _this,
6787            animationObserver,
6788            exitSlideObserver,
6789            _args = arguments;
6790          return _regeneratorRuntime().wrap(function _callee$(_context) {
6791            while (1) switch (_context.prev = _context.next) {
6792              case 0:
6793                params = _args.length > 0 && _args[0] !== undefined ? _args[0] : {};
6794                this.settings = _objectSpread(_objectSpread({}, this.defaults), params);
6795                this.base.setDebug(this.settings.debug);
6796                this.base.setDisableWidth(this.settings.disableWidth);
6797  
6798                // Handle any new animations
6799                if (_typeof(this.settings.animations) == 'object' && this.settings.animations.length) {
6800                  Object.entries(this.settings.animations).forEac
6800h(function (_ref) {
6801                    var _ref2 = _slicedToArray(_ref, 2),
6802                      key = _ref2[0],
6803                      value = _ref2[1];
6804                    _this2.animaitionMap.set(key, value);
6805                  });
6806                }
6807  
6808                // if (!this.settings.container) return false;
6809                this.containers = document.querySelectorAll(this.settings.containerElSelector);
6810                if (this.containers) {
6811                  _context.next = 8;
6812                  break;
6813                }
6814                return _context.abrupt("return", false);
6815              case 8:
6816                this.containers.forEach(function (el) {
6817                  var animatables = el.querySelectorAll(_this2.settings.animatablesSelector);
6818                  // this.animatables = animatables;
6819  
6820                  if (!animatables.length) return false;
6821                  _this2.base.log('Animatables: ', animatables);
6822                  _this2.animatablesMap.set(el, animatables);
6823                  return true;
6824                });
6825                _this = this;
6826                animationObserver = new IntersectionObserver(function (entries, observer) {
6827                  entries.forEach(function (entry) {
6828                    if (entry.isIntersecting) {
6829                      _this.animate(entry.target, observer);
6830                    }
6831                  });
6832                }, {
6833                  root: null,
6834                  threshold: 0.5
6835                });
6836                exitSlideObserver = new IntersectionObserver(function (entries, observer) {
6837                  entries.forEach(function (entry) {
6838                    if (entry.isIntersecting) {
6839                      _this.animate(entry.target, observer);
6840                    }
6841                  });
6842                }, {
6843                  root: null,
6844                  threshold: 0.25
6845                });
6846                document.querySelectorAll(this.settings.containerElSelector).forEach(function (item) {
6847                  animationObserver.observe(item);
6848                });
6849                document.querySelectorAll(this.settings.exitSlideContainerElSelector).forEach(function (item) {
6850                  exitSlideObserver.observe(item);
6851                });
6852              case 14:
6853              case "end":
6854                return _context.stop();
6855            }
6856          }, _callee, this);
6857        }));
6858        function init() {
6859          return _init.apply(this, arguments);
6860        }
6861        return init;
6862      }()
6863      /**
6864       * Starts the animations
6865       */
6866      )
6867    }, {
6868      key: "animate",
6869      value: (function () {
6870        var _animate = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee2(containerElem, observer) {
6871          var _this3 = this;
6872          var els;
6873          return _regeneratorRuntime().wrap(function _callee2$(_context2) {
6874            while (1) switch (_context2.prev = _context2.next) {
6875              case 0:
6876                if (!this.settings.disabled) {
6877                  _context2.next = 2;
6878                  break;
6879                }
6880                return _context2.abrupt("return");
6881              case 2:
6882                els = this.animatablesMap.get(containerElem);
6883                if (els) {
6884                  _context2.next = 5;
6885                  break;
6886                }
6887                return _context2.abrupt("return");
6888              case 5:
6889                if (!this.base.isMobileWidth()) {
6890                  // Interate each ainimateable element to determine the animation type
6891                  els.forEach(function (el) {
6892                    var animation = el.dataset.vgAnimationEffect;
6893  
6894                    // Now execute the required animation
6895                    if (!animation) return false;
6896                    _this3.handleAnimation(el, animation);
6897                    observer.unobserve(containerElem);
6898                  });
6899                }
6900              case 6:
6901              case "end":
6902                return _context2.stop();
6903            }
6904          }, _callee2, this);
6905        }));
6906        function animate(_x, _x2) {
6907          return _animate.apply(this, arguments);
6908        }
6909        return animate;
6910      }()
6911      /**
6912       * Adds the appropriate animation class to an element
6913       * @param {*} el 
6914       * @param {*} animation 
6915       * @returns 
6916       */
6917      )
6918    }, {
6919      key: "handleAnimation",
6920      value: function handleAnimation(el, animation) {
6921        var animationClass = this.animaitionMap.get(animation),
6922          animationSpeed = el.dataset.vgAnimationSpeed || 'moderate',
6923          animationDelay = el.dataset.vgAnimationDelay || 'none';
6924        if (!animationClass) return;
6925        el.classList.add("".concat(animationClass, "-").concat(animationSpeed, "-").concat(animationDelay));
6926      }
6927    }]);
6928  }();
6929  
6930  /***/ }),
6931  
6932  
6932/***/ "./src/js/vg-carousel.js":
6933  /*!*******************************!*\
6934    !*** ./src/js/vg-carousel.js ***!
6935    \*******************************/
6936  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
6937  
6938  __webpack_require__.r(__webpack_exports__);
6939  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
6940  /* harmony export */   VgCarousel: () => (/* binding */ VgCarousel)
6941  /* harmony export */ });
6942  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
6943  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
6944  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
6945  function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
6946  function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
6947  function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
6948  function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
6949  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
6950  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
6951  var VgCarousel = /*#__PURE__*/function () {
6952    function VgCarousel(Base) {
6953      var Parallax = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
6954      _classCallCheck(this, VgCarousel);
6955      _defineProperty(this, "base", void 0);
6956      _defineProperty(this, "scrollContainer", void 0);
6957      _defineProperty(this, "carousel", void 0);
6958      _defineProperty(this, "slides", void 0);
6959      _defineProperty(this, "parallax", void 0);
6960      _defineProperty(this, "settings", void 0);
6961      _defineProperty(this, "defaults", void 0);
6962      _defineProperty(this, "next", void 0);
6963      _defineProperty(this, "previous", void 0);
6964      _defineProperty(this, "introCta", void 0);
6965      _defineProperty(this, "slidesMap", void 0);
6966      this.ticking = false;
6967      this.base = Base;
6968      this.parallax = Parallax;
6969      this.slidesMap = new Map();
6970      this.defaults = {
6971        throttleMax: 4,
6972        carouselSelector: '#vg-carousel',
6973        carousel: null,
6974        carouselScrollContainerSelector: '#vg-scroll-container',
6975        debug: true,
6976        slideSelector: '.slide',
6977        speedReductionRatio: 0.05,
6978        disableWidth: 992,
6979        disableOnResize: false,
6980        nextSelector: '.next',
6981        previousSelector: '.previous',
6982        mobileAutoAdvanceDelay: 1000,
6983        mobileAutoAdvanceElement: '.htl-intro-text-container',
6984        introCtaSelector: '.htl-intro-cta'
6985      };
6986    }
6987    return _createClass(VgCarousel, [{
6988      key: "init",
6989      value: function init() {
6990        var _this = this;
6991        var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
6992        this.currentSlide = 0;
6993        this.settings = _objectSpread(_objectSpread({}, this.defaults), params);
6994        this.throttleCount = 0;
6995        this.base.setDebug(this.settings.debug);
6996        this.base.setDisableWidth(this.settings.disableWidth);
6997        if (!this.settings.carouselSelector) return false;
6998        this.scrollContainer = document.querySelector(this.settings.carouselScrollContainerSelector);
6999        if (!this.scrollContainer) return false;
7000        this.carousel = this.settings.carousel || document.querySelector(this.settings.carouselSelector);
7001        if (!this.carousel) return false;
7002        this.handleIntroCta();
7003        this.slides = this.carousel.querySelectorAll(this.settings.slideSelector);
7004        // Cache slides for later use (ordered as they appear in the DOM)
7005        var x = 0;
7006        this.slides.forEach(function (slide) {
7007          _this.slidesMap.set(x, slide);
7008          x++;
7009        });
7010        this.setContainerHeight(this.scrollContainer, this.slides);
7011        this.setContainerWidth(this.carousel, this.slides);
7012        var ticking = false;
7013        var scrollTop = window.scrollY;
7014        this.update(scrollTop);
7015        window.addEventListener('scroll', function () {
7016          if (!ticking) {
7017            scrollTop = window.scrollY || document.documentElement.scrollTop;
7018            requestAnimationFrame(function () {
7019              _this.update(scrollTop);
7020              ticking = false;
7021            });
7022            ticking = true;
7023          }
7024        });
7025        if (!this.settings.disableOnResize) {
7026          document.addEventListener('resize', function () {
7027            _this.setContainerHeight(scrollContainer, _this.slides);
7028            _this.setContainerWidth(_this.carousel, _this.slides);
7029          });
7030        }
7031        this.next = document.querySelector(this.settings.nextSelector);
7032        this.previous = document.querySelector(this.settings.previousSelector);
7033        if (this.next && this.previous) {
7034          this.next.addEventListener('click', function (e) {
7035            e.preventDefault();
7036            _this.handleNext();
7037          });
7038          this.previous.addEventListener('click', function (e) {
7039            e.preventDefault();
7040            _this.handlePrev();
7041          });
7042        }
7043      }
7044    }, {
7045      key: "setContainerHeight",
7046      value: function setContainerHeight(container, slides) {
7047        var totalWidth = Array.from(slides).reduce(function (total, slide) {
7048          return total + slide.offsetWidth;
7049        }, 0);
7050        container.style.height = "".concat(totalWidth + window.innerWidth, "px");
7051      }
7052    }, {
7053      key: "setContainerWidth",
7054      value: function setContainerWidth(container, slides) {
7055        return;
7056        var totalWidth = Array.from(slides).reduce(function (total, slide) {
7057          return total + slide.offsetWidth;
7058        }, 0);
7059        container.style.width = "".concat(totalWidth + this.scrollContainer.offsetTop, "px");
7060      }
7061    }, {
7062      key: "update",
7063      value: function update(scrollTop) {
7064        var _this2 = this;
7065        var carouselOffsetTop = this.scrollContainer.offsetTop;
7066        if (this.base.isMobileWidth()) return false;
7067        this.base.log('Top: ', this.scrollContainer.offsetTop);
7068        if (scrollTop <= carouselOffsetTop) {
7069          this.carousel.style.transform = 'translate3d(0px, 0, 0)';
7070        } else {
7071          // let slide;
7072          var carouselHeight = this.scrollContainer.offsetHeight,
7073            verticalScroll = scrollTop - carouselOffsetTop,
7074            maxVerticalScroll = carouselHeight - window.innerHeight,
7075            // Clamp progress between 0 and 1
7076            progress = Math.min(Math.max(verticalScroll / maxVerticalScroll, 0), 1),
7077            // The total horizontal scroll distance equals (number of slides - 1) * viewport width.
7078            maxHorizontalScroll = this.getMaxHorizontalScroll(),
7079            translateX = -progress * maxHorizontalScroll;
7080          this.carousel.style.transform = "translate3d(".concat(translateX.toFixed(2), "px, 0, 0)");
7081          // Update the current slide index (rounded)
7082          this.currentSlide = Math.round(progress * (this.slides.length));
7083          //console.log('current slide: ' + this.currentSlide);
7084  
7085          // Throttle carousel/Parallax movement
7086          if (this.throttleCount >= this.settings.throttleMax) {
7087            this.throttleCount = 0;
7088            if (this.parallax) {
7089              // window.requestAnimationFrame(() => {
7090              // Get the current slide element from the slide Map
7091              this.slides.forEach(function (slide, index) {
7092                _this2.parallax.update(slide, index);
7093              });
7094  
7095              // });
7096            }
7097          } else {
7098            this.throttleCount++;
7099          }
7100        }
7101      }
7102    }, {
7103      key: "handleIntroCta",
7104      value: function handleIntroCta() {
7105        var _this3 = this;
7106        this.introCta = document.querySelector(this.settings.introCtaSelector);
7107        if (!this.introCta) return;
7108        this.introCta.addEventListener('click', function () {
7109          _this3.handleNext();
7110        });
7111      }
7112    }, {
7113      key: "handleNext",
7114      value: function handleNext() {
7115        if (this.currentSlide < this.slides.length - 1) {
7116          this.currentSlide++;
7117          this.scrollToSlide(this.currentSlide);
7118        }
7119      }
7120    }, {
7121      key: "handlePrev",
7122      value: function handlePrev() {
7123        if (this.currentSlide > 0) {
7124          this.currentSlide--;
7125          this.scrollToSlide(this.currentSlide);
7126        }
7127      }
7128  
7129      // Calculate total horizontal scrollable distance.
7130    }, {
7131      key: "getMaxHorizontalScroll",
7132      value: function getMaxHorizontalScroll() {
7133        return this.carousel.scrollWidth - window.innerWidth;
7134      }
7135  
7136      /*
7137        Helper function to calculate the window scrollY value for a given slide index.
7138        When slides have variable widths, we want to center the target slide in the viewport.
7139        1. We compute the slide’s center relative to the carouselWrapper.
7140        2. The desired horizontal translation (translateX) to center that slide is:
7141             desiredTranslateX = -(slide.offsetLeft + slide.offsetWidth/2 - window.innerWidth/2)
7142        3. Since our scroll-driven translation is:
7143             translateX = -progress * (carouselWrapper.scrollWidth - window.innerWidth)
7144           we can invert this to get the progress:
7145             progress = (slide.offsetLeft + slide.offsetWidth/2 - window.innerWidth/2) / (carouselWrapper.scrollWidth - window.innerWidth)
7146        4. Finally, the vertical scrollY value is:
7147             scrollY = carouselSection.offsetTop + progress * (carouselSection.offsetHeight - window.innerHeight)
7148
7149        Update: instead of centering, we are making the the start be pinned left
7150      */
7151    }, {
7152      key: "getScrollYForSlide",
7153      value: function getScrollYForSlide(slideIndex) {
7154        var slide = this.slides[slideIndex],
7155          slideCenter = slide.offsetLeft + slide.offsetWidth / 2,
7156          viewportCenter = window.innerWidth / 2,
7157          maxHorizontalScroll = this.getMaxHorizontalScroll();
7158        var
7159          // Calculate desired translateX (which is negative if we move left)
7160          //desiredTranslateX = -(slideCenter - viewportCenter),
7161          desiredTranslateX = -(slide.offsetLeft),
7162          // Invert the formula to compute progress
7163          progress = -desiredTranslateX / maxHorizontalScroll;
7164  
7165        // Clamp progress between 0 and 1
7166        progress = Math.min(Math.max(progress, 0), 1);
7167        var carouselHeight = this.scrollContainer.offsetHeight,
7168          maxVerticalScroll = carouselHeight - window.innerHeight,
7169          targetScrollY = this.scrollContainer.offsetTop + progress * maxVerticalScroll;
7170        return targetScrollY;
7171      }
7172    }, {
7173      key: "scrollToSlide",
7174      value: function scrollToSlide(slideIndex) {
7175        var targetScroll = this.getScrollYForSlide(slideIndex);
7176        window.scrollTo({
7177          top: targetScroll,
7178          behavior: 'smooth'
7179        });
7180      }
7181    }]);
7182  }();
7183  
7184  /***/ }),
7185  
7186  
7186/***/ "./src/js/vg-parallaxv2.js":
7187  /*!*********************************!*\
7188    !*** ./src/js/vg-parallaxv2.js ***!
7189    \*********************************/
7190  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
7191  
7192  __webpack_require__.r(__webpack_exports__);
7193  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
7194  /* harmony export */   VgParallax: () => (/* binding */ VgParallax)
7195  /* harmony export */ });
7196  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
7197  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
7198  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
7199  function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
7200  function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
7201  function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
7202  function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
7203  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
7204  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
7205  var VgParallax = /*#__PURE__*/function () {
7206    function VgParallax(base) {
7207      _classCallCheck(this, VgParallax);
7208      _defineProperty(this, "base", void 0);
7209      _defineProperty(this, "scrollContainer", void 0);
7210      _defineProperty(this, "carousel", void 0);
7211      _defineProperty(this, "slides", void 0);
7212      _defineProperty(this, "settings", void 0);
7213      _defineProperty(this, "defaults", void 0);
7214      this.base = base;
7215      this.defaults = {
7216        debug: true,
7217        parallaxablesSelector: '*[data-vg-parallaxable]',
7218        parallaxableEffectAttribute: 'data-vg-parallax-effect',
7219        parallaxableSpeedAttribute: 'data-vg-parallax-speed',
7220        disableWidth: 992,
7221        inViewPercent: 0.1,
7222        disabled: false
7223      };
7224    }
7225    return _createClass(VgParallax, [{
7226      key: "init",
7227      value: function init() {
7228        var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
7229        this.settings = _objectSpread(_objectSpread({}, this.defaults), params);
7230        this.base.setDebug(this.settings.debug);
7231        this.base.setDisableWidth(this.settings.disableWidth);
7232      }
7233    }, {
7234      key: "update",
7235      value: function update(slide, index) {
7236        var _this = this;
7237        if (this.settings.disabled) return;
7238        var rect = slide.getBoundingClientRect(),
7239          isVisible = this.base.isInViewport(slide, this.settings.inViewPercent),
7240          isMobile = this.base.isMobileWidth();
7241        var parallaxbles = slide.querySelectorAll(this.settings.parallaxablesSelector);
7242        if (parallaxbles.length && isVisible && !isMobile) {
7243          parallaxbles.forEach(function (parallax) {
7244            var effect = parallax.dataset.vgParallaxEffect,
7245              speed = parseFloat(parallax.dataset.vgParallaxSpeed) || 20,
7246              // Paralax movement bounds has been extended past 1 to 1.5 
7247              // whcih should effectively cause the parallaxed element to move 
7248              // 50% past the left bounds of its slide container
7249              progress = Math.max(0, Math.min(2, (window.innerWidth - rect.left) / window.innerWidth)),
7250              params = {};
7251            if (index === 0) {
7252              progress = Math.max(0, (window.innerWidth - rect.left) / window.innerWidth);
7253              if (/^fade-in|^slide-fade-in/.test(effect)) {
7254                progress = Math.max(1, (window.innerWidth - rect.left) / window.innerWidth) - 1;
7255              }
7256            }
7257            if (isVisible) {
7258              params = {
7259                parallax: parallax,
7260                effect: effect,
7261                progress: progress,
7262                speed: speed
7263              };
7264  
7265              // Apply different effects dynamically
7266              switch (true) {
7267                case /^slideIn/.test(effect):
7268                  _this.handleSlideEffects(params);
7269                  break;
7270                case /^slideZoom/.test(effect):
7271                  _this.handleSlideZoomEffects(params);
7272                  break;
7273                case /^slideFade/.test(effect):
7274                  _this.handleSlideFadeEffects(params);
7275                  break;
7276                case /^zoom/.test(effect):
7277                  _this.handleZoomEffects(params);
7278                  break;
7279                case /^fade/.test(effect):
7280                  _this.handleFadeEffects(params);
7281                  break;
7282              }
7283            } else {
7284              parallax.style.opacity = 0; // Hide when out of view
7285            }
7286          });
7287        } else if (isMobile) {
7288          parallaxbles.forEach(function (parallax) {
7289            // show everything
7290            parallax.style.opacity = 1;
7291          });
7292        }
7293      }
7294    }, {
7295      key: "handleSlideEffects",
7296      value: function handleSlideEffects(params) {
7297        switch (params.effect) {
7298          case "slideInLeft":
7299            params.parallax.style.transform = "translateX(".concat(((1 - params.progress) * params.speed).toFixed(2), "px) translate3d(0,0,0)");
7300            params.parallax.style.opacity = 1; // Make visible
7301            break;
7302          case "slideInRight":
7303            params.parallax.style.transform = "translateX(".concat(((-1 + params.progress) * params.speed).toFixed(2), "px) translate3d(0,0,0)");
7304            params.parallax.style.opacity = 1; // Make visible
7305            break;
7306          case "slideInBottom":
7307            params.parallax.style.transform = "translateY(".concat(((1 - params.progress) * params.speed).toFixed(2), "px) translate3d(0,0,0)");
7308            params.parallax.style.opacity = 1; // Make visible
7309            break;
7310          case "slideInTop":
7311            params.parallax.style.transform = "translateY(".concat(((-1 + params.progress) * params.speed).toFixed(2), "px) translate3d(0,0,0)");
7312            params.parallax.style.opacity = 1; // Make visible
7313            break;
7314        }
7315      }
7316    }, {
7317      key: "handleSlideZoomEffects",
7318      value: function handleSlideZoomEffects(params) {
7319        switch (params.effect) {
7320          case "slideZoomInLeft":
7321            params.parallax.style.transform = "scale(".concat(0.1 + params.progress * params.speed * 0.1, ") translateX(").concat(((1 - params.progress) * params.speed).toFixed(2), "px) translate3d(0,0,0)");
7322            params.parallax.style.opacity = 1; // Make visible
7323            break;
7324          case "slideZoomInRight":
7325            params.parallax.style.transform = "scale(".concat(0.1 + params.progress * params.speed * 0.1, ") translateX(").concat(((-1 + params.progress) * params.speed).toFixed(2), "px) translate3d(0,0,0)");
7326            params.parallax.style.opacity = 1; // Make visible
7327            break;
7328          case "slideZoomInBottom":
7329            params.parallax.style.transform = "scale(".concat(0.1 + params.progress * params.speed * 0.1, ") translateY(").concat(((1 - params.progress) * params.speed).toFixed(2), "px) translate3d(0,0,0)");
7330            params.parallax.style.opacity = 1; // Make visible
7331            break;
7332          case "slideZoomInTop":
7333            params.parallax.style.transform = "scale(".concat(0.1 + params.progress * params.speed * 0.1, ") translateY(").concat(((-1 + params.progress) * params.speed).toFixed(2), "px) translate3d(0,0,0)");
7334            params.parallax.style.opacity = 1; // Make visible
7335            break;
7336        }
7337      }
7338    }, {
7339      key: "handleSlideFadeEffects",
7340      value: function handleSlideFadeEffects(params) {
7341        switch (params.effect) {
7342          case "slideFadeInLeft":
7343            params.parallax.style.transform = "scale(".concat(0.1 + params.progress * params.speed * 0.1, ") translateX(").concat(((1 - params.progress) * params.speed).toFixed(2), "px) translate3d(0,0,0)");
7344            params.parallax.style.opacity = params.progress * params.speed;
7345            break;
7346          case "slideFadeInRight":
7347            params.parallax.style.transform = "scale(".concat(0.1 + params.progress * params.speed * 0.1, ") translateX(").concat(((-1 + params.progress) * params.speed).toFixed(2), "px) translate3d(0,0,0)");
7348            params.parallax.style.opacity = params.progress * params.speed;
7349            break;
7350          case "slideFadeInBottom":
7351            params.parallax.style.transform = "scale(".concat(0.1 + params.progress * params.speed * 0.1, ") translateY(").concat(((1 - params.progress) * params.speed).toFixed(2), "px) translate3d(0,0,0)");
7352            params.parallax.style.opacity = params.progress * params.speed;
7353            break;
7354          case "slideFadeInTop":
7355            params.parallax.style.transform = "scale(".concat(0.1 + params.progress * params.speed * 0.1, ") translateY(").concat(((-1 + params.progress) * params.speed).toFixed(2), "px) translate3d(0,
73550,0)");
7356            params.parallax.style.opacity = params.progress * params.speed;
7357            break;
7358        }
7359      }
7360    }, {
7361      key: "handleZoomEffects",
7362      value: function handleZoomEffects(params) {
7363        switch (params.effect) {
7364          case "zoomIn":
7365            params.parallax.style.transform = "scale(".concat((0.5 + params.progress * params.speed * 0.5).toFixed(2), ") translate3d(0,0,0)");
7366            params.parallax.style.opacity = 1; // Make visible
7367            break;
7368          case "zoomOut":
7369            params.parallax.style.transform = "scale(".concat((1.5 - params.progress * params.speed * 0.5).toFixed(2), ") translate3d(0,0,0)");
7370            params.parallax.style.opacity = 1; // Make visible
7371            break;
7372        }
7373      }
7374    }, {
7375      key: "handleFadeEffects",
7376      value: function handleFadeEffects(params) {
7377        switch (params.effect) {
7378          case "fadeIn":
7379            params.parallax.style.opacity = params.progress * params.speed;
7380            break;
7381          case "fadeOut":
7382            params.parallax.style.opacity = (1 - params.progress) * params.speed;
7383            break;
7384        }
7385      }
7386    }]);
7387  }();
7388  
7389  /***/ }),
7390  
7391  
7391/***/ "./src/scss/app-lite.scss":
7392  /*!********************************!*\
7393    !*** ./src/scss/app-lite.scss ***!
7394    \********************************/
7395  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
7396  
7397  __webpack_require__.r(__webpack_exports__);
7398  // extracted by mini-css-extract-plugin
7399  
7400  
7401  /***/ }),
7402  
7403  
7403/***/ "./src/scss/app.scss":
7404  /*!***************************!*\
7405    !*** ./src/scss/app.scss ***!
7406    \***************************/
7407  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
7408  
7409  __webpack_require__.r(__webpack_exports__);
7410  // extracted by mini-css-extract-plugin
7411  
7412  
7413  /***/ })
vendor: 6,511 bytes, lines 7413-7565
7413
7414  
7415  /******/ 	});
7416  /************************************************************************/
7417  /******/ 	// The module cache
7418  /******/ 	var __webpack_module_cache__ = {};
7419  /******/ 	
7420  /******/ 	// The require function
7421  /******/ 	function __webpack_require__(moduleId) {
7422  /******/ 		// Check if module is in cache
7423  /******/ 		var cachedModule = __webpack_module_cache__[moduleId];
7424  /******/ 		if (cachedModule !== undefined) {
7425  /******/ 			return cachedModule.exports;
7426  /******/ 		}
7427  /******/ 		// Create a new module (and put it into the cache)
7428  /******/ 		var module = __webpack_module_cache__[moduleId] = {
7429  /******/ 			// no module.id needed
7430  /******/ 			// no module.loaded needed
7431  /******/ 			exports: {}
7432  /******/ 		};
7433  /******/ 	
7434  /******/ 		// Execute the module function
7435  /******/ 		__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
7436  /******/ 	
7437  /******/ 		// Return the exports of the module
7438  /******/ 		return module.exports;
7439  /******/ 	}
7440  /******/ 	
7441  /******/ 	// expose the modules object (__webpack_modules__)
7442  /******/ 	__webpack_require__.m = __webpack_modules__;
7443  /******/ 	
7444  /************************************************************************/
7445  /******/ 	/* webpack/runtime/chunk loaded */
7446  /******/ 	(() => {
7447  /******/ 		var deferred = [];
7448  /******/ 		__webpack_require__.O = (result, chunkIds, fn, priority) => {
7449  /******/ 			if(chunkIds) {
7450  /******/ 				priority = priority || 0;
7451  /******/ 				for(var i = deferred.length; i > 0 && deferred[i - 1][2] > priority; i--) deferred[i] = deferred[i - 1];
7452  /******/ 				deferred[i] = [chunkIds, fn, priority];
7453  /******/ 				return;
7454  /******/ 			}
7455  /******/ 			var notFulfilled = Infinity;
7456  /******/ 			for (var i = 0; i < deferred.length; i++) {
7457  /******/ 				var [chunkIds, fn, priority] = deferred[i];
7458  /******/ 				var fulfilled = true;
7459  /******/ 				for (var j = 0; j < chunkIds.length; j++) {
7460  /******/ 					if ((priority & 1 === 0 || notFulfilled >= priority) && Object.keys(__webpack_require__.O).every((key) => (__webpack_require__.O[key](chunkIds[j])))) {
7461  /******/ 						chunkIds.splice(j--, 1);
7462  /******/ 					} else {
7463  /******/ 						fulfilled = false;
7464  /******/ 						if(priority < notFulfilled) notFulfilled = priority;
7465  /******/ 					}
7466  /******/ 				}
7467  /******/ 				if(fulfilled) {
7468  /******/ 					deferred.splice(i--, 1)
7469  /******/ 					var r = fn();
7470  /******/ 					if (r !== undefined) result = r;
7471  /******/ 				}
7472  /******/ 			}
7473  /******/ 			return result;
7474  /******/ 		};
7475  /******/ 	})();
7476  /******/ 	
7477  /******/ 	/* webpack/runtime/define property getters */
7478  /******/ 	(() => {
7479  /******/ 		// define getter functions for harmony exports
7480  /******/ 		__webpack_require__.d = (exports, definition) => {
7481  /******/ 			for(var key in definition) {
7482  /******/ 				if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
7483  /******/ 					Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
7484  /******/ 				}
7485  /******/ 			}
7486  /******/ 		};
7487  /******/ 	})();
7488  /******/ 	
7489  /******/ 	/* webpack/runtime/hasOwnProperty shorthand */
7490  /******/ 	(() => {
7491  /******/ 		__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
7492  /******/ 	})();
7493  /******/ 	
7494  /******/ 	/* webpack/runtime/make namespace object */
7495  /******/ 	(() => {
7496  /******/ 		// define __esModule on exports
7497  /******/ 		__webpack_require__.r = (exports) => {
7498  /******/ 			if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
7499  /******/ 				Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
7500  /******/ 			}
7501  /******/ 			Object.defineProperty(exports, '__esModule', { value: true });
7502  /******/ 		};
7503  /******/ 	})();
7504  /******/ 	
7505  /******/ 	/* webpack/runtime/jsonp chunk loading */
7506  /******/ 	(() => {
7507  /******/ 		// no baseURI
7508  /******/ 		
7509  /******/ 		// object to store loaded and loading chunks
7510  /******/ 		// undefined = chunk not loaded, null = chunk preloaded/prefetched
7511  /******/ 		// [resolve, reject, Promise] = chunk loading, 0 = chunk loaded
7512  /******/ 		var installedChunks = {
7513  /******/ 			"/dist/js/app": 0,
7514  /******/ 			"dist/css/app": 0,
7515  /******/ 			"dist/css/app-lite": 0
7516  /******/ 		};
7517  /******/ 		
7518  /******/ 		// no chunk on demand loading
7519  /******/ 		
7520  /******/ 		// no prefetching
7521  /******/ 		
7522  /******/ 		// no preloaded
7523  /******/ 		
7524  /******/ 		// no HMR
7525  /******/ 		
7526  /******/ 		// no HMR manifest
7527  /******/ 		
7528  /******/ 		__webpack_require__.O.j = (chunkId) => (installedChunks[chunkId] === 0);
7529  /******/ 		
7530  /******/ 		// install a JSONP callback for chunk loading
7531  /******/ 		var webpackJsonpCallback = (parentChunkLoadingFunction, data) => {
7532  /******/ 			var [chunkIds, moreModules, runtime] = data;
7533  /******/ 			// add "moreModules" to the modules object,
7534  /******/ 			// then flag all "chunkIds" as loaded and fire callback
7535  /******/ 			var moduleId, chunkId, i = 0;
7536  /******/ 			if(chunkIds.some((id) => (installedChunks[id] !== 0))) {
7537  /******/ 				for(moduleId in moreModules) {
7538  /******/ 					if(__webpack_require__.o(moreModules, moduleId)) {
7539  /******/ 						__webpack_require__.m[moduleId] = moreModules[moduleId];
7540  /******/ 					}
7541  /******/ 				}
7542  /******/ 				if(runtime) var result = runtime(__webpack_require__);
7543  /******/ 			}
7544  /******/ 			if(parentChunkLoadingFunction) parentChunkLoadingFunction(data);
7545  /******/ 			for(;i < chunkIds.length; i++) {
7546  /******/ 				chunkId = chunkIds[i];
7547  /******/ 				if(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {
7548  /******/ 					installedChunks[chunkId][0]();
7549  /******/ 				}
7550  /******/ 				installedChunks[chunkId] = 0;
7551  /******/ 			}
7552  /******/ 			return __webpack_require__.O(result);
7553  /******/ 		}
7554  /******/ 		
7555  /******/ 		var chunkLoadingGlobal = self["webpackChunkparallax"] = self["webpackChunkparallax"] || [];
7556  /******/ 		chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));
7557  /******/ 		chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));
7558  /******/ 	})();
7559  /******/ 	
7560  /************************************************************************/
7561  /******/ 	
7562  /******/ 	// startup
7563  /******/ 	// Load entry module and return exports
7564  /******/ 	// This entry module depends on other loaded chunks and execution need to be delayed
7565  /******/ 	__webpack_require__.O(undefined, ["dist/css/app","dist/css/app-lite"], (
7565) => (__webpack_require__("./src/js/app.js")))
7566  /******/ 	__webpack_require__.O(undefined, ["dist/css/app","dist/css/app-lite"], () => (__webpack_require__("./src/scss/app-lite.scss")))
7567  /******/ 	var __webpack_exports__ = __webpack_require__.O(undefined, ["dist/css/app","dist/css/app-lite"], () => (__webpack_require__("./src/scss/app.scss")))
7568  /******/ 	__webpack_exports__ = __webpack_require__.O(__webpack_exports__);
7569  /******/ 	
7570  /******/ })()
7571  ;
7572// =============================================================================
7573// clientErrorLogger.js
7574// =============================================================================
7575// PURPOSE: Ships client-side JS errors to the server so they appear in
7576// logs/error.log and are discoverable via Splunk. Uses function declaration
7577// (hoisted) so it is safe for any file in this bundle to call, regardless of
7578// concatenation order.
7579//
7580// USAGE: logClientError("ERROR" | "WARN", message, source)
7581//
7582// Example: logClientError("WARN", "Error processing URL: " + href, "external-link-icon.js");
7583//
7584// WHY FIRE-AND-FORGET: A logging utility that throws or blocks on network
7585// errors would make every catch block worse. The inner try/catch guarantees
7586// this function is always safe to call.
7587// =============================================================================
7588
7589function logClientError(level, message, source) {
7590    try {
7591        if (level !== "ERROR" && level !== "WARN") return;
7592        let msg = String(message != null ? message : "").substring(0, 500);
7593        let src = String(source != null ? source : "").substring(0, 200);
7594        if (!msg || !src) return;
7595        $.ajax({
7596            type: "GET",
7597            url: "/api/corporatesite/client-log",
7598            data: {
7599                level: level,
7600                message: msg,
7601                source: src,
7602                url: window.location.href
7603            }
7604        });
7605    } catch (e) { /* must never throw — logging failures must not impact UX */ }
7606}
7607
7608window.addEventListener('pageshow', function (event) {
7609    if (event.persisted) {
7610        window.location.reload();
7611    }
7612});
7613
7614if (performance.navigation.type === 2) {
7615    window.location.reload();
7616}
7617
7618const errorIcon =
7619    `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" class="bi bi-exclamation-diamond-fill" viewBox="0 0 16 16">
7620        <path d="M9.05.435c-.58-.58-1.52-.58-2.1 0L.436 6.95c-.58.58-.58 1.519 0 2.098l6.516 6.516c.58.58 1.519.58 2.098 0l6.516-6.516c.58-.58.58-1.519 
76200-2.098L9.05.435zM8 4c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 4.995A.905.905 0 0 1 8 4zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/>
7621    </svg>`;
7622
7623function validatingEmail(elementID) {
7624    const field = document.getElementById(elementID);
7625    const errorMessage = document.createElement('span');
7626    errorMessage.setAttribute('role', 'alert');
7627    errorMessage.setAttribute('tabindex', 0);
7628    const mailformat = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
7629    const data = jQuery(`#${elementID}`).val();
7630    const requiredEmail = document.getElementById('errorRequired');
7631    const invalidEmail = document.getElementById('errorInvalid');
7632
7633    if (data === '') {
7634        errorMessage.setAttribute('id', 'errorRequired');
7635        errorMessage.innerHTML = `${errorIcon} <b>Error:</b> Email is required`;
7636
7637        if (!requiredEmail) {
7638            field.parentNode.insertBefore(errorMessage, field.nextSibling);
7639            field.focus();
7640        }
7641        if (invalidEmail) {
7642            invalidEmail.remove();
7643        }
7644        return false;
7645
7646    } else if (data !== '' && !data.match(mailformat)) {
7647        errorMessage.setAttribute('id', 'errorInvalid');
7648        errorMessage.innerHTML = `${errorIcon} <b>Error:</b> Email is not valid`;
7649
7650        if (!invalidEmail) {
7651            field.parentNode.insertBefore(errorMessage, field.nextSibling);
7652            field.focus();
7653        }
7654        if (requiredEmail) {
7655            requiredEmail.remove();
7656        }
7657        return false;
7658    }
7659
7660    if (invalidEmail) {
7661        invalidEmail.remove();
7662    }
7663    if (requiredEmail) {
7664        requiredEmail.remove();
7665    }
7666    return true;
7667}
7668
7669function validatingIndustry(elementID) {
7670    const field = document.getElementById(elementID);
7671    const errorMessage = document.createElement('span');
7672    errorMessage.setAttribute('id', 'errorIndustry');
7673    errorMessage.setAttribute('role', 'alert');
7674    errorMessage.setAttribute('tabindex', 0);
7675    const data = jQuery(`#${elementID}`).val();
7676    const errorIndustry = document.getElementById('errorIndustry');
7677
7678    if (data === 'Select industry...' && !errorIndustry) {
7679        errorMessage.innerHTML = `${errorIcon} <b>Error:</b> No industry selected`;
7680        field.parentNode.insertBefore(errorMessage, field.nextSibling);
7681		field.focus();
7682        return false;
7683    } else if (data !== 'Select industry...' && errorIndustry) {
7684        errorIndustry.remove();
7685        return false;
7686    } else if (data === 'Select industry...' && errorIndustry) {
7687        field.focus();
7688        return false;
7689    } else {
7690        return true;
7691    }
7692}
7693
7694function displayErrorMessage(errorSpan){
7695    errorSpan.innerHTML = `${errorIcon} <b>Error:</b> ${errorSpan.getAttribute('data-error-message')}`;
7696    errorSpan.setAttribute('class', 'errorMessageShow');
7697}
7698
7699function hideErrorMessage(errorSpan){
7700    errorSpan.innerHTML = '';
7701}
7702
7703function generateValidator(inputValidationFx) {
7704    return function generateValidatorInner(inputId, errorSpanId) {
7705        let input;
7706        if (inputId){
7707            input = jQuery(`#${inputId}`).val();
7708        }
7709
7710        const errorSpan = jQuery(`#${errorSpanId}`)[0];
7711        if (inputValidationFx(input)) {
7712            errorSpan.innerHTML = '';
7713            errorSpan.setAttribute('class', 'errorMessageHide');
7714            jQuery(`#${inputId}`).removeClass("errorMessageShow");
7715            return true;
7716        } else {
7717            displayErrorMessage(errorSpan);
7718            jQuery(`#${inputId}`).addClass("errorMessageShow");
7719            return false;
7720        }
7721    }
7722}
7723
7724function emailValidation(emailString) {
7725    const emailRegex = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
7726    const isEmpty = emailString === '';
7727    const isValid = emailRegex.test(emailString);
7728    return !isEmpty && isValid;
7729}
7730
7731function industryValidation(industry) {
7732    return industry !== 'Select industry...';
7733}
7734
7735function optionalPhoneValidation(phone) {
7736    const regex = /^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$/;
7737    const isEmpty = phone === '';
7738    const isValid = regex.test(phone);
7739    return isEmpty || isValid;
7740}
7741
7742function nameValidation(name) {
7743    const regex = /^[^- '](?=(?!.*[- '][- '.]))(?=(?!.*[.][-'.]))[A-Za-z0-9- '.]{2,}$/;
7744    const isEmpty = name === '';
7745    const isValid = regex.test(name);
7746    return isEmpty || isValid;
7747}
7748
7749function requiredFieldValidation(field) {
7750    const isEmpty = field === '';
7751    return !isEmpty;
7752}
7753
7754function requiredDropdownValidation(selectedOption) {
7755    return selectedOption !== 'Choose';
7756}
7757
7758function requiredStateValidation(state) {
7759    const regex = /^[a-zA-Z]{2}$/;
7760    const isEmpty = state === '';
7761    const isValid = regex.test(state);
7762    return !isEmpty && isValid;
7763}
7764
7765function requiredZipCodeValidation(zip) {
7766    const regex = /^\d{5}
7766$/;
7767    const isEmpty = zip === '';
7768    const isValid = regex.test(zip);
7769    return !isEmpty && isValid;
7770}
7771
7772const validateEmail = generateValidator(emailValidation);
7773const validateIndustry = generateValidator(industryValidation);
7774const validateOptionalPhone = generateValidator(optionalPhoneValidation);
7775const validateRequiredField = generateValidator(requiredFieldValidation);
7776const validateRequiredDropdown = generateValidator(requiredDropdownValidation);
7777const validateRequiredState = generateValidator(requiredStateValidation);
7778const validateRequiredZipCode = generateValidator(requiredZipCodeValidation);
7779const validateName = generateValidator(nameValidation);
7780// formats input with numbers only, removing other non-numeric characters
7781function formatNumericInput(input) {
7782    return input.replace(/\D/g, '');
7783}
7784
7785// formats input with alphabetic characters only, removing other non-alpha characters
7786function formatAlphabeticInput(input) {
7787    return input.replace(/[^a-zA-Z]/g, '');
7788}
7789
7790// formats phone number as "(xxx) xxx-xxxx"
7791function formatPhoneInput(input) {
7792    input = input.replace(/\D/g, '');
7793    const size = input.length;
7794    if (size > 0) {
7795        input = '(' + input;
7796    }
7797    if (size > 3) {
7798        input = input.slice(0,4) + ') ' + input.slice(4,11);
7799    }
7800    if (size > 6) {
7801        input = input.slice(0,9) + '-' + input.slice(9);
7802    }
7803    return input;
7804}
7805
7806// formats dollar amount with commas, e.g. "$xxx,xxx,xxx"
7807function formatDollarAmountInput(input) {
7808    input = input.replace(/\D/g, '');
7809    const size = input.length;
7810    if (size > 0) {
7811        input = '$' + input;
7812    }
7813    return input.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
7814}
7815
7816// =============================================================================
7817// youtube-one-trust.js
7818// =============================================================================
7819// PURPOSE: Shared utility layer for OneTrust cookie consent and YouTube IFrame
7820// API bootstrapping. This file is intentionally framework-free — no classes,
7821// no imports. It exposes plain functions that every YouTube/Wistia player class
7822// in this bundle calls directly.
7823//
7824// WHY IT EXISTS AS PLAIN FUNCTIONS (not a class):
7825// AEM concatenates all clientlib JS files into a single flat bundle with no
7826// module system. Functions declared with the `function` keyword are hoisted to
7827// the top of the bundle scope, making them available to every other file
7828// regardless of load order. Class expressions and `const`/`let` are NOT hoisted
7829// and would cause ReferenceErrors if any class file happened to be concatenated
7830// before this file. The js.txt ordering guarantees this file loads first, but
7831// the function-declaration pattern provides an extra safety net.
7832//
7833// RELATIONSHIP TO OTHER FILES:
7834//   - YouTubePlayerBase.js calls _wireOneTrustAccepted / _wireOneTrustClosed
7835//     which in turn call setupOneTrustAcceptListener / setupOneTrustCloseListener
7836//   - WistiaVideoPlayerFallback.js and WistiaVideoBannerFallback.js call
7837//     showOneTrustModal, setupOneTrustAcceptListener, setupOneTrustCloseListener
7838//     directly when the Wistia player falls back to YouTube without consent
7839//   - WistiaFallback.js calls loadYouTubeApi to bootstrap the YouTube IFrame API
7840// =============================================================================
7841
7842
7843// -----------------------------------------------------------------------------
7844// COOKIE UTILITIES
7845// These functions read and parse the OneTrust consent cookie to determine
7846// whether the user has granted targeting/advertising consent (group C0004).
7847// OneTrust sets a cookie named 'OptanonConsent' whose value is a URL-encoded
7848// string containing a 'groups' field, e.g. "groups=C0001:1,C0003:0,C0004:1".
7849// -----------------------------------------------------------------------------
7850
7851// Reads a named field from within a named cookie value.
7852// Returns the field value as a string, or null if the cookie or field is absent.
7853function getCookieOneTrustCookie(name, field) {
7854    const value = `; ${document.cookie}`;
7855    const parts = value.split(`; ${name}=`);
7856    let cookieValue;
7857    if (parts.length >= 2) {
7858            cookieValue = parts.pop().split(';').shift();
7859    }
7860    if (!cookieValue) return null;
7861    let decodedCookieValue = cookieValue;
7862    try {
7863        decodedCookieValue = decodeURIComponent(cookieValue);
7864    } catch (e) {
7865        console.error(`[OneTrust] Error decoding cookie value for ${name}:`, e);
7866        logClientError("ERROR", "[OneTrust] Error decoding cookie value for " + name + ": " + (e && e.message ? e.message : 
7866String(e)), "youtube-one-trust.js");
7867        decodedCookieValue = cookieValue;
7868    }
7869    const params = new URLSearchParams(decodedCookieValue);
7870    return params.get(field);
7871}
7872
7873// Returns true if the user has explicitly enabled targeting cookies (group C0004).
7874// This is the primary consent signal used when a player is first initialised.
7875// "C0004:1" means the group is enabled; "C0004:0" means disabled or not set.
7876function hasOneTrustTargetingConsent() {
7877    // Author simulation flag: set via page properties, rendered only when wcmmode=disabled.
7878    // Allows authors on TEST to preview YouTube players without a working OneTrust cookie. Removing for PROD
7879    //const analyticsDiv = document.getElementById('analytics-details');
7880    //if (analyticsDiv && analyticsDiv.getAttribute('data-simulate-youtube-consent') === 'false') {
7881    //    return false;
7882    //}
7883    const groups = getCookieOneTrustCookie('OptanonConsent', 'groups');
7884    if (!groups) return false;
7885    const groupArr = groups.split(',');
7886    const c0004 = groupArr.find(g => g.startsWith('C0004:'));
7887    return c0004 === 'C0004:1';
7888}
7889
7890
7891// -----------------------------------------------------------------------------
7892// LEGACY CONSENT UI HELPERS
7893// These functions support the older procedural videoBannerFunctionality.js and
7894// videoComponentFunctionality.js files. They are NOT used by the new class-based
7895// architecture — new code uses setupOneTrustAcceptListener instead.
7896// Kept here to avoid breaking the legacy path until it is fully removed.
7897// -----------------------------------------------------------------------------
7898
7899// Builds the inline consent message DOM node shown inside the video wrapper
7900// when the user has not yet granted consent.
7901function createOneTrustMessageConsent() {
7902    let messageConsentContainer = document.createElement('div');
7903    messageConsentContainer.className = 'one-trust-message-consent_container';
7904    let messageConsentText = document.createElement('p');
7905    messageConsentText.className = 'one-trust-message-consent__text';
7906    messageConsentText.innerHTML = 'You have certain cookies disabled on the Vanguard site. <br/>' +
7907        'In order to watch this video, you must agree to the use of cookies provided by YouTube. <br/>' +
7908        'Click <button class="video-one-trust-button-ack" title="Accept YouTube cookies" aria-label="Accept YouTube' +
7909        ' cookies"><span class="video-one-trust-label">here</span></button> ' +
7910        'to permit these cookies and watch the video. <br/>';
7911    messageConsentContainer.appendChild(messageConsentText);
7912    return messageConsentContainer;
7913}
7914
7915// Injects a OneTrust disclosure overlay into wrapperElement for a given player index.
7916// Called by the legacy procedural code when a player is rendered without consent.
7917function createOneTrustDisclosure(wrapperElement, index) {
7918    let oneTrustContainer = document.createElement('div');
7919    oneTrustContainer.className = 'video-one-trust-container';
7920    oneTrustContainer.appendChild(createOneTrustMessageConsent());
7921    wrapperElement.appendChild(oneTrustContainer);
7922}
7923
7924// Removes the consent overlay from a video container after consent is granted.
7925// Uses jQuery (legacy code dependency) to find and clean up the overlay elements.
7926function removeOverlayConsent(videoContainer) {
7927    videoContainer.find('.youtube-wrapper').removeClass('video-consent--overlay');
7928    videoContainer.find('.video-one-trust-container').remove();
7929}
7930
7931// Wires the close/dismiss buttons of the OneTrust modal for the legacy procedural
7932// players. Cleans up player iframes and UI elements when the user dismisses
7933// without granting consent, and removes the YouTubeCookieAccepted localStorage key.
7934function oneTrustCloseMessageClicked(playerElementsBanner, playerElementsVideo) {
7935    $('.hide-one-trust-modal, .close-one-trust-modal').click(function () {
7936        if (playerElementsBanner) {
7937            playerElementsBanner.forEach(function (element) {
7938                let videoWrapper = $(element).closest('.youtube-wrapper');
7939                if (videoWrapper.find('.youtube-player')) {
7940                    videoWrapper.find('.youtube-player').remove();
7941                }
7942                if (videoWrapper.find('.player-overlay')) {
7943                    videoWrapper.find('.player-overlay').remove();
7944                }
7945            });
7946        }
7947        if (playerElementsVideo) {
7948            playerElementsVideo.forEach(function (element) {
7949                let videoPlayerContainer = $(element).closest('.main-youtube-container');
7950                videoPlayerContainer.find('.video-banner .play-pause-btn').remove();
7951                videoPlayerContainer.find('.video-transcript-container').remove();
7952                videoPlayerContainer.find('.video-transcript-modal').remove();
7953            });
7954        }
7955        if (localStorage.getItem('YouTubeCookieAccepted')) {
7956            localStorage.removeItem('YouTubeCookieAccepted');
7957        }
7958        $(this).closest('.one-trust-modal').removeClass('show-one-trust-modal');
7959        $(this).closest('.one-trust-modal').attr('aria-hidden', 'true');
7960        document.querySelector('body').style.overflow = 'visible';
7961    });
7962}
7963
7964// Extracts the numeric player index from a player element id.
7965// Player ids follow the pattern "youtube-player-0", "video-youtube-player-1" etc.
7966// The index is always the last segment after the final dash.
7967function getVideoIndexFromId(elementId) {
7968    const parts = elementId.split('-');
7969    return parseInt(parts[parts.length - 1], 10);
7970}
7971
7972
7973// -----------------------------------------------------------------------------
7974// SHARED MODAL STATE AND CALLBACK QUEUES
7975// Used by both the native YouTube player classes and the Wistia fallback classes.
7976//
7977// var is intentional: AEM concatenates all clientlib JS into a single bundle with
7978// no module scope. These variables must be accessible to showOneTrustModal,
7979// setupOneTrustAcceptListener, and setupOneTrustCloseListener across the same
7980// flat script scope.
7981//
7982// CALLBACK QUEUE PATTERN:
7983// Multiple player instances on the same page (e.g. two Wistia fallbacks) may each
7984// call setupOneTrustAcceptListener before the user responds to the modal. Rather
7985// than registering one DOM listener per caller (which risks duplicate handlers),
7986// a single DOM listener is registered once (singleton via the flags in
7987// _oneTrustState). Every caller pushes its callback into the queue. When the user
7988// accepts or closes, ALL queued callbacks are drained atomically. This ensures no
7989// instance is silently skipped because it registered after the first one.
7990// -----------------------------------------------------------------------------
7991var _oneTrustState = {
7992    modalShown: false,      // true once the modal has been displayed
7993    modalClosed: false,     // true once the user explicitly dismissed without accepting
7994    closeListenerSet: false, // true once the DOM close listener has been registered
7995    acceptListenerSet: false, // true once the DOM accept listener has been registered
7996};
7997var _oneTrustAcceptCallbacks = []; // callbacks waiting for user to grant consent
7998var _oneTrustCloseCallbacks = [];  // callbacks waiting for user to dismiss without consent
7999
8000
8001// -----------------------------------------------------------------------------
8002// MODAL CONTROL
8003// -----------------------------------------------------------------------------
8004
8005// Shows the OneTrust consent modal to the user.
8006// Guards against double-display: skips silently if the modal is already visible,
8007// has already been shown, or has already been closed in this page session.
8008// Also skips in AEM Author mode (data-isauthor="true") to avoid blocking authors.
8009function showOneTrustModal() {
8010    const modal = document.querySelector('#oneTrustModal');
8011    if (!modal || modal.getAttribute('data-isauthor') !== 'false') {
8012        return;
8013    }
8014    if (modal.classList.contains('show-one-trust-modal')) {
8015        _oneTrustState.modalShown = true;
8016        return;
8017    }
8018    if (_oneTrustState.modalShown || _oneTrustState.modalClosed) {
8019        return;
8020    }
8021    _oneTrustState.modalShown = true;
8022    modal.classList.add('show-one-trust-modal');
8023    modal.setAttribute('aria-hidden', 'false');
8024    document.body.style.overflow = 'hidden'; // prevent background scrolling while modal is open
8025}
8026
8027
8028// -----------------------------------------------------------------------------
8029// CONSENT LISTENER SETUP
8030// -----------------------------------------------------------------------------
8031
8032// Registers a callback to be invoked when the user clicks the accept button on
8033// the OneTrust modal. Safe to call from multiple player instances — only one DOM
8034// listener is ever attached (singleton), but all callbacks are queued and drained
8035// together when the user accepts.
8036//
8037// The acceptListenerSet flag NEVER resets to false after firing — the DOM listener
8038// stays permanently attached so future instances created after an acceptance event
8039// (e.g. lazy-loaded components) can still queue callbacks for the next consent flow.
8040function setupOneTrustAcceptListener(onAccept) {
8041    // Queue the callback regardless — all queued callbacks are drained together when the user
8042    // accepts, ensuring every player instance on the page receives the consent signal.
8043    _oneTrustAcceptCallbacks.push(onAccept);
8044
8045    // Only one DOM listener is ever registered (singleton). Subsequent callers just queue above.
8046    if (_oneTrustState.acceptListenerSet) {
8047        return;
8048    }
8049    _oneTrustState.acceptListenerSet = true;
8050
8051    const button = document.querySelector('#oneTrustCookieAcceptance');
8052    if (!button) {
8053        return;
8054    }
8055    button.addEventListener('click', function(e) {
8056        e.preventDefault();
8057        // Persist consent in localStorage so subsequent page loads and Intersection Observer
8058        // checks can verify consent without re-reading the OneTrust cookie every time.
8059        localStorage.setItem('YouTubeCookieAccepted', 'true');
8060        const modal = button.closest('.one-trust-modal');
8061        if (modal) {
8062             modal.classList.remove('show-one-trust-modal');
8063             modal.setAttribute('aria-hidden', 'true');
8064        }
8065        document.body.style.overflow = 'visible';
8066        window.dataLayer = window.dataLayer || [];
8067        window.dataLayer.push({
8068            'event': 'callToAction',
8069            'callToAction': {
8070                'location': `OneTrustModal: ${document.title.split('|').pop().trim()}`,
8071                'ctaName': 'Accept YouTube Cookies',
8072                'ctaType': 'custom button',
8073            },
8074        });
8075        // Drain atomically: splice clears the queue before iterating so any callbacks registered
8076        // during the drain are held for the next acceptance event rather than called twice.
8077        _oneTrustAcceptCallbacks.splice(0).forEach(function(fn) { fn(); });
8078        // Flag intentionally stays true — the listener remains for any future instances that may
8079        // be created after this acceptance (e.g. lazy-loaded components).
8080    });
8081}
8082
8083// Registers a callback to be invoked when the user dismisses the OneTrust modal
8084// without accepting. onClose may be null — callers that only need the modal closed
8085// without any custom teardown can pass null and this function still handles the
8086// singleton DOM listener registration.
8087//
8088// Mirrors the same singleton + queue pattern as setupOneTrustAcceptListener.
8089function setupOneTrustCloseListener(onClose) {
8090    // Queue the callback if provided (may be null for callers that only need the modal closed).
8091    if (onClose) _oneTrustCloseCallbacks.push(onClose);
8092
8093    // Only one set of DOM listeners is ever registered (singleton). Subsequent callers just queue above.
8094    if (_oneTrustState.closeListenerSet) {
8095        return;
8096    }
8097    _oneTrustState.closeListenerSet = true;
8098    document.querySelectorAll('.hide-one-trust-modal, .close-one-trust-modal').forEach(function(btn) {
8099        btn.addEventListener('click', function() {
8100            _oneTrustState.modalClosed = true;
8101            // Remove the localStorage key so consent is not falsely assumed on next page load.
8102            localStorage.removeItem('YouTubeCookieAccepted');
8103            btn.closest('.one-trust-modal') && btn.closest('.one-trust-modal').classList.remove('show-one-trust-modal');
8104            btn.closest('.one-trust-modal') && btn.closest('.one-trust-modal').setAttribute('aria-hidden', 'true');
8105            document.body.style.overflow = 'visible';
8106            window.dataLayer = window.dataLayer || [];
8107            window.dataLayer.push({
8108                'event': 'callToAction',
8109                'callToAction': {
8110                    'location': `OneTrustModal: ${document.title.split('|').pop().trim()}`,
8111                    'ctaName': 'Declined YouTube Cookies',
8112                    'ctaType': 'custom button',
8113                },
8114            });
8115            // Drain atomically — same pattern as the accept listener.
8116            _oneTrustCloseCallbacks.splice(0).forEach(function(fn) { fn(); });
8117            // Flag intentionally stays true — listeners remain for future instances.
8118        });
8119    });
8120}
8121
8122
8123// -----------------------------------------------------------------------------
8124// YOUTUBE IFRAME API BOOTSTRAPPING
8125// -----------------------------------------------------------------------------
8126
8127// Ensures the YouTube IFrame API script is loaded and calls onReady when the API
8128// is available. Safe to call multiple times — idempotent by design.
8129//
8130// HOW THE YOUTUBE IFRAME API WORKS:
8131// YouTube requires a <script src="https://www.youtube.com/iframe_api"> tag in the
8132// page. Once loaded, the API calls window.onYouTubeIframeAPIReady(). After that,
8133// new YT.Player(...) can be called to create player instances.
8134//
8135// HOW THIS FUNCTION HANDLES MULTIPLE CALLERS:
8136// Each Wistia fallback instance (and potentially other components) calls
8137// loadYouTubeApi independently. To avoid injecting the script tag multiple times
8138// and to support multiple onReady callbacks, this function:
8139//   1. If YT.Player already exists, calls onReady immediately.
8140//   2. Otherwise, chains the new onReady callback onto any existing
8141//      window.onYouTubeIframeAPIReady handler (preserving previous callers).
8142//   3. Only injects the <script> tag if it is not already in the DOM.
8143function loadYouTubeApi(onReady) {
8144    if (window.YT && window.YT.Player) {
8145        onReady();
8146        return;
8147    }
8148    const prev = window.onYouTubeIframeAPIReady;
8149    window.onYouTubeIframeAPIReady = function () {
8150        onReady();
8151        if (prev) prev(); // preserve any previously registered onYouTubeIframeAPIReady handler
8152    };
8153    if (!document.querySelector('script[src*="youtube.com/iframe_api"]')) {
8154        const tag = document.createElement('script');
8155        tag.src = 'https://www.youtube.com/iframe_api';
8156        document.head.appendChild(tag);
8157    } else {
8158        console.debug('[OneTrust] loadYouTubeApi: YT API script already in DOM, waiting for callback');
8159    }
8160}
8161
8162// =============================================================================
8163// YouTubePlayerBase.js
8164// =============================================================================
8165// PURPOSE: Abstract base class for all YouTube player implementations on the site.
8166// Provides the shared infrastructure that every player type needs: consent state,
8167// player instance storage, banner visibility helpers, and OneTrust wiring.
8168//
8169// CLASS HIERARCHY (read this top-to-bottom to understand the full picture):
8170//
8171//   YouTubePlayerBase          ← YOU ARE HERE — shared infrastructure
8172//     ├── VideoBannerPlayer    ← autoplay muted background banners
8173//     │     └── WistiaVideoBannerFallback  ← Wistia → YouTube fallback (banner mode)
8174//     └── VideoComponentPlayer ← click-to-play inline video with analytics
8175//           └── WistiaVideoPlayerFallback  ← Wistia → YouTube fallback (player mode)
8176//
8177// CONSENT MODEL:
8178// YouTube cookies require explicit user consent under GDPR/CCPA (OneTrust group
8179// C0004). Players must not create YT.Player instances until consent is confirmed.
8180// Consent is tracked in two ways:
8181//   1. Constructor-injected `hasConsent` flag (read from OneTrust cookie at init time)
8182//   2. 'YouTubeCookieAccepted' localStorage key (set when user clicks Accept on the modal)
8183// _hasConsent() checks both, ensuring late-granted consent is always picked up.
8184//
8185// PROTECTED METHOD CONVENTION:
8186// Methods prefixed with _ are "protected" — they are intended to be called by
8187// subclasses only, not by external code. JavaScript does not enforce this, but
8188// the convention signals intent. Private methods (using #name syntax) are truly
8189// private and cannot be accessed outside this class at all.
8190//
8191// HOW TO ADD A NEW PLAYER TYPE:
8192// 1. Create a new class that extends YouTubePlayerBase (or one of its subclasses).
8193// 2. Implement _buildPlayerId(index), _initElement(element, index), and
8194//    _onConsentGranted() — these are declared abstract via thrown errors below.
8195// 3. Call super(playerElements, hasConsent) from your constructor.
8196// 4. Call this.init() from the entry point that creates your player.
8197// =============================================================================
8198
8199class YouTubePlayerBase {
8200    // Private fields — inaccessible outside this class, even to subclasses.
8201    // Subclasses must use the provided protected accessors (_hasConsent, _getPlayer, etc.)
8202    #hasConsent;       // boolean: was consent granted when this instance was constructed?
8203    #playerElements;   // HTMLElement[]: the DOM elements that become YouTube player containers
8204    #players = [];     // YT.Player[]: indexed player instances, populated as players are created
8205
8206    // playerElements: NodeList or Array of DOM elements to initialise as YouTube players.
8207    //                 Pass an empty array [] for Wistia fallback classes that manage their
8208    //                 own element — they call _registerPlayer / _createYTPlayer directly.
8209    // hasConsent:     boolean — whether the user has consented at construction time.
8210    //                 Use hasOneTrustTargetingConsent() || localStorage check in the caller.
8211    constructor(playerElements, hasConsent) {
8212        this.#playerElements = playerElements;
8213        this.#hasConsent = hasConsent;
8214    }
8215
8216    // --- Public getters ---
8217    // playerElements and players are exposed read-only to subclasses and external
8218    // initialisation code that needs to iterate over them.
8219
8220    get playerElements() { return this.#playerElements; }
8221    get players() { return this.#players; }
8222
8223    // Delegates to _hasConsent() so both the public getter and the method return
8224    // the same authoritative value — avoids silent divergence if consent is granted
8225    // post-construction via localStorage.
8226    get hasConsent() { return this._hasConsent(); }
8227
8228    // --- Lifecycle ---
8229
8230    // Entry point. Call this after constructing a player instance.
8231    // Assigns stable IDs to all player elements, initialises each one, then
8232    // wires up the OneTrust accept/close listeners so the player reacts to
8233    // future consent changes without needing to be reconstructed.
8234    init() {
8235        this.#playerElements.forEach((element, index) => {
8236            element.id = this._buildPlayerId(index);
8237            this._initElement(element, index);
8238        });
8239
8240        this._wireOneTrustAccepted();
8241        this._wireOneTrustClosed();
8242    }
8243
8244    // --- Abstract methods (must be implemented by every subclass) ---
8245    // These throw errors at runtime if a subclass forgets to implement them,
8246    // acting as a lightweight interface contract.
8247
8248    // Returns the string id to assign to the player container element.
8249    // e.g. VideoBannerPlayer returns "youtube-player-0", "youtube-player-1", etc.
8250    _buildPlayerId(index) {
8251        throw new Error('_buildPlayerId must be implemented by subclass');
8252    }
8253
8254    // Called once per element during init(). Subclasses use this to read data
8255    // attributes, create DOM structures (banners, overlays), and create the
8256    // YT.Player if consent is already available.
8257    _initElement(element, index) {
8258        throw new Error('_initElement must be implemented by subclass');
8259    }
8260
8261    // Called when the user grants consent via the OneTrust modal after the
8262    // player was already initialised without consent. Subclasses use this to
8263    // create deferred YT.Player instances and reveal any previously hidden UI.
8264    _onConsentGranted() {
8265        throw new Error('_onConsentGranted must be implemented by subclass');
8266    }
8267
8268    // --- OneTrust wiring ---
8269    // These methods register the player with the shared OneTrust callback queues
8270    // defined in youtube-one-trust.js. They are called automatically by init().
8271    // Subclasses should not need to call these directly.
8272
8273    // Registers this player's consent-granted handler with the shared accept queue.
8274    // When the user clicks Accept on the OneTrust modal, _onConsentGranted() fires.
8275    _wireOneTrustAccepted() {
8276        setupOneTrustAcceptListener(() => {
8277            this._onConsentGranted();
8278        });
8279    }
8280
8281    // Registers this player's consent-denied handler with the shared close queue.
8282    // When the user dismisses the modal without accepting, _onConsentDenied() fires.
8283    // The base implementation is a no-op — subclasses override it if they need
8284    // to tear down UI elements when consent is revoked.
8285    _wireOneTrustClosed() {
8286        setupOneTrustCloseListener(() => {
8287            this._onConsentDenied();
8288        });
8289    }
8290
8291    // Default no-op — subclasses override to clean up UI on consent denial.
8292    _onConsentDenied() {
8293        console.debug(`[YTBase] ${this.constructor.name}: _onConsentDenied (default no-op)`);
8294    }
8295
8296    // --- Banner visibility helpers ---
8297    // Banner elements (thumbnail images shown before/after video plays) are toggled
8298    // by adding/removing the 'banner-hidden' CSS class. This is the single place
8299    // where that toggle lives — do not manipulate banner visibility directly elsewhere.
8300
8301    // Reveals the banner with the given DOM id by removing 'banner-hidden'.
8302    _showBanner(bannerId) {
8303        const banner = document.getElementById(bannerId);
8304        if (banner) banner.classList.remove('banner-hidden');
8305    }
8306
8307    // Hides the banner with the given DOM id by adding 'banner-hidden'.
8308    _hideBanner(bannerId) {
8309        const banner = document.getElementById(bannerId);
8310        if (banner) banner.classList.add('banner-hidden');
8311    }
8312
8313    // --- Player instance storage ---
8314    // YT.Player instances are stored in a private indexed array. Subclasses must
8315    // always use _setPlayer/_getPlayer rather than managing their own player
8316    // references — this keeps the analytics and state logic in subclasses
8317    // aligned with the correct player instance.
8318
8319    // Stores a YT.Player instance at the given index slot.
8320    _setPlayer(index, player) {
8321        this.#players[index] = player;
8322    }
8323
8324    // Retrieves the YT.Player instance at the given index slot.
8325    // Returns undefined if the player has not been created yet.
8326    _getPlayer(index) {
8327        return this.#players[index];
8328    }
8329
8330    // --- Consent check ---
8331    // ALWAYS use this method to check consent — do not read localStorage directly
8332    // or use the hasConsent getter in conditional logic.
8333    _hasConsent() {
8334        const result = this.#hasConsent || localStorage.getItem('YouTubeCookieAccepted') === 'true';
8335        return result;
8336    }
8337}
8338
8339// =============================================================================
8340// VideoBannerPlayer.js
8341// =============================================================================
8342// PURPOSE: Manages autoplay muted background video banners — the full-width
8343// looping videos used as decorative backgrounds in hero sections and the ISG
8344// (Interactive Story Guide) banner. These videos play automatically and silently;
8345// the user can optionally pause/resume them via an overlay button.
8346//
8347// EXTENDS: YouTubePlayerBase (see YouTubePlayerBase.js for base class contract)
8348//
8349// TWO OPERATING MODES — controlled by the isISG flag per player:
8350//
8351//   STANDARD mode (isISG = false):
8352//   - Video autoplays, loops, and is muted.
8353//   - When video approaches 95%, the thumbnail banner is shown and the player
8354//     seeks to 0 — creating a seamless loop without relying on YouTube's own loop
8355//     playerVar (which is unreliable across browsers).
8356//   - IntersectionObserver pauses/resumes when timeline slides scroll in/out of view.
8357//   - visibilitychange handler pauses when the browser tab is hidden.
8358//
8359//   ISG mode (isISG = true):
8360//   - Same autoplay/muted setup but uses a 'playlist' playerVar instead of 'loop'
8361//     for better cross-browser loop compatibility in the ISG context.
8362//   - On first play, the thumbnail banner hides after 3 seconds (longer delay to
8363//     let the video visually establish itself). On subsequent plays, 750ms delay.
8364//   - The ISG end poller pauses the video at 95% and restores the banner, then on
8365//     the next play event seeks to 0 and resumes — implementing a controlled loop.
8366//   - ISG players are never observed by the IntersectionObserver or visibilitychange
8367//     handler — they are managed by the ISG component's own scroll logic.
8368//
8369// CONSENT FLOW:
8370//   - If consent is available at init time: YT.Player is created immediately.
8371//   - If consent is not available: banners and overlays are created (so the page
8372//     looks correct) but YT.Player creation is deferred. When the user accepts via
8373//     OneTrust, _onConsentGranted() is called and all deferred players are created.
8374//   - If the user denies/closes: _onConsentDenied() removes the player iframes and
8375//     overlays so no YouTube content is rendered without consent.
8376//
8377// SUBCLASSES:
8378//   WistiaVideoBannerFallback extends this class to handle the case where a Wistia
8379//   video falls back to YouTube. It manages its own element (not a querySelectorAll
8380//   list) and calls _initPlayerState / _createBannerPlayer directly.
8381// =============================================================================
8382class VideoBannerPlayer extends YouTubePlayerBase {
8383    // Total number of player slots managed by this instance. Used to know when
8384    // all players have initialised so we can start the IntersectionObserver.
8385    #totalPlayers = 0;
8386    #nonIsgCount = 0;       // counts players that will fire #onReady (ISG players do not)
8387    #initializedCount = 0; // counts how many players have fired their onReady event
8388
8389    // Per-player state arrays — indexed by the same index used in YouTubePlayerBase#players.
8390    // Using arrays (not scalars) is critical: a shared scalar would cause cross-player
8391    // state corruption on pages with multiple banner instances.
8392    #isISG = [];                  // boolean: is this player in ISG mode?
8393    #isgFirstIteration = [];      // boolean: has this ISG player played for the first time?
8394    #reachedVideoEnd = [];        // boolean: has the ISG poller flagged near-end?
8395    #manualPause = [];            // boolean: did the user explicitly pause this player?
8396    #buttonOverlayClicked = [];   // boolean: was the overlay button just clicked? (icon sync)
8397    #playerStates = [];           // YT.PlayerState: last known state for each player
8398    #applyBannerInterval = [];          // setInterval handles for the ISG end poller, per player
8399    #standardEndPollerInterval = [];    // setInterval handles for the standard end poller, per player
8400
8401    #intersectionObserver; // shared IntersectionObserver for timeline slide autoplay
8402
8403    // YouTube IFrame API playerVars for standard banner mode.
8404    // All params are static across all banner instances so they live as a class-level constant.
8405    // Base playerVars shared by both standard and ISG banner modes.
8406    // 'loop' and 'playlist' are intentionally absent — they are added dynamically
8407    // in #buildPlayerVars/#buildISGPlayerVars with the video-specific id, because
8408    // YouTube's loop playerVar only suppresses the end screen when playlist is also
8409    // set to the same video id. Without playlist, YouTube renders its branding/end
8410    // screen for a moment before the loop fires, which is visible to the user.
8411    static #BASE_PLAYER_VARS = {
8412        'modestbranding': 1,   // hide YouTube logo in the control bar
8413        'rel': 0,              // don't show related videos at end
8414        'showinfo': 0,         // hide video title overlay
8415        'controls': 0,         // hide player controls (we provide our own overlay button)
8416        'disablekb': 1,        // disable keyboard shortcuts (prevent focus-trap issues)
8417        'fs': 0,               // disable fullscreen button
8418        'iv_load_policy': 3,   // hide video annotations
8419        'cc_load_policy': 0,   // hide closed captions by default
8420        'mute': 1,             // muted — required for autoplay in most browsers
8421        'autohide': 1,
8422        'autoplay': 1,         // autoplay on load
8423        'loop': 1,
8424        'origin': window.location.origin // required for postMessage security — must be a real URL
8425    };
8426
8427    constructor(playerElements, hasConsent) {
8428        super(playerElements, hasConsent);
8429        this.#totalPlayers = playerElements.length;
8430        // Bind the IntersectionObserver callback to this instance so it can access
8431        // private fields and methods when called by the browser.
8432        this.#intersectionObserver = new IntersectionObserver(
8433            this.#onIntersection.bind(this),
8434            { root: null, rootMargin: '0px', threshold: 0.05 }
8435        );
8436    }
8437
8438    // Returns the DOM id to assign to each player container element.
8439    // The base class calls this during init() before _initElement.
8440    _buildPlayerId(index) {
8441        return `youtube-player-${index}`;
8442    }
8443
8444    // Called once per element by the base class init() loop.
8445    // Reads the element's data attributes, creates the thumbnail banner and overlay
8446    // button, initialises per-player state, then creates the YT.Player if consent
8447    // is already available (or defers if not).
8448    _initElement(element, index) {
8449        const thumbnailUrl = element.getAttribute('data-background-url');
8450        const isISG = element.closest('.cmp-isg-banner-media-container') !== null;
8451        this.#createBanner(element.parentNode, index, thumbnailUrl);
8452        this.#createOverlay(element.parentNode, index);
8453        this._initPlayerState(index, isISG);
8454
8455        if (this._hasConsent()) {
8456            const videoId = element.getAttribute('data-video-id');
8457            this.#createPlayer(index, videoId, element.id);
8458        } else {
8459            console.debug(`[VideoBanner] index=${index}: no consent, deferring player creation`);
8460        }
8461    }
8462
8463    // Initialises the per-player state arrays for a given index and ISG mode.
8464    // Called by _initElement for native banner players, and called directly by
8465    // WistiaVideoBannerFallback which manages its own element outside the init() loop.
8466    _initPlayerState(index, isISG) {
8467        this.#isISG[index] = isISG;
8468        this.#isgFirstIteration[index] = true;
8469        this.#reachedVideoEnd[index] = false;
8470        this.#manualPause[index] = false;
8471        this.#buttonOverlayClicked[index] = false;
8472        // Ensure #totalPlayers covers Wistia fallback instances that pass their own index
8473        // rather than being counted in the constructor's playerElements.length.
8474        this.#totalPlayers = Math.max(this.#totalPlayers, index + 1);
8475        if (!isISG) this.#nonIsgCount++;
8476    }
8477
8478    // Protected hook for WistiaVideoBannerFallback: creates a YT.Player for a
8479    // specific index/videoId/containerId without going through the init() loop.
8480    // The native path uses #createPlayer directly; this method exposes it to subclasses.
8481    _createBannerPlayer(index, videoId, containerId) {
8482        this.#createPlayer(index, videoId, containerId);
8483    }
8484
8485    // Called by YouTubePlayerBase when the user grants consent via OneTrust modal.
8486    // Creates all YT.Player instances that were deferred during init() and removes
8487    // the consent overlay UI.
8488    _onConsentGranted() {
8489        this.playerElements.forEach((element, index) => {
8490            const videoId = element.getAttribute('data-video-id');
8491            const playerId = element.getAttribute('id');
8492            const videoContainer = element.closest('.youtube-container');
8493            this.#createPlayer(index, videoId, playerId);
8494            this.#removeConsentOverlay(videoContainer);
8495        });
8496    }
8497
8498    // Called by YouTubePlayerBase when the user closes the OneTrust modal without
8499    // accepting. Removes any player iframes and overlays that were created while
8500    // waiting for consent so no YouTube content is visible without consent.
8501    _onConsentDenied() {
8502        this.playerElements.forEach((element) => {
8503            const wrapper = element.closest('.youtube-wrapper');
8504            if (!wrapper) return;
8505            wrapper.querySelector('.youtube-player')?.remove();
8506            wrapper.querySelector('.player-overlay')?.remove();
8507        });
8508    }
8509
8510    // Creates the thumbnail banner div and appends it to wrapperElement.
8511    // The banner is the visible image shown before and after the video plays.
8512    // Its id ('banner-{index}') is used by _showBanner/_hideBanner in the base class.
8513    #createBanner(wrapperElement, index, thumbnailUrl) {
8514        const banner = document.createElement('div');
8515        banner.className = 'video-banner';
8516        banner.id = `banner-${index}`;
8517        banner.style.backgroundImage = `url(${thumbnailUrl})`;
8518        banner.setAttribute('data-player-index', index);
8519        banner.setAttribute('role', 'presentation'); // decorative — screen readers skip it
8520        wrapperElement.appendChild(banner);
8521    }
8522
8523    // Creates the play/pause overlay button and appends it to wrapperElement.
8524    // The overlay is hidden initially ('overlay-hidden') and revealed once the
8525    // player is playing. Both the button and the overlay div itself are clickable
8526    // so the entire video area acts as a toggle target.
8527    #createOverlay(wrapperElement, index) {
8528        const overlay = document.createElement('div');
8529        overlay.className = 'player-overlay overlay-hidden';
8530        overlay.id = `overlay-${index}`;
8531        overlay.setAttribute('data-player-index', index);
8532
8533        const button = document.createElement('button');
8534        button.className = 'play-pause-btn';
8535        button.setAttribute('data-player-index', index);
8536        button.setAttribute('aria-label', 'Pause video');
8537        button.setAttribute('title', 'Pause video');
8538
8539        const icon = document.createElement('div');
8540        icon.className = 'pause-icon'; // starts as pause icon since video autoplays
8541        icon.id = `play-icon-${index}`;
8542
8543        // The same handler is attached to both the button and the overlay div.
8544        // buttonOverlayClicked[index] flags that an icon update is needed in
8545        // #updateButtonIcon, which only runs when this flag is true (to avoid
8546        // unnecessary DOM queries on every state change).
8547        const toggleHandler = (e) => {
8548            e.stopPropagation();
8549            this.#buttonOverlayClicked[index] = true;
8550            this.#togglePlayPause(index);
8551        };
8552
8553        button.appendChild(icon);
8554        button.addEventListener('click', toggleHandler);
8555        overlay.appendChild(button);
8556        overlay.addEventListener('click', toggleHandler);
8557        wrapperElement.appendChild(overlay);
8558    }
8559
8560    // Creates a new YT.Player instance for the given index/videoId/playerId.
8561    // Selects the correct playerVars (standard vs ISG) based on the #isISG flag.
8562    // Stores the player via _setPlayer so the base class array is authoritative.
8563    #createPlayer(index, videoId, playerId) {
8564        const isISG = this.#isISG[index];
8565        const playerVars = isISG
8566            ? this.#buildISGPlayerVars(videoId)
8567            : VideoBannerPlayer.#BASE_PLAYER_VARS;
8568
8569        this._setPlayer(index, new YT.Player(playerId, {
8570            height: '100%',
8571            width: '100%',
8572            videoId,
8573            playerVars,
8574            events: {
8575                // onReady fires when the player iframe is fully loaded and the API is ready.
8576                // For standard banners we set lazy/inert attributes and start observers.
8577                // For ISG banners we skip #onReady — the ISG component controls timing itself.
8578                onReady: (event) => {
8579                    if (!this.#isISG[index]) this.#onReady(event, index);
8580                },
8581                // onStateChange fires on every player state transition (play, pause, end, etc.).
8582                // Standard and ISG banners have different state machines so they route separately.
8583                onStateChange: (event) => {
8584                    if (this.#isISG[index]) {
8585                        this.#handleStateChangeISG(index, event.data);
8586                    } else {
8587                        this.#handleStateChange(index, event.data);
8588                    }
8589                },
8590                // onError fires when YouTube cannot play the video (private, removed, quota, etc.).
8591                // Restore the banner so something is visible rather than a broken black box.
8592                onError: (event) => {
8593                    logClientError("ERROR", "YouTube banner player error: code=" + event.data, "VideoBannerPlayer.js");
8594                    this._showBanner(`banner-${index}`);
8595                },
8596            },
8597        }));
8598    }
8599
8600    // Builds ISG-specific playerVars. Same as standard but omits loop:1 because the
8601    // ISG end poller (#startEndPollerISG) manages the loop itself via seekTo(0).
8602    #buildISGPlayerVars(videoId) {
8603        return {
8604               'modestbranding': 1, // Hide YouTube logo
8605               'rel': 0, // Disable related videos at the end
8606               'showinfo': 0, // Hide video title
8607               'controls': 0, // Hide player controls
8608               'disablekb': 1, // Disable keyboard controls
8609               'fs': 0, // Disable fullscreen button
8610               'iv_load_policy': 3, // Hide video annotations
8611               'cc_load_policy': 0, // Hide closed captions
8612               'mute': 1, // Mute the video
8613               'autohide': 1,
8614               'autoplay': 1,
8615               'playlist': videoId,
8616               'origin': window.location.origin
8617        };
8618    }
8619
8620    // Called when a standard (non-ISG) player fires onReady.
8621    // Sets accessibility attributes on the iframe:
8622    //   - 'loading=lazy': hints to the browser to defer non-visible resource loads.
8623    //   - 'inert': makes the iframe non-interactive when hidden behind the banner,
8624    //     preventing keyboard users from accidentally tabbing into a muted autoplay video.
8625    // Tracks initialisation count — once all players are ready, starts the
8626    // IntersectionObserver and the off-screen visibility handler.
8627    #onReady(event, index) {
8628        const iframe = event.target.getIframe();
8629        iframe.setAttribute('loading', 'lazy');
8630        iframe.setAttribute('inert', '');
8631        this.#initializedCount++;
8632        if (this.#nonIsgCount > 0 && this.#initializedCount === this.#nonIsgCount) {
8633            console.debug('[VideoBanner] all players ready — starting IntersectionObserver and off-screen handler');
8634            this.#observeTimelineSlides();
8635            this.#handleOffScreen();
8636        }
8637    }
8638
8639    // --- State machine: standard banner ---
8640
8641    // Handles YouTube player state changes for standard (non-ISG) banner players.
8642    // State transitions:
8643    //   PLAYING → hide banner after 400ms delay (gives video time to render a frame
8644    //             before the thumbnail disappears), start end poller.
8645    //   ENDED   → autoplay again after 100ms (YouTube fires ENDED briefly at loop point).
8646    //   PAUSED/BUFFERING → wait 2500ms then restore banner if still paused
8647    //             (the delay distinguishes a momentary buffer from a true pause).
8648    #handleStateChange(index, newState) {
8649        const stateName = this.#stateName(newState);
8650        this.#playerStates[index] = newState;
8651        this.#updateButtonIcon(index)
8652
8653        if (newState === YT.PlayerState.PLAYING) {
8654            setTimeout(() => this._hideBanner(`banner-${index}`), 400);
8655            this.#startEndPoller(index);
8656        } else if (newState === YT.PlayerState.ENDED) {
8657            this.#clearStandardEndPoller(index);
8658            this._showBanner(`banner-${index}`);
8659            setTimeout(() => this.#autoPlay(index), 100);
8660        } else if (newState === YT.PlayerState.BUFFERING || newState === YT.PlayerState.PAUSED) {
8661            this.#clearStandardEndPoller(index);
8662            setTimeout(() => {
8663                const player = this._getPlayer(index);
8664                if (!player || player.getPlayerState() === YT.PlayerState.PAUSED) {
8665                    this._showBanner(`banner-${index}`);
8666                }
8667            }, 2500);
8668        }
8669    }
8670
8671    // --- State machine: ISG banner ---
8672
8673    // Handles YouTube player state changes for ISG banner players.
8674    // ISG mode has more complex loop semantics than standard banners:
8675    //
8676    //   PLAYING → hide banner (3000ms on first iteration, 750ms after), start ISG end poller.
8677    //   PAUSED  → if #reachedVideoEnd is true, the poller paused us at 95% —
8678    //             seek to 0 and play again to loop seamlessly.
8679    //             If paused unexpectedly (buffering, API glitch) and not manually paused,
8680    //             auto-resume. If manually paused, do nothing.
8681    //   ENDED   → YouTube reports ENDED at the true end — seek to 0 and replay.
8682    //
8683    // The updateButtonIcon call syncs the play/pause icon in the overlay button
8684    // whenever the player state changes — but only if the user recently clicked
8685    // the button (buttonOverlayClicked flag prevents unnecessary DOM queries).
8686    #handleStateChangeISG(index, newState) {
8687        const stateName = this.#stateName(newState);
8688        this.#playerStates[index] = newState;
8689        this.#updateButtonIcon(index);
8690
8691        if (newState === YT.PlayerState.PLAYING) {
8692            const delay = this.#isgFirstIteration[index] ? 3000 : 750;
8693            this.#isgFirstIteration[index] = false;
8694            setTimeout(() => this._hideBanner(`banner-${index}`), delay);
8695            this.#startEndPollerISG(index);
8696        } else if (newState === YT.PlayerState.PAUSED) {
8697            this.#clearEndPoller(index);
8698            this._showBanner(`banner-${index}`);
8699            if (this.#reachedVideoEnd[index]) {
8700                this.#reachedVideoEnd[index] = false;
8701                this._getPlayer(index).seekTo(0);
8702                this._getPlayer(index).playVideo();
8703            } else if (!this.#manualPause[index]) {
8704                this.#autoPlay(index);
8705            }
8706        } else if (newState === YT.PlayerState.ENDED) {
8707            this.#clearEndPoller(index);
8708            this._showBanner(`banner-${index}`);
8709            this.#reachedVideoEnd[index] = false;
8710            this._getPlayer(index).seekTo(0);
8711            this._getPlayer(index).playVideo();
8712        }
8713    }
8714
8715    // Polls playback progress every 50ms for standard (non-ISG) banner players.
8716    // Guards against stacking: if a poller is already running for this index, skips.
8717    // The 0/0 = Infinity case is harmless — seekTo(0) at time=0 is a no-op and the
8718    // banner is already visible at that point.
8719    #startEndPoller(index) {
8720        if (this.#standardEndPollerInterval[index]) return;
8721        this.#standardEndPollerInterval[index] = setInterval(() => {
8722            const player = this._getPlayer(index);
8723            if (!player) { this.#clearStandardEndPoller(index); return; }
8724            const rate = player.getCurrentTime() / player.getDuration();
8725            if (parseFloat(rate.toFixed(2)) > 0.95) {
8726                this.#clearStandardEndPoller(index);
8727                this._showBanner(`banner-${index}`);
8728                player.seekTo(0);
8729            }
8730        }, 50);
8731    }
8732
8733    // Clears and nulls the standard end poller interval for the given index.
8734    #clearStandardEndPoller(index) {
8735        clearInterval(this.#standardEndPollerInterval[index]);
8736        this.#standardEndPollerInterval[index] = null;
8737    }
8738
8739    // Polls playback progress every 350ms for ISG banner players.
8740    // Unlike the standard poller, this does not seek immediately — it sets
8741    // #reachedVideoEnd[index] = true and pauses the video. The state machine in
8742    // #handleStateChangeISG then handles the seek-and-replay on the subsequent
8743    // PAUSED event, ensuring the banner is visible during the transition.
8744    // Guards against multiple intervals: if one is already running, skips.
8745    // Division-by-zero guard: stops polling permanently on zero duration (ISG
8746    // banners do not experience transient zero durations the same way).
8747    #startEndPollerISG(index) {
8748        if (this.#applyBannerInterval[index]) return;
8749        this.#applyBannerInterval[index] = setInterval(() => {
8750            const player = this._getPlayer(index);
8751            if (!player) { this.#clearEndPoller(index); return; }
8752            const state = player.getPlayerState();
8753            if (state !== YT.PlayerState.PLAYING && state !== YT.PlayerState.BUFFERING) {
8754                this.#clearEndPoller(index);
8755                return;
8756            }
8757            const duration = player.getDuration();
8758            if (!duration) { this.#clearEndPoller(index); return; }
8759            const rate = player.getCurrentTime() / duration;
8760            if (parseFloat(rate.toFixed(2)) > 0.95) {
8761                this.#clearEndPoller(index);
8762                this.#reachedVideoEnd[index] = true;
8763                this._showBanner(`banner-${index}`);
8764                setTimeout(() => player.pauseVideo(), 50);
8765            }
8766        }, 350);
8767    }
8768
8769    // Clears and nulls the ISG end poller interval for the given index.
8770    // Must be called whenever the ISG player transitions away from PLAYING,
8771    // otherwise multiple intervals accumulate and fight each other.
8772    #clearEndPoller(index) {
8773        clearInterval(this.#applyBannerInterval[index]);
8774        this.#applyBannerInterval[index] = null;
8775    }
8776
8777    // Handles the user clicking the overlay play/pause button.
8778    // Sets manualPause[index] so the ISG state machine knows not to auto-resume
8779    // when a manual pause event arrives. Clears it again on user-initiated play.
8780    #togglePlayPause(index) {
8781        if (!this._getPlayer(index)) return;
8782        if (this.#playerStates[index] === YT.PlayerState.PLAYING) {
8783            this.#manualPause[index] = true;
8784            this.#pause(index);
8785        } else {
8786            this.#manualPause[index] = false;
8787            this.#autoPlay(index);
8788        }
8789    }
8790
8791    // Calls playVideo() and, for non-ISG players, hides the banner after 600ms
8792    // (slightly longer than the PLAYING→hide delay to account for buffering).
8793    #autoPlay(index) {
8794        const player = this._getPlayer(index);
8795        if (!player) return;
8796        player.playVideo();
8797        if (!this.#isISG[index]) {
8798            setTimeout(() => this._hideBanner(`banner-${index}`), 600);
8799        }
8800    }
8801
8802    // Shows the banner and pauses the player. For non-ISG players a 500ms delay
8803    // before pauseVideo() prevents a brief "pause icon flash" visible to the user
8804    // when the banner slides in. ISG pauses immediately since the banner is shown first.
8805    #pause(index) {
8806        const player = this._getPlayer(index);
8807        if (!player) return;
8808        this._showBanner(`banner-${index}`);
8809        if (!this.#isISG[index]) {
8810            setTimeout(() => player.pauseVideo(), 500);
8811        } else {
8812            player.pauseVideo();
8813        }
8814    }
8815
8816    // Updates the play/pause icon in the overlay button to match the current player state.
8817    // Only runs if buttonOverlayClicked[index] is true — that flag is set when the user
8818    // actually clicks the button, avoiding unnecessary DOM queries on every API state change.
8819    #updateButtonIcon(index) {
8820        if (!this.#buttonOverlayClicked[index]) return;
8821        const button = document.querySelector(`.player-overlay .play-pause-btn[data-player-index="${index}"]`);
8822        if (!button) return;
8823        const icon = button.querySelector('div');
8824        const state = this._getPlayer(index)?.getPlayerState();
8825
8826        if (icon.classList.contains('play-icon') && state === YT.PlayerState.PLAYING) {
8827            icon.classList.remove("play-icon");
8828            icon.classList.add("pause-icon");
8829            button.setAttribute('aria-label', 'Pause video');
8830            this.#buttonOverlayClicked[index] = false;
8831        } else if (icon.classList.contains('pause-icon') && state === YT.PlayerState.PAUSED) {
8832            icon.classList.add("play-icon");
8833            icon.classList.remove("pause-icon");
8834            button.setAttribute('aria-label', 'Play video');
8835            button.setAttribute('title', 'Play video');
8836            this.#buttonOverlayClicked[index] = false;
8837        }
8838    }
8839
8840    // Wires the IntersectionObserver to all .horizontalTimelineSlide elements.
8841    // Timeline slides are carousel-style sections that each contain a banner player.
8842    // When a slide scrolls into the viewport, its player autoplays; when it scrolls
8843    // out, it pauses. Only called once all players have fired onReady.
8844    #observeTimelineSlides() {
8845        const slides = document.querySelectorAll('.horizontalTimelineSlide');
8846        slides.forEach(slide => this.#intersectionObserver.observe(slide));
8847    }
8848
8849    // IntersectionObserver callback — fires when any observed timeline slide enters
8850    // or leaves the viewport. Finds the player associated with the slide by reading
8851    // the player's DOM id and extracting its index via getVideoIndexFromId.
8852    // Consent is re-checked here because the observer can fire after a page refresh
8853    // where the user may have revoked consent.
8854    #onIntersection(entries) {
8855        entries.forEach(entry => {
8856            const slide = entry.target.closest('.horizontalTimelineSlide');
8857            const videoPlayer = slide?.querySelector('.youtube-player');
8858            if (!videoPlayer) return;
8859            const playerIndex = getVideoIndexFromId(videoPlayer.id);
8860            if (entry.isIntersecting && this._hasConsent()) {
8861                this.#autoPlay(playerIndex);
8862            } else {
8863                this.#pause(playerIndex);
8864            }
8865        });
8866    }
8867
8868    // Registers a visibilitychange handler that pauses all players when the browser
8869    // tab is hidden (e.g. user switches tabs) and resumes them when the tab becomes
8870    // visible again — but only if the player is still in the viewport and consent
8871    // is still valid. Skipped entirely for ISG players since ISG manages its own lifecycle.
8872    #handleOffScreen() {
8873        if (this.playerElements.some((_, i) => this.#isISG[i])) return;
8874        document.addEventListener('visibilitychange', () => {
8875            this.players.forEach((player, index) => {
8876                if (!player) return;
8877                if (document.visibilityState === 'hidden') {
8878                    player.pauseVideo();
8879                } else {
8880                    const rect = player.getIframe().getBoundingClientRect();
8881                    const inViewport = rect.top >= 0 && rect.bottom <= window.innerHeight;
8882                    if (inViewport && this._hasConsent()) {
8883                        player.playVideo();
8884                    }
8885                }
8886            });
8887        });
8888    }
8889
8890    // Removes the consent overlay UI that was injected while waiting for consent.
8891    // Called after _onConsentGranted() creates the deferred players.
8892    #removeConsentOverlay(videoContainer) {
8893        videoContainer?.querySelector('.youtube-wrapper')?.classList.remove('video-consent--overlay');
8894        videoContainer?.querySelector('.video-one-trust-container')?.remove();
8895    }
8896
8897    // Converts a YT.PlayerState integer to a human-readable string for debug logging.
8898    #stateName(state) {
8899        const map = { '-1': 'UNSTARTED', 0: 'ENDED', 1: 'PLAYING', 2: 'PAUSED', 3: 'BUFFERING', 5: 'CUED' };
8900        return map[state] ?? state;
8901    }
8902}
8903
8904// =============================================================================
8905// VideoComponentPlayer.js
8906// =============================================================================
8907// PURPOSE: Manages click-to-play inline video players used in editorial content
8908// (article pages, landing pages etc.). Unlike banner players, these are NOT
8909// autoplay — the user must click a play button or the thumbnail banner to start
8910// the video. They also fire Adobe Analytics events at playback milestones.
8911//
8912// EXTENDS: YouTubePlayerBase (see YouTubePlayerBase.js for base class contract)
8913//
8914// USER FLOW:
8915//   1. Page loads. The component renders a thumbnail banner with a play button.
8916//   2. If consent is available, the play button is active and clickable.
8917//   3. User clicks play button or the banner → _triggerPlay() fires.
8918//   4. If no YT.Player exists yet, _createYTPlayer() creates one. When the API
8919//      fires onReady, _onPlayerReady() calls playVideo() and hides the banner.
8920//   5. While playing, analytics milestones are tracked at 25%, 50%, 75%, and
8921//      on video complete.
8922//   6. When the video ends, the banner is restored so the user can replay.
8923//
8924// CONSENT FLOW:
8925//   - If consent is available at init: play buttons are wired immediately.
8926//   - If consent is not available: banners are rendered without play buttons.
8927//     When consent is granted via OneTrust, _onConsentGranted() re-runs the
8928//     banner setup to add the play buttons.
8929//   - If consent is denied: play buttons, transcript links, and transcript modals
8930//     are removed so no YouTube-dependent UI is visible.
8931//
8932// SUBCLASSES:
8933//   WistiaVideoPlayerFallback extends this class to handle the Wistia → YouTube
8934//   fallback in player (non-banner) mode. It bypasses _triggerPlay entirely and
8935//   overrides _onPlayerReady to suppress immediate playback (cued mode).
8936//
8937// ANALYTICS:
8938//   Adobe Analytics events are pushed to window.dataLayer (GTM-compatible).
8939//   Events fired: mediaPlay, media25PercentPlayed, media50PercentPlayed,
8940//   media75PercentPlayed, mediaComplete. Each event fires at most once per play
8941//   session and resets when the video ends.
8942// =============================================================================
8943class VideoComponentPlayer extends YouTubePlayerBase {
8944    // Per-player state arrays — indexed by the same index used in YouTubePlayerBase#players.
8945    #mediaNames = [];          // string[]: the authored media name used as the analytics identifier
8946    #eventFlags = [];          // object[]: per-player flags tracking which analytics events have fired
8947    #analyticsIntervals = [];  // setInterval handles for the playback progress poller, per player
8948
8949    constructor(playerElements, hasConsent) {
8950        super(playerElements, hasConsent);
8951    }
8952
8953    // Returns the DOM id to assign to each player container element.
8954    // The base class calls this during init() before _initElement.
8955    _buildPlayerId(index) {
8956        return `video-youtube-player-${index}`;
8957    }
8958
8959    // Called once per element by the base class init() loop.
8960    // Reads the media name from the data attribute (used for analytics), initialises
8961    // event flags, and sets up the banner with the play button.
8962    _initElement(element, index) {
8963        this.#mediaNames[index] = element.getAttribute('data-video-name');
8964        this.#eventFlags[index] = {};
8965        this.#updateBanner(element.parentNode.querySelector('.video-banner'), index, element.getAttribute('data-video-id'), element.id);
8966    }
8967
8968    // Called when the user grants consent via OneTrust after the player was
8969    // initialised without it. Re-runs #updateBanner for each element to add
8970    // the play buttons that were skipped during the initial render.
8971    _onConsentGranted() {
8972        this.playerElements.forEach((element, index) => {
8973            const videoIndex = getVideoIndexFromId(element.id);
8974            this.#updateBanner(
8975                element.parentNode.querySelector('.video-banner'),
8976                videoIndex,
8977                element.getAttribute('data-video-id'),
8978                element.id
8979            );
8980        });
8981    }
8982
8983    // Called when the user dismisses the OneTrust modal without accepting.
8984    // Removes play buttons, transcript containers, and transcript modals from the
8985    // DOM so no YouTube-dependent UI is rendered without consent.
8986    _onConsentDenied() {
8987        this.playerElements.forEach((element) => {
8988            const container = element.closest('.main-youtube-container');
8989            if (!container) return;
8990            container.querySelector('.video-banner .play-pause-btn')?.remove();
8991            container.querySelector('.video-transcript-container')?.remove();
8992            container.querySelector('.video-transcript-modal')?.remove();
8993            container.querySelector('.youtube-details-container')?.remove();
8994        });
8995    }
8996
8997    // Protected hook used by WistiaVideoPlayerFallback to register itself with the
8998    // analytics system without going through the init() loop. Wistia fallback instances
8999    // manage their own DOM element, so they call this directly from their constructor
9000    // with their instance-specific index and the authored media name.
9001    _registerPlayer(index, mediaName) {
9002        this.#mediaNames[index] = mediaName;
9003        this.#eventFlags[index] = {};
9004    }
9005
9006    // Creates and initialises a YT.Player for the given container, then wires onReady,
9007    // onStateChange, and onError events. When the player is ready, delegates to
9008    // _onPlayerReady(index) — whether playback starts immediately is determined by that
9009    // method. The base implementation plays immediately (safe because _createYTPlayer is
9010    // only called after a user click via _triggerPlay). Subclasses that need cued/no-autoplay
9011    // behaviour should override _onPlayerReady to suppress the playVideo() call.
9012    _createYTPlayer(index, videoId, containerId) {
9013        this._setPlayer(index, new YT.Player(containerId, {
9014            height: '100%',
9015            width: '100%',
9016            videoId,
9017            playerVars: {
9018                rel: 0,              // don't show related videos at end
9019                modestbranding: 1,   // hide YouTube logo in control bar
9020                playsinline: 1,      // play inline on iOS instead of fullscreen
9021                origin: window.location.origin, // required for postMessage security
9022            },
9023            events: {
9024                onReady: () => {
9025                    this._onPlayerReady(index);
9026                },
9027                onStateChange: (event) => this.#handleStateChange(index, event.data),
9028                onError: (event) => {
9029                    console.error(`[VideoComponent] YT player error index=${index} code=${event.data}`);
9030                    logClientError("ERROR", "YouTube player error: code=" + event.data, "VideoComponentPlayer.js");
9031                },
9032            },
9033        }));
9034    }
9035
9036    // Called on user click. If a YT.Player already exists (user clicked play a second time
9037    // or replaying after completion), plays it directly. If no player exists yet, creates
9038    // one via _createYTPlayer — the player will call _onPlayerReady on API ready, which
9039    // then calls playVideo().
9040    // NOTE: WistiaVideoPlayerFallback does NOT call this method — it manages its own
9041    // reveal-and-play flow via #revealAndPlay to correctly handle the Wistia DOM structure.
9042    _triggerPlay(index, videoId, containerId) {
9043        const existing = this._getPlayer(index);
9044        if (existing) {
9045            existing.playVideo();
9046            this._hideBanner(`video-component-banner-${index}`);
9047        } else {
9048            this._createYTPlayer(index, videoId, containerId);
9049        }
9050    }
9051
9052    // Called when YT.Player fires onReady. For the native VideoComponent path, the player
9053    // was just created in response to a user click (via _triggerPlay → _createYTPlayer),
9054    // so it is safe and correct to play immediately.
9055    // WistiaVideoPlayerFallback overrides this method to suppress playVideo() — the
9056    // fallback creates the player up-front in a cued state and only plays on user click.
9057    _onPlayerReady(index) {
9058        const player = this._getPlayer(index);
9059        if (player) player.playVideo();
9060        this._hideBanner(`video-component-banner-${index}`);
9061    }
9062
9063    // Creates or updates the banner element for a player.
9064    // Assigns a stable id and data attribute, then — if consent is available —
9065    // creates the play button and wires click handlers on both the button and
9066    // the banner itself (so clicking the thumbnail also starts playback).
9067    // If consent is not available, the banner is set up without the button;
9068    // _onConsentGranted() will call this again to add the button later.
9069    #updateBanner(banner, index, videoId, playerId) {
9070        if (!banner) return;
9071        banner.id = `video-component-banner-${index}`;
9072        banner.setAttribute('data-player-index', index);
9073
9074        if (!this._hasConsent()) {
9075            return;
9076        }
9077
9078        if (banner.querySelector('.play-pause-btn')) {
9079            return;
9080        }
9081
9082        const button = document.createElement('button');
9083        button.className = 'play-pause-btn';
9084        button.setAttribute('data-player-index', index);
9085        button.setAttribute('aria-label', 'Play video');
9086        button.setAttribute('title', 'Play video');
9087
9088        const playIcon = document.createElement('div');
9089        playIcon.className = 'play-icon';
9090        button.appendChild(playIcon);
9091
9092        const play = (e) => {
9093            e.stopPropagation();
9094            this._triggerPlay(index, videoId, playerId);
9095        };
9096        button.addEventListener('click', play);
9097        banner.addEventListener('click', play);
9098        banner.appendChild(button);
9099    }
9100
9101    // Routes YouTube player state change events to the relevant handlers.
9102    // Only PLAYING and ENDED are acted upon — PAUSED and BUFFERING are intentionally
9103    // ignored since the play-button model does not need to react to transient pauses.
9104    #handleStateChange(index, newState) {
9105        const stateName = this.#stateName(newState);
9106        if (newState === YT.PlayerState.PLAYING) {
9107            this.#onPlay(index);
9108        } else if (newState === YT.PlayerState.ENDED) {
9109            this.#onEnded(index);
9110        }
9111    }
9112
9113    // Called when the player transitions to PLAYING.
9114    // Fires the mediaPlay analytics event (once per play session, guarded by eventFlags).
9115    // Starts the progress interval that polls getCurrentTime/getDuration every 500ms to
9116    // fire milestone events at 25%, 50%, 75%, and detect near-completion (>98%).
9117    #onPlay(index) {
9118        const name = this.#mediaNames[index];
9119        if (!name) return;
9120        if (!this.#eventFlags[index][`${name}_played`]) {
9121            this.#pushAdobeEvent(name, 'mediaPlay');
9122            this.#eventFlags[index][`${name}_played`] = true;
9123        }
9124
9125        // Clear any interval left over from a previous play session before starting a new one.
9126        // Without this, rapid state transitions (buffering → playing) stack up multiple intervals
9127        // that all poll simultaneously and never stop since only one reference is tracked.
9128        this.#clearAnalyticsInterval(index);
9129        this.#analyticsIntervals[index] = setInterval(() => {
9130            const player = this._getPlayer(index);
9131            if (!player) { this.#clearAnalyticsInterval(index); return; }
9132            // Stop polling if the player is no longer playing (ended, paused, or stopped).
9133            // This covers the case where the video ends with rate < 0.98 (e.g. rate jumped
9134            // from 0.97 to ENDED without passing the threshold tick) — without this guard
9135            // the interval runs forever printing milestone logs at a low rate.
9136            const state = player.getPlayerState();
9137            if (state !== YT.PlayerState.PLAYING && state !== YT.PlayerState.BUFFERING) {
9138                this.#clearAnalyticsInterval(index);
9139                return;
9140            }
9141            // getDuration() returns 0 before YouTube metadata has loaded and for live streams.
9142            // Without this guard, 0/0 = Infinity which satisfies all milestone thresholds
9143            // simultaneously on the first tick, firing all three milestone events and
9144            // permanently clearing the interval before any real progress is tracked.
9145            const duration = player.getDuration();
9146            if (!duration) return;
9147            const rate = player.getCurrentTime() / duration;
9148            this.#sendMilestone(name, index, rate);
9149
9150            // when the video reaches rate > 98%
9151            // clear the interval, and show the banner again so the user can replay if desired.
9152            if (rate > 0.98) {
9153                this._showBanner(`video-component-banner-${index}`);
9154                this.#clearAnalyticsInterval(index);
9155            }
9156        }, 500);
9157    }
9158
9159    // Called when the player transitions to ENDED.
9160    // Clears the analytics interval, fires mediaComplete, restores the banner,
9161    // and resets event flags so the next play session tracks milestones fresh.
9162    #onEnded(index) {
9163        this.#clearAnalyticsInterval(index);
9164        this._onVideoEnded(index);
9165        const name = this.#mediaNames[index];
9166        if (!name) return;
9167        this.#pushAdobeEvent(name, 'mediaComplete');
9168        this.#resetFlags(index);
9169    }
9170
9171    // Hook called by #onEnded. The base implementation restores the banner.
9172    // WistiaVideoPlayerFallback overrides this to also hide the YouTube container
9173    // and restore the Wistia banner element.
9174    _onVideoEnded(index) {
9175        this._showBanner(`video-component-banner-${index}`);
9176    }
9177
9178    // Checks the current playback rate (0.0–1.0) against milestone thresholds.
9179    // Uses independent if-blocks (NOT else-if) so multiple milestones can fire
9180    // in the same tick if the rate jumps across a boundary between polls.
9181    // Each milestone fires at most once per play session — guarded by eventFlags.
9182    #sendMilestone(name, index, rate) {
9183        const pct = Math.ceil(rate * 100);
9184        const flags = this.#eventFlags[index];
9185        if (pct >= 25 && !flags[`${name}_25`]) {
9186            this.#pushAdobeEvent(name, 'media25PercentPlayed');
9187            flags[`${name}_25`] = true;
9188        }
9189        if (pct >= 50 && !flags[`${name}_50`]) {
9190            this.#pushAdobeEvent(name, 'media50PercentPlayed');
9191            flags[`${name}_50`] = true;
9192        }
9193        if (pct >= 75 && !flags[`${name}_75`]) {
9194            this.#pushAdobeEvent(name, 'media75PercentPlayed');
9195            flags[`${name}_75`] = true;
9196        }
9197    }
9198
9199    // Pushes an Adobe Analytics event to window.dataLayer (Google Tag Manager format).
9200    // GTM picks this up and forwards it to Adobe Analytics via the configured tag rules.
9201    #pushAdobeEvent(mediaName, event) {
9202        window.dataLayer = window.dataLayer || [];
9203        window.dataLayer.push({
9204            event,
9205            media: { mediaName, mediaType: 'video' },
9206        });
9207    }
9208
9209    // Resets all analytics event flags for a player after the video ends.
9210    // This allows the same milestones to fire again when the user replays the video.
9211    #resetFlags(index) {
9212        const name = this.#mediaNames[index];
9213        this.#eventFlags[index] = {
9214            [`${name}_played`]: false,
9215            [`${name}_25`]: false,
9216            [`${name}_50`]: false,
9217            [`${name}_75`]: false,
9218        };
9219    }
9220
9221    // Clears and nulls the analytics interval for a player.
9222    // Called on video end and when the player is destroyed mid-play.
9223    #clearAnalyticsInterval(index) {
9224        clearInterval(this.#analyticsIntervals[index]);
9225        this.#analyticsIntervals[index] = null;
9226    }
9227
9228    // Converts a YT.PlayerState integer to a human-readable string for debug logging.
9229    #stateName(state) {
9230        const map = { '-1': 'UNSTARTED', 0: 'ENDED', 1: 'PLAYING', 2: 'PAUSED', 3: 'BUFFERING', 5: 'CUED' };
9231        return map[state] ?? state;
9232    }
9233}
9234
9235// =============================================================================
9236// WistiaFallback.js
9237// =============================================================================
9238// PURPOSE: Thin factory class that serves as the entry point for the Wistia → YouTube
9239// fallback system. Called by Player.js when the Wistia player fails to load or
9240// times out (typically after an 8-second timeout).
9241//
9242// WHY A FACTORY (not direct instantiation)?
9243// Player.js should not need to know about consent checking, YouTube API loading,
9244// or which fallback class to use. WistiaFallback encapsulates all of that:
9245//   1. Checks consent (OneTrust cookie + localStorage)
9246//   2. Instantiates the correct fallback class for the rendering mode
9247//   3. Bootstraps the YouTube IFrame API (idempotent — safe to call multiple times)
9248//   4. Calls execute() on the instance once the API is ready
9249//
9250// TWO RENDERING MODES:
9251//   createVideoPlayerFallback → WistiaVideoPlayerFallback (click-to-play player)
9252//   createVideoBannerFallback → WistiaVideoBannerFallback (autoplay muted banner)
9253//
9254// Player.js determines which mode to use based on the component's authored
9255// configuration and calls the appropriate factory method.
9256//
9257// CONSENT SNAPSHOT:
9258// Consent is checked once at factory call time and passed as the 'hasConsent'
9259// constructor argument. The fallback classes also check consent again at execute()
9260// time (via _hasConsent() which re-reads localStorage), providing two opportunities
9261// to pick up consent that was granted between page load and Wistia timeout.
9262//
9263// =============================================================================
9264class WistiaFallback {
9265    static #idCounter = 0;
9266    static nextId() { return ++WistiaFallback.#idCounter; }
9267
9268    // Creates a click-to-play YouTube fallback player for Wistia in standard player mode.
9269    // Returns the WistiaVideoPlayerFallback instance (useful for testing and debugging,
9270    // but callers do not need to hold a reference for normal operation).
9271    static createVideoPlayerFallback(config) {
9272        // Check consent at factory call time. hasOneTrustTargetingConsent() reads the
9273        // OneTrust cookie; the localStorage check covers consent granted on a previous
9274        // page view or via the modal on this page before the Wistia timeout fired.
9275        const hasConsent = hasOneTrustTargetingConsent() ||
9276                           localStorage.getItem('YouTubeCookieAccepted') === 'true';
9277        console.debug(`[WistiaFallback] createVideoPlayerFallback videoId=${config.youtubeVideoId} hasConsent=${hasConsent}`);
9278
9279        const instance = new WistiaVideoPlayerFallback(config, hasConsent);
9280
9281        // loadYouTubeApi is idempotent — if the YT API is already loaded (e.g. because
9282        // another player on the page already triggered it), onReady fires synchronously.
9283        // If not yet loaded, the script tag is injected and onReady fires asynchronously.
9284        loadYouTubeApi(() => {
9285            console.debug('[WistiaFallback] YT API ready — executing WistiaVideoPlayerFallback');
9286            instance.execute();
9287        });
9288
9289        return instance;
9290    }
9291
9292    // Creates an autoplay muted YouTube fallback banner for Wistia in banner mode.
9293    // Returns the WistiaVideoBannerFallback instance.
9294    static createVideoBannerFallback(config) {
9295        const hasConsent = hasOneTrustTargetingConsent() ||
9296                           localStorage.getItem('YouTubeCookieAccepted') === 'true';
9297        console.debug(`[WistiaFallback] createVideoBannerFallback videoId=${config.youtubeVideoId} hasConsent=${hasConsent}`);
9298
9299        const instance = new WistiaVideoBannerFallback(config, hasConsent);
9300
9301        loadYouTubeApi(() => {
9302            console.debug('[WistiaFallback] YT API ready — executing WistiaVideoBannerFallback');
9303            instance.execute();
9304        });
9305
9306        return instance;
9307    }
9308}
9309
9310// =============================================================================
9311// WistiaVideoBannerFallback.js
9312// =============================================================================
9313// PURPOSE: YouTube fallback player for the Wistia video component in banner mode.
9314// When the Wistia player fails to load or times out in a hero/banner context,
9315// Player.js detects the failure and calls WistiaFallback.createVideoBannerFallback()
9316// which instantiates this class.
9317//
9318// EXTENDS: VideoBannerPlayer → YouTubePlayerBase
9319//
9320// BANNER MODE vs PLAYER MODE:
9321// The Wistia component has two distinct rendering modes, controlled by the authored
9322// 'isWistiaPlayerBanner' flag:
9323//   - Player mode  → WistiaVideoPlayerFallback (click-to-play with analytics)
9324//   - Banner mode  → THIS CLASS (autoplay muted background, play/pause overlay button)
9325//
9326// WHY A SEPARATE CLASS (not just VideoBannerPlayer)?
9327// The native VideoBannerPlayer uses the base class init() loop to process a
9328// querySelectorAll list of '.youtube-player' elements. The Wistia fallback is
9329// triggered at runtime for one specific element — there is no list, and the Wistia
9330// DOM structure differs from the native banner component structure. This class
9331// manages its own element and calls base class protected methods
9332// (_initPlayerState, _createBannerPlayer, _getPlayer) directly with its own index.
9333//
9334// KEY DESIGN DECISIONS:
9335//
9336//   1. INSTANCE INDEX (#index):
9337//      Each instance gets a unique index via the auto-incrementing #nextIndex counter.
9338//      Without this, all instances on the same page would collide at index 0:
9339//        - _initPlayerState(0) would overwrite another instance's ISG flags
9340//        - _createBannerPlayer(0) would overwrite another instance's YT.Player
9341//        - div.id = 'banner-0' would create duplicate DOM ids
9342//        - _getPlayer(0) in #wireOverlay would control the wrong player
9343//        - The first instance's end-poller interval would be leaked forever
9344//
9345//   2. BANNER AND CONTAINER ELEMENTS:
9346//      #mountBanner() creates a thumbnail div inside the .wistia-player-wrapper.
9347//      #mountContainer() creates the YT.Player host div in the wistia element's parent.
9348//      Both use WistiaFallback.nextId() (a shared monotonic counter) for unique DOM ids.
9349//
9350//   3. OVERLAY TOGGLE:
9351//      Unlike the native VideoBannerPlayer which wires the toggle via #createOverlay,
9352//      this class wires its own toggle via #wireOverlay because it uses the Wistia
9353//      component's authored overlay and button elements (passed in via config) rather
9354//      than creating new ones.
9355//
9356//   4. ISG DETECTION:
9357//      The isISG flag is determined by checking whether the wistia element is inside
9358//      a .cmp-isg-banner-media-container. This mirrors the native banner component's
9359//      own detection logic and controls which state machine the base class uses.
9360//
9361// CONSENT FLOW:
9362//   - If consent is available: player is created immediately on execute().
9363//   - If consent is not available: overlay is hidden and OneTrust modal is shown.
9364//     On acceptance, the player is created and overlay is restored.
9365//     On dismissal, nothing changes (overlay stays hidden).
9366// =============================================================================
9367class WistiaVideoBannerFallback extends VideoBannerPlayer {
9368    #config;          // the config object passed from WistiaFallback (see WistiaFallback.js)
9369    #wistiaElement;   // the <wistia-player> DOM element being replaced
9370    #containerEl = null; // the <div> we create to host the YT.Player iframe
9371    #bannerEl = null;    // the thumbnail banner <div> we create inside the wrapper
9372
9373    // Each instance gets its own slot in the base class player/state arrays via #index.
9374    // #nextIndex is a class-level counter that increments on every construction, ensuring no
9375    // two instances share the same slot. Without this, all instances would collide at index 0:
9376    // - _initPlayerState(0) on instance B overwrites ISG flags written by instance A
9377    // - _createBannerPlayer(0) on instance B overwrites instance A's YT.Player reference,
9378    //   making instance A's player permanently uncontrollable
9379    // - div.id = 'banner-0' is duplicated in the DOM, causing _showBanner/_hideBanner to
9380    //   target the wrong element
9381    // - instance A's #applyBannerInterval[0] is overwritten and the original interval leaks
9382    //   forever since its reference is lost
9383    static #nextIndex = 0;
9384    #index;
9385
9386    // config object shape (provided by WistiaFallback.createVideoBannerFallback):
9387    // {
9388    //   wistiaElement:   HTMLElement  — the <wistia-player> element to hide/replace
9389    //   youtubeVideoId:  string       — YouTube video id to fall back to
9390    //   thumbnailUrl:    string       — URL of the thumbnail image for the banner background
9391    //   overlayElement:  HTMLElement  — the Wistia overlay element (may be null)
9392    //   button:          HTMLElement  — the custom play/pause button element (may be null)
9393    // }
9394    constructor(config, hasConsent) {
9395        super([], hasConsent); // empty array: we manage our own element, not a querySelectorAll list
9396        // wistiaPlayer.js passes button as a jQuery object — unwrap to a plain DOM element
9397        // so #wireOverlay can use native addEventListener without branching on jquery.
9398        this.#config = config.button?.jquery ? { ...config, button: config.button[0] } : config;
9399        this.#wistiaElement = config.wistiaElement;
9400        this.#index = WistiaVideoBannerFallback.#nextIndex++;
9401        // Detect ISG mode by checking the component's container hierarchy.
9402        // This mirrors the native VideoBannerPlayer._initElement detection logic.
9403        const isISG = config.wistiaElement.closest('.cmp-isg-banner-media-container') !== null;
9404        // _initPlayerState initialises all per-player state arrays in the base class
9405        // (isISG flag, firstIteration flag, reachedVideoEnd, manualPause, etc.).
9406        this._initPlayerState(this.#index, isISG);
9407        console.debug(`[WistiaBannerFallback] constructed index=${this.#index} videoId=${config.youtubeVideoId} isISG=${isISG} hasConsent=${hasConsent}`);
9408    }
9409
9410    // Entry point called by WistiaFallback once the YouTube IFrame API is ready.
9411    // Creates the thumbnail banner, hides the original Wistia element, then either
9412    // creates the YouTube player immediately (consent available) or shows the
9413    // OneTrust modal and defers player creation until consent is granted.
9414    execute() {
9415        console.debug(`[WistiaBannerFallback] execute — hasConsent=${this._hasConsent()}`);
9416        this.#mountBanner();
9417        this.#wistiaElement.style.display = 'none';
9418
9419        if (this._hasConsent()) {
9420            this.#mountContainer();
9421            // _createBannerPlayer is the protected hook from VideoBannerPlayer that
9422            // creates the YT.Player and stores it at the given index slot.
9423            // Pass the inner div's id — YT.Player replaces that element with its iframe.
9424            this._createBannerPlayer(this.#index, this.#config.youtubeVideoId, this.#containerEl.firstElementChild.id);
9425            this.#wireOverlay();
9426        } else {
9427            console.debug('[WistiaBannerFallback] no consent — hiding overlay and showing OneTrust modal');
9428            this.#hideOverlay();
9429            showOneTrustModal();
9430            // setupOneTrustAcceptListener queues this callback alongside other waiting instances.
9431            // All callbacks drain together when the user clicks Accept.
9432            setupOneTrustAcceptListener(() => {
9433                console.debug('[WistiaBannerFallback] consent granted — mounting and creating player');
9434                this.#mountContainer();
9435                // Pass the inner div's id — YT.Player replaces that element with its iframe.
9436                this._createBannerPlayer(this.#index, this.#config.youtubeVideoId, this.#containerEl.firstElementChild.id);
9437                this.#wireOverlay();
9438                this.#showOverlay();
9439            });
9440            setupOneTrustCloseListener(null); // no teardown needed on dismissal for this component
9441        }
9442    }
9443
9444    // Creates the thumbnail banner div inside the .wistia-player-wrapper.
9445    // The banner id uses the instance-specific #index to avoid duplicate DOM ids
9446    // when multiple banner fallbacks exist on the same page ('banner-0' duplicated
9447    // would cause _showBanner/_hideBanner in the base class to target the wrong element).
9448    // The thumbnail image is applied as a CSS background if provided in config.
9449    #mountBanner() {
9450        if (this.#bannerEl) return; // idempotent — only mount once
9451        const wrapper = this.#wistiaElement.closest('.wistia-player-wrapper');
9452        const div = document.createElement('div');
9453        div.className = 'video-banner youtube-fallback-banner';
9454        // Use instance-specific index to avoid duplicate DOM ids when multiple banner
9455        // fallbacks exist on the same page — 'banner-0' would otherwise appear twice.
9456        div.id = `banner-${this.#index}`;
9457        if (this.#config.thumbnailUrl) {
9458            div.style.backgroundImage = `url(${this.#config.thumbnailUrl})`;
9459            div.style.backgroundSize = 'cover';
9460            div.style.backgroundPosition = 'center';
9461        }
9462        wrapper.appendChild(div);
9463        this.#bannerEl = div;
9464        console.debug(`[WistiaBannerFallback] mounted banner id=${div.id}`);
9465    }
9466
9467    // Creates the wrapper that holds the YT.Player iframe.
9468    // #containerEl is an outer wrapper div that is never touched by the YouTube IFrame API —
9469    // any future visibility or state operations target this stable element.
9470    // The inner div (ytDiv) is what YT.Player replaces with its iframe; execute() passes
9471    // ytDiv's id to _createBannerPlayer so the API never reaches #containerEl.
9472    // Uses a shared monotonic counter (WistiaFallback.nextId()) for unique ids so
9473    // multiple fallback instances mounting within the same millisecond don't collide.
9474    #mountContainer() {
9475        if (this.#containerEl) return; // idempotent — only mount once
9476        const wrapper = document.createElement('div');
9477        wrapper.className = 'youtube-fallback-player';
9478        wrapper.style.cssText = 'width:100%;height:100%;position:absolute;top:0;left:0;';
9479
9480        const ytDiv = document.createElement('div');
9481        ytDiv.id = 'youtube-fallback-banner-' + WistiaFallback.nextId();
9482        ytDiv.style.cssText = 'width:100%;height:100%;';
9483        wrapper.appendChild(ytDiv);
9484
9485        this.#wistiaElement.parentNode.insertBefore(wrapper, this.#wistiaElement.nextSibling);
9486        this.#containerEl = wrapper;
9487        console.debug(`[WistiaBannerFallback] mounted YT container ytDiv id=${ytDiv.id}`);
9488    }
9489
9490    // Wires the play/pause toggle to the Wistia component's authored overlay and button
9491    // elements (both are passed in via config). Clicking either toggles playback.
9492    // _getPlayer uses this.#index so this handler always controls the player belonging
9493    // to this specific instance, not a shared slot 0 player.
9494    #wireOverlay() {
9495        console.debug(`[WistiaBannerFallback] #wireOverlay index=${this.#index}`);
9496        const toggle = (e) => {
9497            e.stopPropagation();
9498            const player = this._getPlayer(this.#index);
9499            if (!player) return;
9500            const isPlaying = player.getPlayerState() === YT.PlayerState.PLAYING;
9501            console.debug(`[WistiaBannerFallback] overlay toggle index=${this.#index} — currently ${isPlaying ? 'playing' : 'paused'}`);
9502            if (isPlaying) {
9503                player.pauseVideo();
9504                this.#updateIcon(false);
9505            } else {
9506                player.playVideo();
9507                this.#updateIcon(true);
9508            }
9509        };
9510        this.#config.overlayElement?.addEventListener('click', toggle);
9511        this.#config.button?.addEventListener('click', toggle);
9512    }
9513
9514    // Hides the overlay button and the overlay container when consent is not available.
9515    // The overlay is hidden rather than removed so it can be restored by #showOverlay
9516    // when consent is later granted, without needing to re-create the DOM elements.
9517    #hideOverlay() {
9518        console.debug(`[WistiaBannerFallback] #hideOverlay index=${this.#index}`);
9519        this.#config.button?.style && (this.#config.button.style.display = 'none');
9520        this.#config.overlayElement?.classList.add('overlay-hidden');
9521    }
9522
9523    // Restores the overlay button and container after consent is granted.
9524    #showOverlay() {
9525        console.debug(`[WistiaBannerFallback] #showOverlay index=${this.#index}`);
9526        this.#config.button?.style && (this.#config.button.style.display = '');
9527        this.#config.overlayElement?.classList.remove('overlay-hidden');
9528    }
9529
9530    // Updates the play/pause icon on the button to reflect the current playback state.
9531    // Uses classList.replace() to swap between 'play-icon' and 'pause-icon' classes,
9532    // and updates the aria-label for screen reader accessibility.
9533    // isPlaying=true means video is now playing so icon should show pause affordance.
9534    #updateIcon(isPlaying) {
9535        console.debug(`[WistiaBannerFallback] #updateIcon index=${this.#index} isPlaying=${isPlaying}`);
9536        const btn = this.#config.button?.[0] || this.#config.button; // support jQuery or plain HTMLElement
9537        if (!btn) return;
9538        const icon = btn.querySelector('div') || btn.querySelector('span');
9539        if (!icon) return;
9540        if (isPlaying) {
9541            icon.classList.replace('play-icon', 'pause-icon');
9542            btn.setAttribute('aria-label', 'Pause video');
9543        } else {
9544            icon.classList.replace('pause-icon', 'play-icon');
9545            btn.setAttribute('aria-label', 'Play video');
9546        }
9547    }
9548}
9549
9550// =============================================================================
9551// WistiaVideoPlayerFallback.js
9552// =============================================================================
9553// PURPOSE: YouTube fallback player for the Wistia video component in standard
9554// (non-banner) player mode. When the Wistia player fails to load or times out,
9555// Player.js detects the failure and calls WistiaFallback.createVideoPlayerFallback()
9556// which instantiates this class.
9557//
9558// EXTENDS: VideoComponentPlayer → YouTubePlayerBase
9559//
9560// WHY A SEPARATE CLASS (not just VideoComponentPlayer)?
9561// The native VideoComponentPlayer is driven by a querySelectorAll list of DOM
9562// elements managed through the base class init() loop. The Wistia fallback is
9563// triggered at runtime for a single specific element — there is no list, no init()
9564// loop, and the Wistia DOM structure differs from the native YouTube component DOM.
9565// This class manages its own element and calls base class protected methods
9566// (_registerPlayer, _createYTPlayer, _getPlayer) directly with its own index.
9567//
9568// KEY DESIGN DECISIONS:
9569//
9570//   1. CUED MODE (no autoplay on creation):
9571//      When the fallback fires, we create the YT.Player immediately (so it's ready
9572//      for the first click) but do NOT play it. This is achieved by overriding
9573//      _onPlayerReady() to suppress the base class's playVideo() call. The player
9574//      sits in a cued/ready state until the user clicks.
9575//
9576//   2. PLAY ON CLICK (not on API ready):
9577//      The user must click the play button or the Wistia banner to start playback.
9578//      #revealAndPlay() handles the reveal of the YouTube container and starts
9579//      playback. If the user clicks before the YT API has finished loading the
9580//      player, #playPending is set to true and _onPlayerReady() picks it up.
9581//
9582//   3. INSTANCE INDEX (#index):
9583//      Each instance gets a unique index via the auto-incrementing #nextIndex counter.
9584//      This is critical: without it, all instances on the same page would collide at
9585//      index 0 in the base class arrays, causing players to overwrite each other.
9586//
9587//   4. VISIBILITY:
9588//      The YouTube container is created with the 'player-hidden' CSS class applied.
9589//      It is revealed (class removed) only when the user clicks play. When the video
9590//      ends, 'player-hidden' is re-applied and the Wistia banner is restored.
9591//      Only the CSS class controls visibility — do NOT also set style.display,
9592//      as inline styles survive class toggling and would break replay.
9593//
9594// CONSENT FLOW:
9595//   - If consent is available: player is cued immediately on execute().
9596//   - If consent is not available: the play button is hidden and the OneTrust modal
9597//     is shown. On acceptance, the player is cued and the button is revealed.
9598//     On dismissal, nothing changes (the button stays hidden).
9599// =============================================================================
9600class WistiaVideoPlayerFallback extends VideoComponentPlayer {
9601    #config;          // the config object passed from WistiaFallback (see WistiaFallback.js)
9602    #wistiaElement;   // the <wistia-player> DOM element being replaced
9603    #containerEl = null;   // the <div> we create to host the YT.Player iframe
9604    #playPending = false;  // true if user clicked before the YT API finished loading
9605
9606    // Each instance gets its own slot in the base class player/analytics arrays via #index.
9607    // #nextIndex is a class-level counter that increments on every construction, ensuring no
9608    // two instances share the same slot. Without this, all instances would collide at index 0:
9609    // the second instance's _registerPlayer(0) and _createYTPlayer(0) calls would silently
9610    // overwrite the first instance's media name and YT.Player reference, making the first
9611    // player permanently uncontrollable and its analytics events misfiring under the wrong name.
9612    static #nextIndex = 0;
9613    #index;
9614
9615    // config object shape (provided by WistiaFallback.createVideoPlayerFallback):
9616    // {
9617    //   wistiaElement:   HTMLElement  — the <wistia-player> element to hide/replace
9618    //   youtubeVideoId:  string       — YouTube video id to fall back to
9619    //   mediaName:       string       — authored name used for Adobe Analytics events
9620    //   bannerElement:   HTMLElement  — the Wistia banner/thumbnail shown before play
9621    //   button:          HTMLElement  — the custom play button element (may be null)
9622    //   onEnd:           Function     — optional callback fired when the video ends
9623    // }
9624    constructor(config, hasConsent) {
9625        super([], hasConsent); // empty array: we manage our own element, not a querySelectorAll list
9626        // wistiaPlayer.js passes button as a jQuery object — unwrap to a plain DOM element
9627        // so #wireClickHandlers can use native addEventListener without branching on jquery.
9628        this.#config = config.button?.jquery ? { ...config, button: config.button[0] } : config;
9629        this.#wistiaElement = config.wistiaElement;
9630        this.#index = WistiaVideoPlayerFallback.#nextIndex++;
9631        this._registerPlayer(this.#index, config.mediaName);
9632        console.debug(`[WistiaPlayerFallback] constructed index=${this.#index} videoId=${config.youtubeVideoId} mediaName=${config.mediaName} hasConsent=${hasConsent}`);
9633    }
9634
9635    // Entry point called by WistiaFallback once the YouTube IFrame API is ready.
9636    // Hides the original Wistia element (which has failed or timed out), then
9637    // either cues the YouTube player immediately (consent available) or shows the
9638    // OneTrust modal and defers everything until consent is granted.
9639    execute() {
9640        console.debug(`[WistiaPlayerFallback] execute — hiding wistia element, hasConsent=${this._hasConsent()}`);
9641        this.#wistiaElement.style.display = 'none';
9642
9643        if (this._hasConsent()) {
9644            this.#mount();
9645            this.#cuePlayer();
9646            this.#wireClickHandlers();
9647            this._showButton();
9648        } else {
9649            console.debug('[WistiaPlayerFallback] no consent — hiding button and showing OneTrust modal');
9650            this._hideButton();
9651            showOneTrustModal();
9652            // setupOneTrustAcceptListener queues this callback alongside any other players
9653            // on the page that are also waiting for consent. All callbacks drain together
9654            // when the user clicks Accept (see youtube-one-trust.js for the queue pattern).
9655            setupOneTrustAcceptListener(() => {
9656                console.debug('[WistiaPlayerFallback] consent granted — mounting and cueing player');
9657                this.#mount();
9658                this.#cuePlayer();
9659                this.#wireClickHandlers();
9660                this._showButton();
9661            });
9662            setupOneTrustCloseListener(null); // no teardown needed on dismissal for this component
9663        }
9664    }
9665
9666    // Initialises the YT.Player without triggering playback. The player is created via
9667    // _createYTPlayer which delegates onReady to _onPlayerReady. This class overrides
9668    // _onPlayerReady to suppress the base class playVideo() call — that override is what
9669    // makes the player cued rather than anything _createYTPlayer itself controls.
9670    #cuePlayer() {
9671        // Pass the inner div's id — YT.Player replaces that div with its iframe.
9672        // #containerEl is the outer wrapper and must not be passed here, as the API
9673        // would detach it and classList toggling in #revealAndPlay would stop working.
9674        const ytDivId = this.#containerEl.firstElementChild.id;
9675        console.debug(`[WistiaPlayerFallback] cueing YT player index=${this.#index} videoId=${this.#config.youtubeVideoId}
9675 ytDivId=${ytDivId}`);
9676        this._createYTPlayer(this.#index, this.#config.youtubeVideoId, ytDivId);
9677    }
9678
9679    // Override of VideoComponentPlayer._onPlayerReady.
9680    // The base class plays immediately when the player is ready (correct for the native
9681    // click-to-play flow where the player is created in response to a user click).
9682    // Here we suppress that behaviour because the player is created up-front in a cued
9683    // state — playback should only start when the user explicitly clicks the play button.
9684    //
9685    // Exception: if the user clicked the play button while the API was still loading
9686    // (#playPending = true), we play immediately when the API becomes ready.
9687    _onPlayerReady(index) {
9688        if (this.#playPending) {
9689            console.debug('[WistiaPlayerFallback] player ready — pending click, playing now');
9690            this.#playPending = false;
9691            this._getPlayer(index)?.playVideo();
9692        } else {
9693            console.debug('[WistiaPlayerFallback] player cued and ready — waiting for user click');
9694        }
9695    }
9696
9697    // Override of VideoComponentPlayer._onVideoEnded.
9698    // After the video ends, hides the YouTube container by re-adding 'player-hidden'
9699    // and restores the Wistia banner so the page returns to its pre-play appearance.
9700    // Only the CSS class controls visibility — do NOT add style.display='none' here,
9701    // as that would survive the class toggle in #revealAndPlay and break replay.
9702    // Fires the optional onEnd callback if provided (e.g. for parent component cleanup).
9703    _onVideoEnded(_index) {
9704        console.debug('[WistiaPlayerFallback] video ended — hiding container, restoring banner');
9705        if (this.#containerEl) {
9706            this.#containerEl.classList.add('player-hidden');
9707        }
9708        this.#config.bannerElement?.classList.remove('banner-hidden');
9709        this.#config.onEnd?.();
9710    }
9711
9712    // Creates and inserts the wrapper that holds the YT.Player iframe.
9713    // #containerEl is an outer wrapper div that carries 'player-hidden' and is never
9714    // touched by the YouTube IFrame API — visibility toggling always targets this element.
9715    // The inner div (ytDiv) is what YT.Player replaces with its iframe; #cuePlayer()
9716    // passes ytDiv's id to _createYTPlayer so the API never reaches #containerEl.
9717    // Uses a shared monotonic counter (WistiaFallback.nextId()) for unique ids so
9718    // multiple fallback instances mounting within the same millisecond don't collide.
9719    #mount() {
9720        if (this.#containerEl) return; // idempotent — only mount once
9721        const wrapper = document.createElement('div');
9722        wrapper.className = 'youtube-fallback-player player-hidden';
9723        wrapper.style.cssText = 'width:100%;height:100%;';
9724
9725        const ytDiv = document.createElement('div');
9726        ytDiv.id = 'youtube-fallback-player-' + WistiaFallback.nextId();
9727        ytDiv.style.cssText = 'width:100%;height:100%;';
9728        wrapper.appendChild(ytDiv);
9729
9730        this.#wistiaElement.parentNode.insertBefore(wrapper, this.#wistiaElement.nextSibling);
9731        this.#containerEl = wrapper;
9732        console.debug(`[WistiaPlayerFallback] mounted YT container ytDiv id=${ytDiv.id}`);
9733    }
9734
9735    // Wires click handlers on the custom play button and the Wistia banner element.
9736    // Both elements trigger the same #revealAndPlay flow so the user can click
9737    // either the button or the thumbnail area to start playback.
9738    #wireClickHandlers() {
9739        console.debug(`[WistiaPlayerFallback] #wireClickHandlers index=${this.#index}`);
9740        const play = (e) => {
9741            e.stopPropagation();
9742            console.debug('[WistiaPlayerFallback] play triggered by user click');
9743            this.#revealAndPlay();
9744        };
9745        this.#config.button?.addEventListener('click', play);
9746        this.#config.bannerElement?.addEventListener('click', play);
9747    }
9748
9749    // Called when the user clicks play. Reveals the YouTube container by removing
9750    // 'player-hidden' and hides the Wistia banner. Then either plays the already-ready
9751    // player, or sets #playPending = true if the YT API hasn't finished loading yet
9752    // (in which case _onPlayerReady picks up the flag and plays when ready).
9753    #revealAndPlay() {
9754        // Reveal the YT container — only the class is toggled, never style.display,
9755        // so replay after video-end works correctly.
9756        this.#containerEl.classList.remove('player-hidden');
9757        this.#config.bannerElement?.classList.add('banner-hidden');
9758        console.debug('[WistiaPlayerFallback] container revealed, banner hidden');
9759
9760        const player = this._getPlayer(this.#index);
9761        if (player) {
9762            console.debug('[WistiaPlayerFallback] calling playVideo on existing player');
9763            player.playVideo();
9764        } else {
9765            // Player not ready yet — set pending flag so _onPlayerReady triggers play
9766            // when the YouTube IFrame API finishes loading the player.
9767            console.debug('[WistiaPlayerFallback] player not ready yet — flagging pending play');
9768            this.#playPending = true;
9769        }
9770    }
9771
9772    // Hides the custom play button (used when consent is not yet available).
9773    _hideButton() {
9774        console.debug('[WistiaPlayerFallback] _hideButton');
9775        if (this.#config.button) this.#config.button.style.display = 'none';
9776    }
9777
9778    // Restores the custom play button to its default visible state.
9779    _showButton() {
9780        console.debug('[WistiaPlayerFallback] _showButton');
9781        if (this.#config.button) this.#config.button.style.display = '';
9782    }
9783}
9784
9785// Add "back-to-top" button to page...
9786const emptyDiv = document.createElement("div");
9787
9788emptyDiv.innerHTML = `
9789    <button class="back-to-top-btn" type="button" aria-hidden="true" title="Scroll back to top of web page.">
9790        <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" fill="currentColor" class="bi bi-arrow-up-short" viewBox="0 0 16 16">
9791            <path fill-rule="evenodd" d="M8 12a.5.5 0 0 0 .5-.5V5.707l2.146 2.147a.5.5 0 0 0 .708-.708l-3-3a.5.5 0 0 0-.708 0l-3 3a.5.5 0 1 0 .708.708
9791L7.5 5.707V11.5a.5.5 0 0 0 .5.5z"/>
9792        </svg>
9793    </button>`
9794
9795document.body.append(emptyDiv);
9796
9797// Go back to top of page on button click...
9798const backToTopButton = document.getElementsByClassName("back-to-top-btn")[0];
9799
9800backToTopButton.setAttribute("tabIndex", "-1");
9801
9802backToTopButton.addEventListener("click", (e) => {
9803    document.body.scrollTop = 0; // For Safari
9804    document.documentElement.scrollTop = 0; // For Chrome, Firefox, and IE
9805    e.currentTarget.blur(); // Remove button focus
9806
9807    skipRecordSubPageViewed = true;
9808
9809    window.dataLayer = window.dataLayer || [];
9810    window.dataLayer.push({
9811        "event": "callToAction", //e111
9812        "callToAction": {
9813            "location": document.title.split("|")[0].trim(), //v103
9814            "ctaName": 'back to top', //v104
9815            "ctaType": 'custom button' //v111
9816        }
9817    });
9818
9819    setTimeout(function() {
9820        skipRecordSubPageViewed = false;
9821    }, 2000);
9822});
9823
9824// Show/Hide button based on scroll direction...
9825window.onscroll = () => {
9826    let currentDistanceFromTop = window.pageYOffset || document.documentElement.scrollTop;
9827    backToTopButton.blur();
9828    // Do not show the back-to-top button until the user has scrolled down 2000px
9829    if (currentDistanceFromTop <= 2000) {
9830        backToTopButton.classList.remove("back-to-top-btn-grow");
9831        backToTopButton.setAttribute('aria-hidden', 'true');
9832        backToTopButton.setAttribute("tabIndex", "-1"); // Focus not allowed while hidden
9833    }
9834    else {
9835        backToTopButton.classList.add("back-to-top-btn-grow");
9836        backToTopButton.setAttribute('aria-hidden', 'false');
9837        backToTopButton.setAttribute("tabIndex", "0"); // Focus allowed if it's not hidden
9838    }
9839}
9840
9841$(document).ready(() => {
9842    const notTimelineClass = document.querySelector(".horizontalTimeline") == null ? "not-timeline-page" : "";
9843    const backTopButton = document.querySelector(".back-to-top-btn");
9844
9845    if(backTopButton){
9846        backTopButton.classList.add(notTimelineClass);
9847    }
9848});
9849
9850$(function () {
9851    const currentHost = window.location.host;
9852    let allAnchorTags = document.querySelectorAll('a');
9853    allAnchorTags.forEach((anchorTag) => {
9854        try {
9855            const rawHref = anchorTag.getAttribute('href');
9856            if (rawHref === null || rawHref.trim() === '' || rawHref === '#') return;
9857            let url = new URL(anchorTag.href);
9858            let anchorTagClassName = anchorTag.className;
9859
9860            if (url.host === currentHost || anchorTagClassName.includes('cmp-sec-tile__card--link') || anchorTagClassName.includes('vgn-link') || anchorTagClassName.includes('cmp-deepLinks')) {
9861                return;
9862            }
9863
9864            //set value of iconClass indicating it is an external link
9865            let iconClass = 'external-link-icon';
9866
9867            //if the link is external, remove the ::after pseudo-element by setting the content to none
9868            anchorTag.classList.add("external-link-update-arrow-content");
9869
9870            // Remove CTA blue arrow <i> element so only the external link icon is shown
9871            const existingCtaArrow = anchorTag.querySelector('i.arrow');
9872            if (existingCtaArrow) {
9873                existingCtaArrow.remove();
9874            }
9875
9876            // applicable only for cta buttons - check if existing <i> already has the icon class
9877            const existingIconWithClass = anchorTag.querySelector('i.external-link-icon');
9878            if (existingIconWithClass) {
9879                // Ensure existing icon has the correct class without adding a duplicate
9880                existingIconWithClass.className = iconClass;
9881                return;
9882            }
9883
9884            if (anchorTag.getAttribute("data-iconurl") && anchorTag.getAttribute("data-iconurl") !== 'empty') {
9885                let iconSelector = "span." + anchorTag.getAttribute("data-iconurl");
9886                const iconElement = anchorTag.querySelector(iconSelector);
9887                if (iconElement) iconElement.remove();
9888            }
9889
9890            // Create icon element and append it (don't use innerHTML to avoid replacing italic/bold tags)
9891            const icon = document.createElement('i');
9892            icon.className = iconClass;
9893            icon.setAttribute('aria-label', 'Opens in a new tab');
9894            anchorTag.appendChild(icon);
9895        } catch (exception) {
9896            console.warn("Error processing URL: ", anchorTag.href, exception);
9897            logClientError("WARN", "Error processing URL: " + anchorTag.href + " - " + (exception && exception.message ? exception.message : String(exception)), "external-link-icon.js");
9898        }
9899    });
9900});
9901
9902// Add subscribe banner to page if the template is an article and
9903// the subscribeBannerFlag is set to false or not set at all and
9904// the page is not in a preview environment.
9905
9906$(document).ready(function () {
9907    const templateNameElement = document.getElementById("templatename");
9908    const templateName = templateNameElement.getAttribute("data-templatename");
9909    let hideBanner = templateNameElement.getAttribute("data-subscribeBannerFlag");
9910    if (templateName === "article-template" && (hideBanner === "false" || hideBanner == null) && !isPreviewEnvironment()) {
9911        addSubscribeBannerToPage();
9912    }
9913});
9914
9915function isPreviewEnvironment() {
9916    let subdomain = window.location.hostname.split(".")[0];
9917    let previewEnvironments = ["corpaempubpre", "corpaemdisppre"];
9918    return previewEnvironments.includes(subdomain);
9919}
9920
9921function addSubscribeBannerToPage () {
9922
9923    const subscribeBannerContainer = document.createElement("div");
9924    subscribeBannerContainer.innerHTML = `
9925        <div class="subscribe-banner">
9926            <p class="cmp-articletopicbodycopy__sectionBodyCopy">Get Vanguard's insights delivered straight to your inbox.</p>
9927            <div class="btn-secondary-download-container">
9928                <a href="/content/corporatesite/us/en/corp/subscription-dashboard.html" id="subscribe-banner-button" class="subscribe-banner__button button button-secondary-download align-self-center button-padding-26" target="_blank" tabindex="0">
9929                    Subscribe
9930                </a>
9931            </div>
9932            <div class="cancel-button-container" style="background-color: #040505">
9933                <a href="javascript:;" id="magic-box-icon" role="button" aria-label="Dismiss" tabindex="0">
9934                    <svg focusable="false" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Clear" class="subscribe-banner__magic-box-clear-svg"><title>Clear</title><g fill="currentColor"><path d="m7.881 6.501 4.834-4.834c.38-.38.38-1.001 0-1.381s-1.001-.38-1.381 0l-4.834 4.834-4.834-4.835c-.38-.38-1.001-.38-1.381 0s-.38 1.001 0 1.381l4.834 4.834-4.834 4.834c-.38.38-.38 1.001 0 1.381s1.001.38 1.381 0l4.834-4.834 4.834 4.834c.38.38 1.001.38 1.381 0s .38-1.001 0-1.381z"></path></g></svg>
9935                </a>
9936            </div> 
9937        </div>`;
9938    document.body.append(subscribeBannerContainer);
9939
9940    const subscribeBanner = document.getElementsByClassName("subscribe-banner")[0];
9941    const subscribeBannerButton = document.getElementById("subscribe-banner-button");
9942    const subscribeBannerCancelButton = document.getElementById("magic-box-icon");
9943
9944    // Show/Hide banner/button based on scroll distance...
9945    window.addEventListener("scroll", () => {
9946        let subscribeBannerCurrentDistanceFromTop = window.pageYOffset || document.documentElement.scrollTop;
9947        subscribeBanner.blur();
9948        // Do not show the banner/button until the user has scrolled down 1000px
9949        if (subscribeBannerCurrentDistanceFromTop <= 1000) {
9950            subscribeBanner.classList.remove("subscribe-banner--show-subscribe-banner");
9951            subscribeBannerButton.setAttribute('aria-hidden', 'true');
9952            subscribeBannerButton.setAttribute("tabIndex", "-1"); // Focus not allowed while hidden
9953            subscribeBannerCancelButton.setAttribute('aria-hidden', 'true');
9954            subscribeBannerCancelButton.setAttribute("tabIndex", "-1");
9955        } else {
9956            subscribeBanner.classList.add("subscribe-banner--show-subscribe-banner");
9957            subscribeBannerButton.setAttribute('aria-hidden', 'false');
9958            subscribeBannerButton.setAttribute("tabIndex", "0");
9959            subscribeBannerCancelButton.setAttribute('aria-hidden', 'false');
9960            subscribeBannerCancelButton.setAttribute("tabIndex", "0");
9961        }
9962    });
9963
9964    // Cancel button functionality
9965    subscribeBannerCancelButton.addEventListener("click", () => {
9966        window.dataLayer = window.dataLayer || [];
9967        window.dataLayer.push({
9968            "event": "callToAction", //e111
9969            "callToAction": {
9970                "location": $(document).find("title")[0].text, //v103
9971                "ctaName": 'Subscribe Banner Dismiss', //v104
9972                "ctaType": 'custom button' //v111
9973            }
9974        });
9975        subscribeBanner.remove();
9976    });
9977
9978    subscribeBannerButton.addEventListener("click", () => {
9979        window.dataLayer = window.dataLayer || [];
9980        window.dataLayer.push({
9981            "event": "callToAction", //e111
9982            "callToAction": {
9983                "location": $(document).find("title")[0].text, //v103
9984                "ctaName": 'Subscribe Banner', //v104
9985                "ctaType": 'custom button' //v111
9986            }
9987        });
9988    });
9989}
9990(() => {
9991    const bannerElements = document.querySelectorAll('.videoBanner .youtube-container');
9992    const componentElements = document.querySelectorAll('.videoComponent .main-youtube-container');
9993
9994    const hasBanners = bannerElements.length > 0;
9995    const hasComponents = componentElements.length > 0;
9996
9997    console.debug(`[Bootstrap] videoBanner containers=${bannerElements.length} videoComponent containers=${componentElements.length}`);
9998
9999    if (!hasBanners && !hasComponents) {
10000        console.debug('[Bootstrap] no YouTube video components found, exiting');
10001        return;
10002    }
10003
10004    localStorage.removeItem('YouTubeCookieAccepted');
10005
10006    const hasConsent = hasOneTrustTargetingConsent();
10007    console.debug(`[Bootstrap] OneTrust targeting consent=${hasConsent}`);
10008
10009    if (!hasConsent) {
10010        showOneTrustModal();
10011    } else {
10012        console.debug('[Bootstrap] consent already granted — setting YouTubeCookieAccepted');
10013        localStorage.setItem('YouTubeCookieAccepted', 'true');
10014    }
10015
10016    loadYouTubeApi(() => {
10017        console.debug('[Bootstrap] YT API ready — initialising controllers');
10018
10019        if (hasBanners) {
10020            const playerEls = document.querySelectorAll('.youtube-player');
10021            console.debug(`[Bootstrap] initialising VideoBannerPlayer with ${playerEls.length} element(s)`);
10022            const bannerController = new VideoBannerPlayer(Array.from(playerEls), hasConsent);
10023            bannerController.init();
10024        }
10025
10026        if (hasComponents) {
10027            const playerEls = document.querySelectorAll('.video-youtube-player');
10028            console.debug(`[Bootstrap] initialising VideoComponentPlayer with ${playerEls.length} element(s)`);
10029            const componentController = new VideoComponentPlayer(Array.from(playerEls), hasConsent);
10030            componentController.init();
10031        }
10032    });
10033})();
10034

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.