PageSourceSearch

https://www.duinrell.com/modules/carousel/swiper-wrapper.js?ts=1764345941

js duinrell.com collected 2026-10-01 14:49:54 UTC 10,054 bytes, 282 lines download raw bytes

1import {CLONE_CLASS, REAL_INDEX_ATTR, ACTIVE_CLASS, DOT_CLASS, DOT_SELECTED_CLASS} from './constants.js';
2import {fadeTo, swapText} from './utils.js';
3import {CarouselBase} from "./base-carousel.js";
4
5let SwiperLib = null;
6
7/** Dynamically load Swiper ES module */
8async function loadSwiper() {
9    if (!SwiperLib) {
10        const mod = await import('https://cdn.jsdelivr.net/npm/swiper@12/swiper-bundle.min.mjs');
11        SwiperLib = mod.Swiper;
12    }
13    return SwiperLib;
14}
15
16/** Swiper wrapper implementing CarouselBase interface */
17export class Carousel extends CarouselBase {
18    constructor(element, opts = {}) {
19        super(element);
20        this.element = element;
21        this.opts = opts;
22        this.swiper = null;
23        this.slides = [];
24        this.fadeTo = fadeTo;
25        this.swapText = swapText;
26    }
27
28    async init() {
29        if (this.element.__initialized) return;
30        this.element.__initialized = true;
31
32        const Swiper = await loadSwiper();
33        const container = this.element.querySelector('.carousel__viewport');
34        if (!Swiper || !container) return;
35
36        this.slides = [...container.querySelectorAll('.carousel__slide')];
37
38        const swiperOpts = {
39            centeredSlides: false,
40            //loop: this.opts.loop ?? true,
41            loop: true,
42            slidesPerView: 'auto',
43            //slidesPerView: 4.5,
44            //loopFillGroupWithBlank: true,
45            loopAddBlankSlides: true,
46            loopAdditionalSlides: 10,
47            slidesPerGroup: 1,
48            //loopedSlides: 5,
49            //speed: this.opts.speed ?? 500,
50            wrapperClass: 'carousel__container',
51            slideClass: 'carousel__slide',
52            navigation: {
53                nextEl: this.element.querySelector('.carousel__next'),
54                prevEl: this.element.querySelector('.carousel__prev'),
55            },
56            pagination: {
57                el: this.element.querySelector('.carousel__dots'),
58                clickable: true,
59                bulletClass: 'carousel__dot',
60                bulletActiveClass: 'carousel__dot--selected',
61                renderBullet: (index, className) =>
62                    `<button class="${className}" type="button"><span>${index + 1}</span></button>`,
63            },
64            on: {
65                slideChange: () => this._emit('select', this.getActiveIndex()),
66                update: () => this._emit(('reInit')),
67                slidesLengthChange: () => this._emit(('reInit')),
68            },
69            ...this.opts,
70        };
71
72        console.log(swiperOpts);
73
74        this.swiper = new Swiper(container, swiperOpts);
75
76        this.updateActiveClass();
77        this.initDots();
78
79        // updateSlideIndex(this);
80        // this.on('reInit', () => updateSlideIndex(this))
81    }
82
83    updateActiveClass() {
84        const activeIndex = this.getActiveIndex();
85        this.slides.forEach((slide, i) =>
86            slide.classList.toggle(ACTIVE_CLASS, i === activeIndex)
87        );
88    }
89
90    initDots() {
91        const dotsNode = this.element.querySelector('.carousel__dots');
92        if (!dotsNode) return;
93
94        const updateDots = () => {
95            const activeIndex = this.getActiveIndex();
96            const dots = [...dotsNode.querySelectorAll(`.${DOT_CLASS}`)];
97            dots.forEach((dot, i) =>
98                dot.classList.toggle(DOT_SELECTED_CLASS, i === activeIndex)
99            );
100        };
101
102        this.swiper.on('slideChange', updateDots);
103        updateDots();
104    }
105
106    next() {
107        this.swiper?.slideNext();
108    }
109
110    prev() {
111        this.swiper?.slidePrev();
112    }
113
114// goTo(index) {
115//     if (!this.swiper) return;
116//
117//     const slidesCount = this.slides.length;
118//     const current = this.getActiveIndex();
119//     console.log(current, index)
120//     if (index === current) return;
121//
122//     let targetIndex = index;
123//
124//     if (this.swiper.params.loop) {
125//         // Calculate forward-only distance in loop
126//         const distance = (index - current + slidesCount) % slidesCount;
127//         targetIndex = current + distance;
128//     }
129//     console.log(targetIndex)
130//     // Use slideTo for smooth animation
131//     this.swiper.slideTo(targetIndex, this.swiper.params.speed, true);
132// }
133
134    goTo(index) {
135        console.log(index);
136
137        //scrollLeftAlign(this.swiper, index);
138        //scrollLeftAlign(swiper, 0, 0);
139
140        this.swiper?.slideToLoop(index);
141        //this.swiper?.slideTo(index);
142        //this.goToDirectional(index, 'forward');
143    }
144
145    /**
146     * Scroll Swiper so the given real slide index becomes the left-most visible slide.
147     * Works with loop:true (handles duplicated slides) and non-loop mode.
148     *
149     * @param {Swiper} swiper - Swiper instance
150     * @param {number} index  - real slide index (0..N-1)
151     * @param {number}
151 [speed] - animation speed (ms). Default uses swiper.params.speed
152     */
153    scrollLeftAlign(swiper, index, speed = swiper?.params?.speed ?? 300) {
154        if (!swiper) return;
155
156        // determine real slides count
157        // prefer swiper.slides.length - looped duplicates if loop is on
158        // but we compute from swiper.slides and duplicate marker class for robustness
159        const allSlides = Array.from(swiper.slides || []);
160        // Swiper duplicate slides commonly have class 'swiper-slide-duplicate'
161        const realSlides = allSlides.filter(s => !s.classList.contains('swiper-slide-duplicate'));
162        const realTotal = realSlides.length || allSlides.length;
163
164        if (realTotal === 0) return;
165
166        // normalize index into range [0, realTotal-1]
167        const normalizedIndex = ((index % realTotal) + realTotal) % realTotal;
168
169        // If not looped, just go to that index
170        if (!swiper.params.loop) {
171            swiper.slideTo(normalizedIndex, speed);
172            return;
173        }
174
175        // When loop: find the internal index that corresponds to the real slide.
176        // Swiper creates loopedSlides copies at start and end; swiper.loopedSlides is available.
177        const loopedSlides = swiper.loopedSlides || Math.ceil(swiper.params.slidesPerView) || 0;
178
179        // target internal index: offset by loopedSlides
180        const target = loopedSlides + normalizedIndex;
181
182        // Use slideTo to force the exact internal index (not slideToLoop which picks shortest route).
183        swiper.slideTo(target, speed);
184    }
185
186    goToDirectional(index, direction = 'forward', speed = this.swiper.params.speed) {
187        const swiper = this.swiper;
188        if (!swiper) return;
189
190        const total = swiper.slides.length;
191        const realTotal = swiper.slides.filter(s => !s.classList.contains('swiper-slide-duplicate')).length;
192        const current = swiper.realIndex;
193
194        let target;
195
196        if (direction === 'forward') {
197            target = index >= current
198                ? swiper.loopedSlides + index
199                : swiper.loopedSlides + index + realTotal;
200        } else {
201            target = index <= current
202                ? swiper.loopedSlides + index
203                : swiper.loopedSlides + index - realTotal;
204        }
205
206        swiper.slideTo(target, speed);
207    }
208
209    getActiveIndex() {
210        return this.swiper?.realIndex ?? this.swiper?.activeIndex ?? 0;
211    }
212
213    getSlides() {
214        return this.slides;
215    }
216
217    destroy() {
218        this.swiper?.destroy(true, true);
219        this.swiper = null;
220    }
221}
222
223function updateSlideIndex(carouselInstance) {
224    const slides = carouselInstance.getSlides();
225    const active = carouselInstance.getActiveIndex();
226
227    slides.forEach((slide, i) => {
228        slide.dataset.carouselSlideIndex = i;
229    });
230}
231
232/**
233 * Scroll Swiper so the given real slide index becomes the left-most visible slide.
234 * Works with loop:true (handles duplicated slides) and non-loop mode.
235 *
236 * @param {Swiper} swiper - Swiper instance
237 * @param {number} index  - real slide index (0..N-1)
238 * @param {number} [speed] - animation speed (ms). Default uses swiper.params.speed
239 */
240function scrollLeftAlign(swiper, index, speed = swiper?.params?.speed ?? 300) {
241    if (!swiper) return;
242
243    // determine real slides count
244    // prefer swiper.slides.length - looped duplicates if loop is on
245    // but we compute from swiper.slides and duplicate marker class for robustness
246    const allSlides = Array.from(swiper.slides || []);
247    // Swiper duplicate slides commonly have class 'swiper-slide-duplicate'
248    const realSlides = allSlides.filter(s => !s.classList.contains('swiper-slide-duplicate'));
249    const realTotal = realSlides.length || allSlides.length;
250
251    if (realTotal === 0) return;
252
253    // normalize index into range [0, realTotal-1]
254    const normalizedIndex = ((index % realTotal) + realTotal) % realTotal;
255
256    // If not looped, just go to that index
257    if (!swiper.params.loop) {
258        swiper.slideTo(normalizedIndex, speed);
259        return;
260    }
261
262    // When loop: find the internal index that corresponds to the real slide.
263    // Swiper creates loopedSlides copies at start and end; swiper.loopedSlides is available.
264    const loopedSlides = swiper.loopedSlides || Math.ceil(swiper.params.slidesPerView) || 0;
265
266    // target internal index: offset by loopedSlides
267    const target = loopedSlides + normalizedIndex;
268
269    // Use slideTo to force the exact internal index (not slideToLoop which picks shortest route).
270    swiper.slideTo(target, speed);
271}
272
273function loadSwiperStylesheet() {
274    const href = 'https://cdn.jsdelivr.net/npm/swiper@12/swiper-bundle.min.css';
275    // Check if already loaded
276    if (![...document.styleSheets].some(ss => ss.href === href)) {
277        const link = document.createElement('link');
278        link.rel = 'stylesheet';
279        link.href = href;
280        document.head.appendChild(link);
281    }
282}

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.