PageSourceSearch

https://www.credit-agricole.fr/etc.clientlibs/settings/wcm/designs…sel.min.7ba30f062de8985ebb9c612123f714a0.js

js credit-agricole.fr collected 2026-09-24 09:15:58 UTC 15,078 bytes, 384 lines download raw bytes

1/* eslint-disable */
2class Carousel {
3    constructor(carouselEl, options = { showDots: true, controls: true }) {
4        this._resizeObserver = null;
5        this._mutationObserver = null;
6        this._intersectionObserver = null;
7        this._debouncedSynchronize = null;
8        this.snappedCardIndex = 0;
9        this.paginatedCardIndex = [];
10        this.numberOfCardVisible = 0;
11      this._controlsInitialized = false;
12        this._resizeObserver = new ResizeObserver(debounceCarousel(() => {
13          if (!this.carouselEl.offsetParent) {
14            return;
15          }
16            this._updatePagination();
17            this._synchronize();
18        }, 100));
19        this._mutationObserver = new MutationObserver(debounceCarousel((mutationList) => {
20            for (const mutation of mutationList) {
21                if (mutation.type == 'childList') {
22                    const options = {
23                        showDots: this.dots.length ? true : false,
24                        controls: this.controls ? true : false,
25                    };
26                    this._intersectionObserver.disconnect();
27                    this.carouselWrapper.scrollTo({ left: 0, behavior: 'instant' });
28                    this.dots = [];
29                    this._init(options);
30                }
31            }
32        }, 100));
33        this.cards = [];
34        this.dots = [];
35        this._debouncedSynchronize = debounceCarousel(() => {
36            this._synchronize();
37        }, 100);
38        /**
39         * @param {boolean} reversed - Sens de direction du scroll.
40         */
41        this.goToNextPage = throttle((reversed = false) => {
42            if (this.paginatedCardIndex.length === 0) {
43                return;
44            }
45            const newIndex = this.paginatedCardIndex.indexOf(this.snappedCardIndex) +
46                (reversed ? -1 : +1);
47            if (!this.paginatedCardIndex[newIndex] == undefined) {
48                return;
49            }
50            this.snappedCardIndex = this.paginatedCardIndex[newIndex];
51            this._updateDots();
52            this.goToCard(this.snappedCardIndex);
53
54        }, 100, { leading: true });
55        if (!carouselEl) {
56            throw new Error('Incorrect given carouselEl');
57        }
58        this.carouselEl = carouselEl;
59        this.carouselWrapper = carouselEl.querySelector('[carousel-cards]');
60        if (!this.carouselWrapper) {
61            throw new Error('Given carousel is missing the attribute carousel-cards');
62        }
63        // entries = only the elements whose visibility changed at this precise moment
64        // cards = all the elements of the carousel
65        this._intersectionObserver = new IntersectionObserver((entries) => {
66            // Updates the is-visible attribute
67            entries.forEach(({ target, isIntersecting }) => {
68                target.toggleAttribute('is-visible', isIntersecting);
69             });
70
71            const cards = Array.from(this.carouselWrapper.children);
72            const visibles = cards.filter(card => card.hasAttribute('is-visible'));
73             
74            if (cards.length === 1 || visibles.length === cards.length) {
75              // Case 1: only one element OR all visible → remove aria-current
76              cards.forEach(card => card.removeAttribute('aria-current'));
77            } else if (visibles.length === 1) {
78              // Case 2: only one visible, others hidden
79              cards.forEach(card => {
80                card.setAttribute('aria-current', card.hasAttribute('is-visible') ? 'true' : 'false');
81              });
82            } else {
83              // Case 3: multiple visible but not all → remove aria-current
84              cards.forEach(card => card.removeAttribute('aria-current'));
85            }
86        }, {
87          root: this.carouselWrapper,
88          threshold: 1.0
89        });
90        if (options.toggleKeyboardNavRef) {
91            this.toggleKeyboardNav = options.toggleKeyboardNavRef;
92        }
93        this._resizeObserver.observe(this.carouselEl);
94        this._mutationObserver.observe(this.carouselWrapper, {
95            childList: true,
96            subtree: false,
97        });
98        this.carouselWrapper.addEventListener('scroll', this._debouncedSynchronize);
99        this.carouselWrapper.tabIndex = -1;
100        if (options.controls) {
101            this.controls =
102                this.carouselEl.querySelector('carousel-controls');
103            if (!this.controls) {
104                throw new Error('A <carousel-controls> element is required');
105            }
106            this.controls.addEventListener('click', this.handleControls.bind(this));
107        }
108        this._init(options);
109    }
110    _init(options = { showDots: true, controls: true }) {
111        this.cards = Array.from(this.carouselWrapper.children);
112        this.cards.forEach((card, index) => {
113            this._intersectionObserver.observe(card);
114            card.toggleAttribute('is-visible', card.getBoundingClientRect().right <
115                this.carouselWrapper.getBoundingClientRect().right);
116            if (options.showDots) {
117                this.dots.push(document.createElement('mt-carousel-dots'));
118            }
119            if (this.toggleKeyboardNav) {
120                this.toggleKeyboardNav(card, index == 0);
121            }
122        });
123
124        // Used to simulate :focus-visible behavior in JS.
125        // lastWasKeyboard is true if the last interaction was keyboard navigation (Tab, Shift+Tab, Arrow keys).
126        // This allows us to only scroll cards into view when focus comes from keyboard, not mouse or touch.
127        let lastWasKeyboard = false;
128
129        document.addEventListener('keydown', (e) => {
130            // Tab, Shift+Tab or arrow keys = keyboard navigation
131            if (
132                e.key === 'Tab' ||
133                (e.key.startsWith && e.key.startsWith('Arrow'))
134            ) {
135                lastWasKeyboard = true;
136            }
137        });
138        document.addEventListener('mousedown', () => {
139            lastWasKeyboard = false;
140        });
141        document.addEventListener('touchstart', () => {
142            lastWasKeyboard = false;
143        });
144
145        // Add a single 'focusin' event listener to each card.
146        // 'focusin' triggers when the card or any of its focusable children receives focus.
147        // which can improve scroll performance in some browsers.
148        this.cards.forEach(card => {
149            card.addEventListener('focusin', (event) => {
150                // Simulate :focus-visible: true if navigation was via keyboard (Tab or Shift+Tab)
151                if (!lastWasKeyboard) {
152                    return; // Do not scroll if focus was triggered by mouse or touch
153                }
154                card.scrollIntoView({ behavior: 'auto', block: 'nearest', inline: 'nearest' });
155                lastWasKeyboard = false; // Reset after scroll
156            }, { passive: true });
157        });
158
159        if (options.showDots) {
160            this._insertPaginationDots();
161        }
162        this._updatePagination();
163        this._synchronize();
164    }
165    /**
166     * Enlève les observers et eventlisteners
167     */
168    disconnect() {
169        this._intersectionObserver.disconnect();
170        this._resizeObserver.disconnect();
171        this.carouselWrapper.removeEventListener('scroll', this._debouncedSynchronize);
172        if (this.controls) {
173            this.controls.removeEventListener('click', this.handleControls);
174        }
175    }
176    _sendEvent(eventName) {
177        this.carouselEl.dispatchEvent(new Event(eventName));
178    }
179    /**
180     * Permet de déterminer les decrés de libertés du scroll
181     */
182    _updateControls() {
183        if (this.cards.length == 0) {
184            return;
185        }
186
187      // cacher les boutons et dots si tout est visible
188      const allVisible = this.cards.every(card => card.hasAttribute('is-visible'));
189      const previousPageBtn = this.carouselEl.querySelector('carousel-controls mds-icon-button:first-of-type');
190      const nextPageBtn = this.carouselEl.querySelector('carousel-controls mds-icon-button:last-of-type');
191      if (previousPageBtn && nextPageBtn) {
192        if (allVisible || !this._controlsInitialized) {
193          previousPageBtn.style.display = 'none';
194          nextPageBtn.style.display = 'none';
195          this._controlsInitialized = true;
196          // Désactive le scroll horizontal
197          this.carouselWrapper.style.overflowX = 'hidden';
198          // Désactive les dots
199          this.dots.forEach(dot => {
200            dot.style.display = 'none';
201          });
202        } else {
203          previousPageBtn.style.display = '';
204          nextPageBtn.style.display = '';
205          // Réactive le scroll horizontal
206          this.carouselWrapper.style.overflowX = 'auto';
207          // Réactive les dots
208          this.dots.forEach(dot => {
209            dot.style.display = '';
210          });
211          // activer/désactiver previous et next btn
212            const lastCard = this.cards.at(-1);
213         const firstCard = this.cards.at(0);
214         const hasReachedEnd = Boolean(lastCard && lastCard.matches('[is-visible]'));
215            const hasReachedStart = Boolean(firstCard && firstCard.matches('[is-visible]'));
216
217          [
218            { btn: previousPageBtn, shouldDisable: hasReachedStart },
219            { btn: nextPageBtn, shouldDisable: hasReachedEnd },
220          ].forEach(({ btn, shouldDisable }) => {
221            if (shouldDisable) {
222              btn.setAttribute("disabled", "");
223              btn.setAttribute("tabindex", "-1");
224            } else {
225              btn.removeAttribute("disabled");
226              btn.setAttribute("tabindex", "0");
227            }
228            btn.addEventListener("click", (event) => {
229              if (btn.hasAttribute("disabled")) {
230                event.preventDefault();
231                event.stopPropagation();
232              }
233            });
234          });
235        }
236      }
237
238      if (previousPageBtn) {
239        previousPageBtn.addEventListener('keydown', (e) => {
240          if (e.key === 'Enter') {
241            e.preventDefault();
242            const lis = this.carouselWrapper.querySelectorAll(':scope > li');
243            if (lis.length > 0) {
244              lis[0].setAttribute('tabindex', '0');
245              lis[0].focus();
246              lis[0].setAttribute('tabindex', '-1');
247            }
248          }
249        });
250      }
251      if (nextPageBtn) {
252        nextPageBtn.addEventListener('keydown', (e) => {
253          if (e.key === 'Enter') {
254            e.preventDefault();
255            const lis = this.carouselWrapper.querySelectorAll(':scope > li');
256            if (lis.length > 0) {
257              lis[lis.length - 1].setAttribute('tabindex', '0');
258              lis[lis.length - 1].focus();
259              lis[lis.length - 1].setAttribute('tabindex', '-1');
260            }
261          }
262        });
263      }
264    }
265    handleControls({ target }) {
266        if (target.matches('mds-icon-button')) {
267            this.goToNextPage(target.matches('mds-icon-button:first-of-type'));
268        }
269    }
270    _updatePagination() {
271        if (this.cards.length == 0) {
272            return;
273        }
274        this.numberOfCardVisible = this.cards.filter((card) => card.hasAttribute('is-visible')).length;
275        this.paginatedCardIndex = [];
276        this.cards.forEach((card, index) => {
277            const isFirstCardOfPage = (index % this.numberOfCardVisible === 0 &&
278                this.cards.length - index > this.numberOfCardVisible) ||
279                index === this.cards.length - this.numberOfCardVisible;
280            card.classList.toggle('snapped', isFirstCardOfPage);
281            if (this.dots[index]) {
282                this.dots[index].classList.toggle('visible', isFirstCardOfPage);
283            }
284            if (isFirstCardOfPage) {
285                this.paginatedCardIndex.push(index);
286            }
287        });
288        if (!this.paginatedCardIndex.includes(this.snappedCardIndex)) {
289            this.snappedCardIndex = this.paginatedCardIndex[0] || 0;
290        }
291    }
292    _updateDots() {
293        this.cards.forEach((_card, index) => {
294            if (this.dots[index]) {
295                this.dots[index].classList.toggle('active', index === this.snappedCardIndex);
296            }
297        });
298    }
299    /**
300     * Permet la navigation du carousel à une carte définie
301     * @param  {number} customIndex index du carousel concerné
302     */
303    goToCard(customIndex, inlinePosition = 'start') {
304        const index = customIndex || this.snappedCardIndex;
305        const currentCard = this.cards[index];
306        // workaround vertical scroll
307        if (currentCard) {
308
309          // Save current vertical scroll position
310            const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
311
312          currentCard.scrollIntoView({
313                behavior: 'auto',
314                block: 'nearest',
315                inline: inlinePosition,
316            });
317            // Restore vertical scroll position to avoid automatic scrolling ( fix problem items with height > screen height - scroll movement after click on next/previous button )
318            window.scrollTo(window.pageXOffset, scrollTop);
319        }
320
321      this._sendEvent('carouselpagechange');
322    }
323    /**
324     * Permet de déterminer la première card affichée, en particulier pour la navigation au clavier/swipe
325     */
326    _updateSnappedCardIndex() {
327        const currentCard = this.cards.findIndex((card) => card.hasAttribute('is-visible'));
328        if (this.cards.at(-1) && this.cards.at(-1).hasAttribute('is-visible')) {
329            this.snappedCardIndex = this.paginatedCardIndex.at(-1) || 0;
330            return;
331        }
332        this.snappedCardIndex = currentCard ? currentCard : 0;
333    }
334    _synchronize() {
335        this._updateSnappedCardIndex();
336        this._updateControls();
337        this._updateDots();
338    }
339    _insertPaginationDots() {
340        const footer = this.carouselEl.querySelector('footer');
341        if (this.dots.length === 0 && !footer) {
342            return;
343        }
344        footer.innerHTML = '';
345        footer.append(...this.dots);
346    }
347}
348function throttle(fn, time, options) {
349    let timeout = 0;
350    let savedArguments = null;
351    return (...args) => {
352        if (timeout) {
353            savedArguments = args;
354            return;
355        }
356        if (options && options.leading) {
357            // @ts-ignore
358            fn.apply(this, args);
359            timeout = window.setTimeout(() => {
360                timeout = 0;
361                savedArguments = null;
362            }, time);
363            return;
364        }
365        timeout = window.setTimeout(() => {
366            // @ts-ignore
367            fn.apply(this, savedArguments || args);
368            savedArguments = null;
369            timeout = 0;
370        }, time);
371    };
372}
373function debounceCarousel(fn, time = 0) {
374    let timeout;
375    return (...args) => {
376        clearTimeout(timeout);
377        timeout = window.setTimeout(() => {
378            // @ts-ignore
379            fn.apply(this, args);
380        }, time);
381    };
382}
383/* eslint-enable */
384//_ sourceMappingURL=carousel.utils.js.map

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.