PageSourceSearch

https://www.aperol.com/app/themes/campari-wdf/includes/extensions/…/coachella-animations.js?ver=dd210

js aperol.com collected 2026-09-24 18:17:33 UTC 30,205 bytes, 911 lines download raw bytes

1document.body.classList.add('scroll-locked');
2
3document.addEventListener('DOMContentLoaded', () => {
4
5    let isMobile = false;
6    let topOffset = 0;
7
8    const introTimelineAnimDuration = 1000;
9    const introTimelineZeroOffset = '-=' + introTimelineAnimDuration;
10
11    const handleResize = () => {
12        isMobile = window.innerWidth < 834;
13        topOffset = 70;
14    }
15
16    handleResize();
17    window.addEventListener("resize", handleResize());
18
19    const handleIntroTimeline = () => {
20        const timelineIntro = anime.timeline({
21            easing: 'easeInOutQuad',
22            duration: introTimelineAnimDuration,
23            autoplay: false
24        });
25
26        // white bar
27        timelineIntro.add ({
28            targets: ['.sn_block_cover_bottom_inner_white-bar', '.sn_block_cover_top_inner_white-bar'],
29            translateX: '100%'
30        })
31
32        // open
33        timelineIntro.add ({
34            targets: '.sn_block_cover_bottom',
35            translateY: '100%'
36        }).add({
37            targets: '.sn_block_cover_top',
38            translateY: '-100%'
39        }, introTimelineZeroOffset)
40
41        // ticket come in
42        timelineIntro.add ({
43            targets: '.sn_block_ticket_wr .sn_block_ticket',
44            translateX: ['-90vw', '0vw'],
45        }).add ({
46            targets: '.sn_block_ticket_countdown-banner',
47            translateX: ['90vw', '0vw'],
48            rotate: 4
49        }, introTimelineZeroOffset)
50
51        // play
52        timelineIntro.play();
53        window.scroll(0,0);
54        document.body.classList.add('scroll-locked');
55    }
56
57    handleIntroTimeline();
58
59    // #endregion
60
61    // #region TICKET - TEXT/VIDEO
62
63    const ticketTimelineAnimDuration = 1000;
64    const ticketTimelineZeroOffset = '-=' + ticketTimelineAnimDuration;
65
66    const handleTicketTimeline = (animateSun = true) => {
67
68        document.querySelector('.sn_block.sn_block_ticket').style.pointerEvents = 'none';
69
70        const timelineTicket = anime.timeline({
71            easing: 'easeInOutQuad',
72            duration: ticketTimelineAnimDuration,
73            autoplay: false
74          });
75
76        // ticket come in
77        if(!isMobile) {
78            timelineTicket.add ({
79                targets: '.sn_block_ticket_wr .sn_block_ticket',
80                rotate: -40,
81                translateY: '200vh'
82            }).add({
83                targets: '.ticket_right',
84                rotate: 120,
85            }, ticketTimelineZeroOffset).add({
86                targets: '.sn_block_ticket_countdown-banner.hide-mobile',
87                rotate: 40,
88                translateY: '200vh'
89            }, ticketTimelineZeroOffset).add({
90                targets: '.sn_block_ticket_countdown-banner.hide-desktop',
91                rotate: 40,
92                translateY: '200vh'
93            }, ticketTimelineZeroOffset)
94        }
95        else
96        {
97            timelineTicket.add({
98                targets: '.sn_block_ticket_wr .sn_block_ticket .ticket_right',
99                rotate: -22,
100                duration: 200
101            }).add ({
102                targets: '.sn_block_ticket_wr .sn_block_ticket',
103                rotate: -40,
104                translateY: '200vh'
105            }, '+=200').add({
106                targets: '.ticket_right',
107                rotate: 120,
108            }, ticketTimelineZeroOffset).add({
109                targets: '.sn_block_ticket_countdown-banner.hide-mobile',
110                rotate: 40,
111                translateY: '200vh'
112            }, ticketTimelineZeroOffset).add({
113                targets: '.sn_block_ticket_countdown-banner.hide-desktop',
114                rotate: 40,
115                translateY: '200vh'
116            }, ticketTimelineZeroOffset)
117        }
118
119        // ferris wheel
120        timelineTicket.add({
121            targets: '.sn_block_ticket .sn_background',
122            scale: 15
123        })
124
125        // text-video fade in + navbar & download in
126        timelineTicket.add ({
127            targets: '.sn_block_event_schedule_download_wr',
128            translateY: -78
129        }).add({
130            targets: '.sn_header',
131            top: 0
132        }, ticketTimelineZeroOffset).add ({
133            targets: '.progress-bar',
134            translateY: [-200, 0]
135        }, ticketTimelineZeroOffset).add({
136            targets: '.sn_block.sn_block_ticket',
137            opacity: 0
138        }, ticketTimelineZeroOffset)
139
140        // text-video video & sun in
141
142        timelineTicket.add ({
143            targets: '.sn_block_text_video .video-content',
144            scale: [0, 1]
145        }).add({
146            targets: '.decoration-palm-2-stroke',
147            translateX: isMobile ? '70vw' : '17vw'
148        }, ticketTimelineZeroOffset).add ({
149            targets: '.decoration-palm-2',
150            translateX: isMobile ? '70vw' : '17vw'
151        }, ticketTimelineZeroOffset).add ({
152            targets: '.decoration-palm-1',
153            translateX: isMobile ? '-40vw' : '-6vw'
154        }, ticketTimelineZeroOffset)
155        
156        if(animateSun) {
157            timelineTicket.add({
158                targets: '.decoration-sun',
159                scale: [0, 1],
160            }, ticketTimelineZeroOffset)
161        }
162
163        timelineTicket.play();
164    }
165    const ticketRight = document.querySelector('.ticket_right')
166    const ticketLeft = document.querySelector('.ticket_left_content');
167    if (ticketRight && ticketLeft) {
168        const handleTicketAnimation = () => {
169            handleTicketTimeline();
170            window.scrollTo(0,0);
171
172            setTimeout(() => {
173                document.body.classList.remove('scroll-locked');
174            }, 4000);
175        }
176      ticketRight.addEventListener('click', () => {
177        handleTicketAnimation();
178      })
179      ticketLeft.addEventListener('click', () => {
180        handleTicketAnimation();
181      })
182    }
183
184    // #endregion
185
186    // #region SOCIAL GALLERY ANIMATIONS
187
188    const sgTimelineAnimDuration = 1000;
189    const sgTimelineZeroOffset = '-=' + sgTimelineAnimDuration;
190
191    const handleSocialGalleryTimeline = (reverse = false) => {
192        const socialGalleryTimeline = anime.timeline({
193            easing: 'easeInOutQuad',
194            duration: sgTimelineAnimDuration,
195            autoplay: false
196          });
197
198        socialGalleryTimeline.add ({
199            targets: ['.swe_post:nth-child(4)', '.swe_post:nth-child(5)'],
200            scale: reverse ? [1, 0] : [0, 1]
201        }).add({
202            targets: ['.swe_post:nth-child(3)', '.swe_post:nth-child(6)', '.swe_post:nth-child(8)'],
203            scale: reverse ? [1, 0] : [0, 1]
204        }).add({
205            targets: ['.swe_post:nth-child(1)', '.swe_post:nth-child(2)', '.swe_post:nth-child(7)'],
206            scale: reverse ? [1, 0] : [0, 1]
207        })
208
209        socialGalleryTimeline.play();
210    }
211
212    // #endregion
213
214    // #region SCROLL
215
216    const clamp = (value, min, max) => {
217        return Math.max(Math.min(value, max), min);
218    }
219
220    // returns normalized value of element's top between the triggerTop and triggerBottom points
221    const getScrollProgress = (elementTop, triggerTop, triggerBottom) => {
222        return(clamp(elementTop, triggerTop, triggerBottom) - triggerBottom) / (tr
222iggerTop - triggerBottom);
223    }
224
225    // animations
226    const sunAnimation = anime({
227        targets: '.decoration-sun',
228        scale: isMobile ? [1, 6] : [1, 2 + ((clamp(window.innerWidth, 1280, 2560) - 1280) / 1280) * 2],
229        easing: 'linear'
230    })
231
232    const livestreamAnimation = anime({
233        targets: '.live_streaming_container_inner',
234        scale: [0.8, 1],
235        easing: 'linear'
236    })
237
238    // Apply hardware acceleration to slider track
239    const latestNewsContainer = document.querySelector('.latest_news_slider_container');
240    const sliderTrack = document.querySelector('.latest_news_slider_container .slider-track');
241    const newsCards = document.querySelectorAll('.news_card');
242
243    if (sliderTrack) {
244        sliderTrack.style.willChange = 'transform';
245        sliderTrack.style.transform = 'translateZ(0)';
246    }
247
248    // Calculate total slides and currently visible slides
249    const calculateSlidePositions = () => {
250        if (!newsCards.length) return { cardWidth: 0, totalPositions: 0, visibleCards: 0 };
251        
252        const cardWidth = newsCards[0].offsetWidth + parseInt(getComputedStyle(newsCards[0]).marginLeft) * 2;
253        const containerWidth = latestNewsContainer ? latestNewsContainer.offsetWidth : 0;
254        const visibleCards = Math.floor(containerWidth / cardWidth);
255        const totalPositions = Math.max(0, newsCards.length - visibleCards);
256        
257        return {
258            cardWidth,
259            totalPositions,
260            visibleCards
261        };
262    };
263
264    // Current snap position
265    let currentSnapPosition = 0;
266
267    // Simple snap animation with hardware acceleration
268    const latestNewsAnimation = () => {
269        const { cardWidth, totalPositions } = calculateSlidePositions();
270        
271        // Calculate target position based on scroll progress
272        const latestNews = document.querySelector('.sn_block.sn_block_latest_news');
273        const latestNewsTop = latestNews ? latestNews.getBoundingClientRect().top : 0;
274        const scrollProgress = getScrollProgress(
275            latestNewsTop, 
276            -(latestNews.getBoundingClientRect().height - window.innerHeight), 
277            topOffset
278        );
279        
280        // Convert continuous scroll progress to discrete snap positions
281        const targetPosition = Math.min(
282            totalPositions,
283            Math.round(scrollProgress * totalPositions)
284        );
285        
286        // Only update if we're changing positions
287        if (targetPosition !== currentSnapPosition) {
288            currentSnapPosition = targetPosition;
289            
290            // Animate to the new position with hardware acceleration
291            anime({
292                targets: sliderTrack,
293                translateX: -currentSnapPosition * cardWidth,
294                easing: 'easeOutExpo',
295                duration: 500,
296                complete: () => {
297                    // Force GPU acceleration and prevent subpixel rendering after animation completes
298                    sliderTrack.style.transform = `translateX(${-currentSnapPosition * cardWidth}px) translateZ(0)`;
299                }
300            });
301        }
302    };
303
304    anime.set('.news_card:nth-child(even)', { rotate: 4 });
305    anime.set('.news_card:nth-child(odd)', { rotate: -4 });
306
307    const latestNewsCardEven = anime ({
308        targets: '.news_card:nth-child(even)',
309        rotate: [4, -4, 4],
310        easing: 'linear',
311        autoplay: false
312    })
313    const latestNewsCardOdd = anime({
314        targets: '.news_card:nth-child(odd)',
315        rotate: [-4, 4, -4],
316        easing: 'linear',
317        autoplay: false
318    })
319
320    // execute animations
321    const handleScrollAnimations = () => {
322        // text-video sun
323        const textVideoBlock = document.querySelector('.sn_block.sn_block_text_video');
324        const textVideoTop = textVideoBlock ? textVideoBlock.getBoundingClientRect().top : 0;
325        const sunAnimationPercent = getScrollProgress(textVideoTop, -window.innerHeight * 0.75, topOffset);
326
327        sunAnimation.seek(sunAnimationPercent * sunAnimation.duration)
328
329        // livestream
330        const livestream = document.querySelector('.live_streaming_container_inner');
331        const livestreamTop = livestream ? livestream.getBoundingClientRect().top : 0;
332        const livestreamAnimationProgress = getScrollProgress(livestreamTop, 90, window.innerHeight / 2);
333
334        livestreamAnimation.seek(livestreamAnimationProgress * livestreamAnimation.duration)
335
336        // latest news - replaced with snap function
337        updateLatestNewsInScrollHandler();
338    }
339
340    // Replacement for the latest news section in handleScrollAnimations
341    const updateLatestNewsInScrollHandler = () => {
342        const latestNews = document.querySelector('.sn_block.sn_block_latest_news');
343        if (!latestNews) return;
344        
345        const latestNewsTop = latestNews.getBoundingClientRect().top;
346        const latestNewsAnimationProgress = getScrollProgress(
347            latestNewsTop, 
348            -(latestNews.getBoundingClientRect().height - window.innerHeight), 
349            topOffset
350        );
351        
352        // Call our snap function
353        latestNewsAnimation();
354        
355        // Keep the card rotations
356        latestNewsCardOdd.seek(latestNewsAnimationProgress * latestNewsCardOdd.duration);
357        latestNewsCardEven.seek(latestNewsAnimationProgress * latestNewsCardEven.duration);
358    };
359
360    // fully in or above viewport
361    function onAboveBottomChange(element, onAbove, onBelow, useTopReference = true, offset = 150) {
362        let isAbove = false;
363
364        function checkPosition() {
365            if(!element)
366                return;
367
368            const rect = element.getBoundingClientRect();
369
370            if(useTopReference) {
371                if (rect.top < offset + 130 && !isAbove) {
372                    isAbove = true;
373                    onAbove();
374                } else if (rect.top >= offset + 130 && isAbove) {
375                    isAbove = false;
376                    onBelow();
377                }
378            }
379            else
380            {
381                if (rect.bottom < window.innerHeight - offset && !isAbove) {
382                    isAbove = true;
383                    onAbove();
384                } else if (rect.bottom >= window.innerHeight - offset && isAbove) {
385                    isAbove = false;
386                    onBelow();
387                }
388            }
389        }
390
391        window.addEventListener('scroll', checkPosition);
392        window.addEventListener('resize', checkPosition);
393
394        checkPosition();
395    }
396
397    // social gallery animation trigger
398    if(!isMobile) {
399        onAboveBottomChange(document.querySelector('.sn_block.sn_block_social_gallery'),
400            () => handleSocialGalleryTimeline(),
401            () => handleSocialGalleryTimeline(true)
402        );
403    }
404
405    // #endregion
406
407    // #region SCROLL TO SECTION
408
409
410    const goToLiveStreamCtas = document.querySelectorAll('.sn_block_t
410icket_countdown-banner .countdown-wrapper-inner');
411    if (goToLiveStreamCtas){
412
413			goToLiveStreamCtas.forEach(goToLiveStreamCta => {
414
415				goToLiveStreamCta.addEventListener('click', (e) => {
416	
417					e.preventDefault();
418					// Keep the ticket animation
419					handleTicketTimeline(false);
420					
421					// Remove the delay and scroll immediately
422					document.body.classList.remove('scroll-locked');
423					const liveStreamingBB = document.querySelector('[data-name="shopify-product-slider"]');
424                    console.log(liveStreamingBB);
425                    
426					if (liveStreamingBB) {
427						// Immediate scroll to the element without waiting 4 seconds
428						liveStreamingBB.scrollIntoView({ behavior: 'smooth', block: 'start' });
429					}
430					handleScrollAnimations();
431				})
432				
433			});
434
435    }
436
437    const goToDayClubCta = document.querySelector('.ticket-side-ctas__item:first-child');
438    if (goToDayClubCta){
439        goToDayClubCta.addEventListener('click', (e) => {
440        e.preventDefault();
441        handleTicketTimeline(false);
442        handleScrollAnimations();
443        window.scrollTo(0,0);
444  
445        setTimeout(() => {
446            document.body.classList.remove('scroll-locked');
447        }, 4000);
448      })
449
450    }
451
452    const goToSectionLastCta = document.querySelector('.ticket-side-ctas__item:last-child');
453    if (goToSectionLastCta){
454        let goToSectionAttr = goToSectionLastCta.getAttribute('go-to');
455        goToSectionLastCta.addEventListener('click', (e) => {
456
457        e.preventDefault();
458        handleTicketTimeline(false);
459        
460        if(!goToSectionAttr) {
461            goToSectionAttr = "image-carousel";
462        }
463        document.body.classList.remove('scroll-locked');
464        const goToSectionBB = document.querySelector('[data-name="'+goToSectionAttr+'"]');
465        if (goToSectionBB) {
466            goToSectionBB.scrollIntoView({ behavior: 'smooth', block: 'start' });
467        }
468        handleScrollAnimations();
469      })
470
471    }
472    // #endregion
473
474    // // #region SOCIAL GALLERY MOBILE
475
476    // const playSocialGalleryImageAnimation = (imageElement) => {
477    //     anime({
478    //         targets: imageElement,
479    //         scale: [0, 1],
480    //         easing: 'easeInOutQuad'
481    //     })
482    // }
483
484    // const playSocialGalleryImageAnimationReverse = (imageElement) => {
485    //     anime({
486    //         targets: imageElement,
487    //         scale: [1, 0],
488    //         easing: 'easeInOutQuad'
489    //     })
490    // }
491
492    // if(isMobile) {
493    //     const socialGalleryImageElements = document.querySelectorAll('.swe_post_container');
494    //     for (let i = 0; i < socialGalleryImageElements.length; i++) {
495    //         onAboveBottomChange(socialGalleryImageElements[i],
496    //             () => playSocialGalleryImageAnimation(`.swe_post:nth-child(${i + 1}) .swe_post_container`),
497    //             () => playSocialGalleryImageAnimationReverse(`.swe_post:nth-child(${i + 1}) .swe_post_container`),
498    //             false,
499    //             -100
500    //         );
501    //     }
502    // }
503
504    // // #endregion
505
506    // #region SHOP
507
508    const handleShopActivation = (activate = true) => {
509        if(activate)
510            document.querySelector('.sn_block_shopify_product_slider')?.classList.add('active');
511        else
512            document.querySelector('.sn_block_shopify_product_slider')?.classList.remove('active');
513    }
514
515    if(!isMobile) {
516        onAboveBottomChange(document.querySelector('.sn_block.sn_block_shopify_product_slider'),
517            () => handleShopActivation(),
518            () => handleShopActivation(false)
519        );
520    } else {
521        onAboveBottomChange(document.querySelector('.sn_block.sn_block_shopify_product_slider'),
522            () => handleShopActivation(),
523            () => handleShopActivation(false),
524            false,
525            -200
526        );
527    }
528
529    // #endregion
530
531    // #region QUIZ
532
533    const quizElement = document.querySelector('.sn_block_quiz_wr');
534
535    const playQuizAnimation = () => {
536        anime({
537            targets: quizElement,
538            translateY: ['50%', '0%'],
539            easing: 'easeInOutQuad'
540        })
541    }
542
543    const playQuizAnimationReverse = () => {
544        anime({
545            targets: quizElement,
546            translateY: ['0%', '50%'],
547            easing: 'easeInOutQuad'
548        })
549    }
550
551    onAboveBottomChange(document.querySelector('.sn_block_quiz.sn_block'),
552        () => playQuizAnimation(),
553        () => playQuizAnimationReverse()
554    );
555
556    // #endregion
557
558    // #region SPOTIFY
559
560    const spotifyElement = document.querySelector('.sn_block_spotify_playlist_wr iframe');
561    const spotifyElementLets = document.querySelector('.sn_block_spotify_playlist_wr .lets .content');
562    const spotifyElementPlay = document.querySelector('.sn_block_spotify_playlist_wr .play .content');
563
564    const playSpotifyAnimation = () => {
565        anime({
566            targets: spotifyElement,
567            translateX: ['90%', '0%'],
568            translateY: ['80%', '0%'],
569            rotate: ['-16deg', '0deg'],
570            easing: 'easeInOutQuad'
571        })
572        anime ({
573            targets: spotifyElementLets,
574            translateX: ['-100%', '0%'],
575            easing: 'easeInOutQuad'
576        })
577        anime ({
578            targets: spotifyElementPlay,
579            translateX: ['100%', '0%'],
580            easing: 'easeInOutQuad'
581        })
582    }
583
584    const playSpotifyAnimationReverse = () => {
585        anime({
586            targets: spotifyElement,
587            translateX: ['0%', '90%'],
588            translateY: ['0%', '80%'],
589            rotate: ['0deg', '-16deg'],
590            easing: 'easeInOutQuad'
591        })
592        anime ({
593            targets: spotifyElementLets,
594            translateX: ['0%', '-100%'],
595            easing: 'easeInOutQuad'
596        })
597        anime ({
598            targets: spotifyElementPlay,
599            translateX: ['0%', '100%'],
600            easing: 'easeInOutQuad'
601        })
602    }
603
604    onAboveBottomChange(document.querySelector('.sn_block.sn_block_spotify_playlist'),
605        () => playSpotifyAnimation(),
606        () => playSpotifyAnimationReverse()
607    );
608
609    // #endregion
610
611    // #region PROGRESS BAR
612
613    const progressBarParent = document.querySelector('.progress-bar');
614    const progressBar = document.querySelector('.progress-bar-fill');
615
616    const blocks = Array.from(document.querySelectorAll('.sn_site_wrapper .sn_block'));
617    blocks.shift();
618    blocks.shift();
619    blocks.shift(); 
620    
621    blocks.forEach(block => {
622        const progressBarRepeatElement = document.createElement('div');
623        progressBarRepeatElement.classList.add('progress-bar-section');
624        progressBarParent.appendChild(progressBarRepeatElement);
625    })
626
627    const updateProgressBar = () => {
628        const progressBarProgress = () => {
629            const lastBlock = blocks[blocks.length - 1];
630            const lastBlockRectBottom = lastBlock ? lastBlock.getBoundingClientRect().bottom : 0;
631
632            const barLength = window.innerWidth / blocks.length;
633
634            for(let i = 0; i < blocks.length; i++) {
635                const currentBlock = blocks[i];
636                const currentBlockBottom = currentBlock ? currentBlock.getBoundingClientRect().bottom : 0;
637                const currentBlockHeight = currentBlock ? currentBlock.getBoundingClientRect().height : 0;
638                const nextBlock = blocks[i + 1]?.getBoundingClientRect();
639
640                if (currentBlockBottom > window.innerHeight) {
641                    const progress = getScrollProgress(currentBlockBottom, window.innerHeight, window.innerHeight + currentBlockHeight) * barLength;
642                    if (!nextBlock || nextBlock.top >= window.innerHeight) {
643                        return (barLength * i) + progress;
644                    }
645                }
646            }
647
648            return lastBlockRectBottom < window.innerHeight ? window.innerWidth : barLength;
649        }
650
651        progressBar.style.width = progressBarProgress() + 'px';
652
653    }
654
655    // #endregion
656
657    // Add window resize handler to recalculate positions
658    window.addEventListener('resize', () => {
659        // Reset current position on resize to prevent out-of-bounds
660        const { totalPositions } = calculateSlidePositions();
661        currentSnapPosition = Math.min(currentSnapPosition, totalPositions);
662        latestNewsAnimation();
663    });
664
665    updateProgressBar();
666    window.onscroll = () => {
667        updateProgressBar();
668        handleScrollAnimations();
669    }
670
671    // #region SLIDER WHEEL
672
673    const tiles = document.querySelectorAll('.carousel-card');
674    const radius = isMobile ? 1575 : 2000; // Raggio più grande
675    let currentIndex = 0;
676
677    function updatePositions() {
678        const centerX = window.innerWidth / 2;
679        const angleStep = Math.PI / 18; // 15 gradi
680
681        tiles.forEach((tile, index) => {
682            const relativeIndex = ((index - currentIndex) + tiles.length) % tiles.length;
683            const adjustedIndex = relativeIndex - Math.floor(tiles.length / 2);
684
685            // Calcola l'angolo rispetto alla verticale
686            const angle = adjustedIndex * angleStep;
687
688            // Posizione sulla circonferenza
689            const x = centerX + Math.sin(angle) * radius;
690            const y = radius - Math.cos(angle) * radius;
691
692            // Rotazione allineata al raggio
693            const rotation = angle * (180 / Math.PI);
694
695            // Opacità basata sulla distanza dal centro
696            const opacity = Math.abs(adjustedIndex) > 2 ? 0 : 1;
697
698            tile.style.transform = `translate(${x}px, ${y}px) rotate(${rotation}deg)`;
699            tile.style.opacity = opacity;
700            tile.style.zIndex = 10 - Math.abs(adjustedIndex);
701        });
702    }
703
704    function slide() {
705        currentIndex = (currentIndex + 1) % tiles.length;
706        updatePositions();
707    }
708
709    // Posizionamento iniziale
710    updatePositions();
711
712    // Autoplay
713    setInterval(slide, 3000);
714
715    // Aggiorna posizioni al ridimensionamento
716    window.addEventListener('resize', updatePositions);
717
718    // #endregion
719
720    // #region SLIDER LINEAR
721
722    // const track = document.querySelector('.slider-track');
723    // const tilesLinear = document.querySelectorAll('.news_card');
724
725    // let currentIndexLinear = 0;
726    // let startX = 0;
727    // let currentX = 0;
728    // let isDragging = false;
729    // let startTime = 0;
730    // let dragThreshold = 50; // Soglia minima di trascinamento per cambiare slide
731
732    // // Clona gli elementi per il loop infinito
733    // function cloneElements() {
734    //   const visibleSlides = getVisibleSlides();
735    //   const slidesToClone = Math.ceil(visibleSlides);
736
737    //   // Clona elementi all'inizio e alla fine
738    //   tilesLinear.forEach((tile, index) => {
739    //     if (index < slidesToClone) {
740    //       const endClone = tile.cloneNode(true);
741    //       if (track){
742    //         track.appendChild(endClone);
743    //       }
744    //     }
745    //     if (index >= tilesLinear.length - slidesToClone) {
746    //       const startClone = tile.cloneNode(true);
747    //       if (track){
748    //         track.insertBefore(startClone, track.firstChild);
749    //       }
750    //     }
751    //   });
752
753    //   // Imposta la posizione iniziale per mostrare le slide originali
754    //   currentIndexLinear = slidesToClone;
755    //   updateSliderPosition(false);
756    // }
757
758    // function getVisibleSlides() {
759    //   const width = window.innerWidth;
760    //   if (width <= 767) return 1.25;
761    //   if (width <= 1023) return 2.5;
762    //   return 4;
763    // }
764
765    // function getTileWidth() {
766    //   const tile = document.querySelector('.news_card');
767    //   if (!tile) return;
768    //   return tile.offsetWidth + parseInt(getComputedStyle(tile).marginLeft) * 2;
769    // }
770
771    // function updateSliderPosition(animate = true) {
772    //   const translateX = -currentIndexLinear * getTileWidth();
773    //   if (track){
774    //     track.style.transition = animate ? 'transform 0.3s ease-out' : 'none';
775    //     track.style.transform = `translateX(${translateX}px)`;
776    //   }
777    // }
778
779    // function next() {
780    //   currentIndexLinear++;
781    //   updateSliderPosition();
782    //   checkBoundary();
783    // }
784
785    // function prev() {
786    //   currentIndexLinear--;
787    //   updateSliderPosition();
788    //   checkBoundary();
789    // }
790
791    // function checkBoundary() {
792    //   if (!track) return;
793
794    //   const totalSlides = track.children.length;
795    //   const visibleSlides = getVisibleSlides();
796
797    //   // Reset alla fine del loop
798    //   track.addEventListener('transitionend', () => {
799    //     if (currentIndexLinear >= totalSlides - Math.ceil(visibleSlides)) {
800    //       currentIndexLinear = Math.ceil(visibleSlides);
801    //       updateSliderPosition(false);
802    //     } else if (currentIndexLinear <= 0) {
803    //       currentIndexLinear = totalSlides - Math.ceil(visibleSlides) - 1;
804    //       updateSliderPosition(false);
805    //     }
806    //   }, { once: true });
807    // }
808
809    // function handleDragStart(e) {
810    //   isDragging = true;
811    //   startX = e.type.includes('mouse') ? e.pageX : e.touches[0].pageX;
812    //   startTime = Date.now();
813    //   if (track){
814    //     track.classList.add('dragging');
815    //   }
816    // }
817
818    // function handleDragMove(e) {
819    //   if (!isDragging) return;
820    //   e.preventDefault();
821
822    //   currentX = e.type.includes('mouse') ? e.pageX : e.touches[0].pageX;
823    //   const diff = currentX - startX;
824
825    //   // Applica il movimento durante il drag
826    //   const translateX = -currentIndexLinear * getTileWidth() + diff;
827    //   if (track){
828    //     track.style.transform = `translateX(${translateX}px)`;
829    //   }
830    // }
831
832    // function handleDragEnd() {
833    //   if (!isDragging) return;
834
835    //   const dragDuration = Date.now() - startTime;
836    //   const dragDistance = currentX - startX;
837
838    //   // Determina se cambiare slide in base alla distanza o velocità del drag
839    //   if (Math.abs(dragDistance) > dragThreshold || (Math.abs(dragDistance) > 20 && dragDuration < 200)) {
840    //     if (dragDistance > 0) {
841    //       prev();
842    //     } else {
843    //       next();
844    //     }
845    //   } else {
846    //     // Torna alla posizione originale se il drag non è sufficiente
847    //     updateSliderPosition();
848    //   }
849
850    //   isDragging = false;
851    //   if (track){
852    //     track.classList.remove('dragging');
853    //   }
854    // }
855
856    // // Event Listeners
857    // if (track){
858    //   track.addEventListener('mousedown', handleDragStart);
859    //   track.addEventListener('touchstart', handleDragStart);
860    //   track.addEventListener('mousemove', handleDragMove);
861    //   track.addEventListener('touchmove', handleDragMove);
862    //   track.addEventListener('mouseup', handleDragEnd);
863    //   track.addEventListener('touchend', handleDragEnd);
864    //   track.addEventListener('mouseleave', handleDragEnd);
865    // }
866
867    // // Inizializzazione
868    // cloneElements();
869
870    // // Gestione del resize
871    // let resizeTimeout;
872    // window.addEventListener('resize', () => {
873    //   clearTimeout(resizeTimeout);
874    //   resizeTimeout = setTimeout(() => {
875    //     updateSliderPosition(false);
876    //   }, 100);
877    // });
878
879    // // Configurazione slider
880    // const sliderConfig = {
881    //   autoplay: true,          // abilita/disabilita autoplay
882    //   autoplaySpeed: 300000000,     // velocità autoplay in ms
883    //   pauseOnHover: true       // pausa autoplay al passaggio del mouse
884    // };
885
886    // // Gestione autoplay
887    // let autoplayInterval;
888
889    // function startAutoplay() {
890    //   if (sliderConfig.autoplay) {
891    //     autoplayInterval = setInterval(next, sliderConfig.autoplaySpeed);
892    //   }
893    // }
894
895    // function stopAutoplay() {
896    //   clearInterval(autoplayInterval);
897    // }
898
899    // // Gestione pause on hover
900    // if (sliderConfig.pauseOnHover) {
901    //   if (track){
902    //     track.addEventListener('mouseenter', stopAutoplay);
903    //     track.addEventListener('mouseleave', startAutoplay);
904    //   }
905    // }
906
907    // Avvia autoplay se abilitato
908    // startAutoplay();
909
910    // #endregion
911});

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.