PageSourceSearch

https://de.lexus.ch/etc.clientlibs/new-cars/clientlibs/clientlib-s…res.min.2cfc05a8cf5796e6f87d356219755a27.js

js lexus.ch collected 2026-09-26 08:37:39 UTC 15,264 bytes, 421 lines download raw bytes

1(function () {
2    "use strict";
3
4    // DOM Elements - with null checks for AEM
5    const modal = document.querySelector(".new-cars-shared-features-modal");
6    const modalClose = modal ? modal.querySelector(".new-cars-shared-features-modal-close") : null;
7    const modalContent = modal ? modal.querySelector(".new-cars-shared-features-modal-content") : null;
8    const cardWrapper = document.querySelector(".new-cars-shared-features-cards");
9    const arrowLeft = document.querySelector(".new-cars-shared-features-arrow-left");
10    const arrowRight = document.querySelector(".new-cars-shared-features-arrow-right");
11    const arrowWrapper = document.querySelector(".new-cars-shared-features-arrow-wrapper");
12    const cards = document.querySelectorAll(".new-cars-shared-features-card");
13
14    if (!cardWrapper || cards.length === 0) {
15        return;
16    }
17
18    /* Constants -------------------------------------------------------------------*/
19    const CARD_WIDTH = 236;
20    const EXPANDED_CARD_WIDTH = 500;
21    const MOBILE_BREAKPOINT = 767;
22    const DESKTOP_BREAKPOINT = 961;
23    const DRAG_THRESHOLD = 5;
24    const INITIAL_CARD_TO_SHOW = 0;
25
26    const totalCardsWidth = cards.length * CARD_WIDTH + EXPANDED_CARD_WIDTH;
27
28    let isDragging = false;
29    let startX = 0;
30    let scrollLeft = 0;
31    let hasDragged = false;
32    let lastFocusedElement = null;
33
34    // On mobile there should not be a default card open.
35    const cardToShow = cards[INITIAL_CARD_TO_SHOW];
36    if (cardToShow && !isMobile()) {
37        cardToShow.classList.add("active");
38    }
39
40    /* Utility Functions --------------------------------------------------------*/
41    function isMobile() {
42        return window.innerWidth <= MOBILE_BREAKPOINT;
43    }
44
45    function scrollToCard(card) {
46        if (card && typeof card.scrollIntoView === "function") {
47            card.scrollIntoView({
48                behavior: "smooth",
49                block: "nearest",
50                inline: "center"
51            });
52        }
53    }
54
55    function setActiveCard(targetCard) {
56        cards.forEach(function (card) {
57            card.classList.remove("active");
58        });
59        if (targetCard) {
60            targetCard.classList.add("active");
61            targetCard.focus();
62        }
63    }
64
65    /* Focus Trap Functions -------------------------------------------------------*/
66    function getFocusableElements(element) {
67        const focusableElementsString =
68            'a[href], area[href], input:not([disabled]):not([type="hidden"]):not([aria-hidden]), select:not([disabled]):not([aria-hidden]), textarea:not([disabled]):not([aria-hidden]), button:not([disabled]):not([aria-hidden]), iframe, object, embed, [contenteditable], [tabindex]:not([tabindex^="-"])';
69        return element.querySelectorAll(focusableElementsString);
70    }
71
72    function trapFocus(event) {
73        if (event.key !== "Tab" || !modalContent) return;
74
75        const focusableElements = getFocusableElements(modalContent);
76        const firstElement = focusableElements[0];
77        const lastElement = focusableElements[focusableElements.length - 1];
78
79        if (event.shiftKey) {
80            if (document.activeElement === firstElement || document.activeElement === modalContent) {
81                event.preventDefault();
82                lastElement.focus();
83            }
84        } else if (document.activeElement === lastElement) {
85            event.preventDefault();
86            firstElement.focus();
87        }
88    }
89
90    function handleModalKeydown(event) {
91        if (event.key === "Escape") {
92            hideMobileModal();
93            return;
94        }
95
96        trapFocus(event);
97    }
98
99    function setInitialModalFocus() {
100        if (!modalContent) return;
101        modalContent.focus();
102    }
103
104    /* Modal Functions -----------------------------------------------------------*/
105    function showMobileModal() {
106        if (!modal) return;
107
108        lastFocusedElement = document.activeElement;
109
110        modal.classList.add("active");
111
112        setTimeout(() => {
113            setInitialModalFocus();
114        }, 50);
115
116        document.addEventListener("keydown", handleModalKeydown);
117    }
118
119    function hideMobileModal() {
120        if (!modal) return;
121
122        modal.classList.remove("active");
123        cards.forEach(function (card) {
124            card.classList.remove("active");
125        });
126
127        document.removeEventListener("keydown", handleModalKeydown);
128
129        if (lastFocusedElement) {
130            lastFocusedElement.focus();
131            lastFocusedElement = null;
132        }
133    }
134
135    /* Arrow Functions -----------------------------------------------------------*/
136    function checkScrollArrows() {
137        if (!arrowLeft || !arrowRight || !cardWrapper) return;
138
139        const hasScrollbar = cardWrapper.scrollWidth > cardWrapper.clientWidth;
140
141        // Hide/show arrow wrapper based on scrollbar presence
142        if (arrowWrapper) {
143            if (hasScrollbar) {
144                arrowWrapper.style.display = "flex";
145
146                // Check scroll position for individual arrow states
147                const isAtStart = cardWrapper.scrollLeft <= 0;
148                const isAtEnd = cardWrapper.scrollLeft >= cardWrapper.scrollWidth - cardWrapper.clientWidth - 1;
149
150                if (isAtStart) {
151                    arrowLeft.classList.add("disabled");
152                } else {
153                    arrowLeft.classList.remove("disabled");
154                }
155
156                if (isAtEnd) {
157                    arrowRight.classList.add("disabled");
158                } else {
159                    arrowRight.classList.remove("disabled");
160                }
161            } else {
162                // No scrollbar, hide arrows completely
163                arrowWrapper.style.display = "none";
164            }
165        }
166    }
167
168    function updateCardContainerLayout() {
169        const screenWidth = window.innerWidth;
170
171        if (cardWrapper) {
172            // Calculate if content fits within viewport
173            const hasScrollbar = cardWrapper.scrollWidth > cardWrapper.clientWidth;
174
175            if (!hasScrollbar && screenWidth > DESKTOP_BREAKPOINT && screenWidth > totalCardsWidth) {
176                // Center the grid when content fits and on larger screens
177                cardWrapper.style.justifyContent = "center";
178            } else {
179                // Use default grid behavior (start alignment) when scrolling is needed
180                cardWrapper.style.justifyContent = "start";
181            }
182        }
183    }
184
185    /* Drag Handlers --------------------------------------------------------------*/
186    function handleMouseDown(e) {
187        // Only allow dragging if there's a scrollbar
188        const hasScrollbar = cardWrapper.scrollWidth > cardWrapper.clientWidth;
189        if (!hasScrollbar) return;
190
191        isDragging = true;
192        hasDragged = false;
193        startX = e.pageX - cardWrapper.offsetLeft;
194        scrollLeft = cardWrapper.scrollLeft;
195        cardWrapper.style.userSelect = "none";
196        cardWrapper.classList.add("dragging");
197    }
198
199    function handleMouseUp() {
200        isDragging = false;
201        cardWrapper.style.userSelect = "";
202        cardWrapper.classList.remove("dragging");
203    }
204
205    function handleMouseLeave() {
206        isDragging = false;
207        cardWrapper.style.userSelect = "";
208        cardWrapper.classList.remove("dragging");
209    }
210
211    function handleMouseMove(e) {
212        if (!isDragging) return;
213        e.preventDefault();
214
215        const x = e.pageX - cardWrapper.offsetLeft;
216        const walk = x - startX;
217
218        if (Math.abs(walk) > DRAG_THRESHOLD) {
219            hasDragged = true;
220        }
221
222        cardWrapper.scrollLeft = scrollLeft - walk;
223    }
224
225    /* Card Click Handler ----------------------------------------------------------*/
226    function createCardClickHandler(card, index) {
227        return function (e) {
228            const headlineElement = card.querySelector(".new-cars-shared-features-headline");
229            const cardHeadline = headlineElement ? headlineElement.textContent.trim() : "";
230
231            T1.channels.statistics.publish(dxp?.constants?.TOPICS_T1.STATS_TRACK, {
232                extraData: {
233                    event: {
234                        name: "componentevent",
235                        componentName: "shared-features",
236                        componentGroup: "content",
237                        action: "expand-card",
238                        label: cardHeadline || `card-${index}`
239                    }
240                }
241            });
242
243            if (hasDragged) {
244                e.preventDefault();
245                e.stopPropagation();
246                return;
247            }
248
249            const isCurrentlyActive = card.classList.contains("active");
250
251            // Mobile behavior
252            if (isMobile()) {
253                if (!isCurrentlyActive && modal) {
254                    setActiveCard(card);
255
256                    // Update modal content from data attributes
257                    const description = card.getAttribute("data-description");
258                    const linkUrl = card.getAttribute("data-link-url");
259                    const linkLabel = card.getAttribute("data-link-label");
260                    const externalLink = card.getAttribute("data-external-link");
261
262                    if (modal) {
263                        const modalDescription = modal.querySelector(".new-cars-shared-features-modal-description");
264                        const modalLink = modal.querySelector(".new-cars-shared-features-modal-link");
265                        const externalLinkIcon = modal.querySelector(".external-link-icon");
266
267                        if (modalDescription) {
268                            modalDescription.innerHTML = description || "";
269                        }
270
271                        if (modalLink) {
272                            if (linkUrl) {
273                                modalLink.href = linkUrl;
274                                modalLink.style.display = "block";
275                                const linkLabelElement = modalLink.querySelector(".link-label");
276                                if (linkLabelElement) {
277                                    linkLabelElement.textContent = linkLabel || "";
278                                }
279
280                                if (externalLink === "true" && externalLinkIcon) {
281                                    externalLinkIcon.style.display = "inline";
282                                } else if (externalLinkIcon) {
283                                    externalLinkIcon.style.display = "none";
284                                }
285                            } else {
286                                modalLink.style.display = "none";
287                            }
288                        }
289                    }
290
291                    showMobileModal();
292                    scrollToCard(card);
293                }
294                return;
295            }
296
297            // Desktop behavior remains unchanged
298            if (isCurrentlyActive) {
299                card.classList.remove("active");
300                const nextIndex = (index + 1) % cards.length;
301                const nextCard = cards[nextIndex];
302                nextCard.classList.add("active");
303                setActiveCard(nextCard);
304                scrollToCard(nextCard);
305            } else {
306                setActiveCard(card);
307                scrollToCard(card);
308            }
309        };
310    }
311
312    /* Arrow Click Handlers --------------------------------------------------------*/
313    function handleLeftArrowClick() {
314        if (arrowLeft && !arrowLeft.classList.contains("disabled")) {
315            const scrollDistance = totalCardsWidth / cards.length;
316            cardWrapper.scrollBy({
317                left: -scrollDistance,
318                behavior: "smooth"
319            });
320
321            T1.channels.statistics.publish(dxp?.constants?.TOPICS_T1.STATS_TRACK, {
322                extraData: {
323                    event: {
324                        name: "componentevent",
325                        componentName: "shared-features",
326                        componentGroup: "content",
327                        action: "browse-content"
328                    }
329                }
330            });
331        }
332    }
333
334    function handleRightArrowClick() {
335        if (arrowRight && !arrowRight.classList.contains("disabled")) {
336            const scrollDistance = totalCardsWidth / cards.length;
337            cardWrapper.scrollBy({
338                left: scrollDistance,
339                behavior: "smooth"
340            });
341
342            T1.channels.statistics.publish(dxp?.constants?.TOPICS_T1.STATS_TRACK, {
343                extraData: {
344                    event: {
345                        name: "componentevent",
346                        componentName: "shared-features",
347                        componentGroup: "content",
348                        action: "browse-content"
349                    }
350                }
351            });
352        }
353    }
354
355    /* Initialize Event Listeners -------------------------------------------------*/
356    function initializeEventListeners() {
357        // Modal events
358        if (modalClose) {
359            modalClose.addEventListener("click", hideMobileModal);
360        }
361        if (modal) {
362            const modalLink = modal.querySelector(".new-cars-shared-features-modal-link");
363            if (modalLink) {
364                modalLink.addEventListener("click", hideMobileModal);
365            }
366
367            modal.addEventListener("click", function (e) {
368                if (e.target === modal) {
369                    hideMobileModal();
370                }
371            });
372        }
373
374        // Drag events
375        cardWrapper.addEventListener("mousedown", handleMouseDown);
376        cardWrapper.addEventListener("mouseleave", handleMouseLeave);
377        cardWrapper.addEventListener("mouseup", handleMouseUp);
378        cardWrapper.addEventListener("mousemove", handleMouseMove);
379
380        // Card click events
381        cards.forEach(function (card, index) {
382            const animationDelay = index * 100;
383            card.style.setProperty("--shared-features-animation-delay", animationDelay + "ms");
384            card.addEventListener("click", createCardClickHandler(card, index));
385
386            const readMoreButton = card.querySelector(".new-cars-shared-features-bottom-readmore");
387            if (readMoreButton) {
388                readMoreButton.addEventListener("click", function (e) {
389                    e.stopPropagation(); // Prevent card click from firing
390                });
391            }
392        });
393
394        // Arrow events
395        if (arrowLeft) {
396            arrowLeft.addEventListener("click", handleLeftArrowClick);
397        }
398        if (arrowRight) {
399            arrowRight.addEventListener("click", handleRightArrowClick);
400        }
401
402        // Window events
403        window.addEventListener("resize", function () {
404            updateCardContainerLayout();
405            checkScrollArrows();
406        });
407
408        cardWrapper.addEventListener("scroll", function () {
409            checkScrollArrows();
410        });
411    }
412
413    /* Initialize everything ------------------------------------------------------*/
414    function init() {
415        initializeEventListeners();
416        updateCardContainerLayout();
417        checkScrollArrows();
418    }
419
420    init();
421})();

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.