PageSourceSearch

https://dacor.de/wp-content/plugins/dacor-shop/public/assets/js/carousel.js?ver=1.7.17

js dacor.de collected 2026-10-06 08:56:18 UTC 10,945 bytes, 322 lines download raw bytes

1/**
2 * DACOR Shop - Carousel Component
3 * @package DACOR_Shop
4 */
5
6(function($) {
7    'use strict';
8
9    const DacorCarousel = {
10        currentIndex: 0,
11        itemsPerView: 3,
12        totalItems: 0,
13        enableSwipe: true,
14        $carousel: null,
15        $track: null,
16        $items: null,
17        $prevBtn: null,
18        $nextBtn: null,
19        $indicators: null,
20        touchStartX: 0,
21        touchEndX: 0,
22        isDragging: false,
23        startOffset: 0,
24
25        init: function(options) {
26            const width = $(window).width();
27            if (width < 768) {
28                this.itemsPerView = 1;
29            } else if (width < 1024) {
30                this.itemsPerView = 2;
31            } else {
32                this.itemsPerView = options.itemsPerView || 3;
33            }
34
35            this.enableSwipe = options.enableSwipe !== false;
36            this.onSelect = options.onSelect || function() {};
37
38            this.$carousel = $('#dacor-tarif-carousel');
39            if (this.$carousel.length === 0) return;
40
41            this.$track = this.$carousel.find('.carousel-track');
42            this.$items = this.$track.find('.dacor-tarif-card, .dacor-modem-card');
43            this.$prevBtn = this.$carousel.find('.carousel-prev');
44            this.$nextBtn = this.$carousel.find('.carousel-next');
45            this.$indicators = $('.carousel-indicators .indicator');
46
47            this.totalItems = this.$items.length;
48            this.currentIndex = 0;
49
50            this.setupLayout();
51            this.setupControls();
52            if (this.enableSwipe) {
53                this.setupTouchEvents();
54            }
55
56            this.updateView();
57        },
58
59        setupLayout: function() {},
60
61        setupControls: function() {
62            const self = this;
63
64            this.$prevBtn.off('click').on('click', function() {
65                self.prev();
66            });
67
68            this.$nextBtn.off('click').on('click', function() {
69                self.next();
70            });
71
72            this.$indicators.off('click').on('click', function() {
73                const index = $(this).data('index');
74                self.goTo(index);
75            });
76
77            // Only handle arrow keys when carousel or its children have focus
78            self.$carousel.off('keydown.carousel').on('keydown.carousel', function(e) {
79                if (e.key === 'ArrowLeft') {
80                    e.preventDefault();
81                    self.prev();
82                } else if (e.key === 'ArrowRight') {
83                    e.preventDefault();
84                    self.next();
85                }
86            });
87        },
88
89        setupTouchEvents: function() {
90            const self = this;
91            const viewport = this.$carousel.find('.carousel-viewport')[0];
92
93            viewport.addEventListener('mousedown', function(e) {
94                // Don't intercept clicks on buttons or links
95                if (e.target.closest('button, a, input, label')) return;
96                self.handleDragStart(e.clientX);
97            });
98
99            viewport.addEventListener('mousemove', function(e) {
100                if (self.isDragging) {
101                    self.handleDragMove(e.clientX);
102                }
103            });
104
105            viewport.addEventListener('mouseup', function(e) {
106                if (self.isDragging) {
107                    self.handleDragEnd(e.clientX);
108                }
109            });
110
111            viewport.addEventListener('mouseleave', function() {
112                if (self.isDragging) {
113                    self.isDragging = false;
114                    self.$track.css('transition', 'transform 0.4s cubic-bezier(0.4, 0, 0.2, 1)');
115                }
116            });
117
118            viewport.addEventListener('touchstart', function(e) {
119                self.handleDragStart(e.touches[0].clientX);
120            }, { passive: true });
121
122            viewport.addEventListener('touchmove', function(e) {
123                if (self.isDragging) {
124                    self.handleDragMove(e.touches[0].clientX);
125                }
126            }, { passive: true });
127
128            viewport.addEventListener('touchend', function(e) {
129                if (self.isDragging) {
130                    self.handleDragEnd(e.changedTouches[0].clientX);
131                }
132            });
133        },
134
135        handleDragStart: function(clientX) {
136            this.isDragging = true;
137            this.touchStartX = clientX;
138            this.startOffset = this.getCurrentOffset();
139            this.$track.css('transition', 'none');
140            this.$carousel.addClass('dragging');
141        },
142
143        handleDragMove: function(clientX) {
144            if (!this.isDragging) return;
145
146            const diff = clientX - this.touchStartX;
147            const $firstCard = this.$items.first();
148            const cardWidth = $firstCard.length ? $firstCard.outerWidth() : 327;
149            const gap = $(window).width() < 768 ? 40 : 59;
150            const itemWidth = cardWidth + gap;
151            const maxOffset = 0;
152            const maxIndex = Math.max(0, this.totalItems - this.itemsPerView);
153            const minOffset = -(maxIndex * itemWidth);
154
155            let newOffset = this.startOffset + diff;
156
157            // Edge resistance
158            if (newOffset > maxOffset) {
159                newOffset = maxOffset + (newOffset - maxOffset) * 0.3;
160            } else if (newOffset < minOffset) {
161                newOffset = minOffset + (newOffset - minOffset) * 0.3;
162            }
163
164            this.$track.css('transform', `translateX(${newOffset}px)`);
165        },
166
167        handleDragEnd: function(clientX) {
168            this.isDragging = false;
169            this.touchEndX = clientX;
170            this.$track.css('transition', 'transform 0.4s cubic-bezier(0.4, 0, 0.2, 1)');
171            this.$carousel.removeClass('dragging');
172
173            const diff = this.touchEndX - this.touchStartX;
174            const threshold = 50;
175
176            if (Math.abs(diff) > threshold) {
177                if (diff > 0) {
178                    this.prev();
179                } else {
180                    this.next();
181                }
182            } else {
183                this.updateView();
184            }
185        },
186
187        getCurrentOffset: function() {
188            const transform = this.$track.css('transform');
189            if (transform === 'none') return 0;
190
191            const matrix = transform.match(/matrix\(([^)]+)\)/);
192            if (!matrix) return 0;
193
194            const values = matrix[1].split(',');
195            return parseFloat(values[4]);
196        },
197
198        next: function() {
199            const maxIndex = this.totalItems - this.itemsPerView;
200            if (this.currentIndex < maxIndex) {
201                this.currentIndex++;
202                this.updateView();
203            }
204        },
205
206        prev: function() {
207            if (this.currentIndex > 0) {
208                this.currentIndex--;
209                this.updateView();
210            }
211        },
212
213        goTo: function(index) {
214            const maxIndex = this.totalItems - this.itemsPerView;
215            if (index >= 0) {
216                this.currentIndex = Math.min(index, maxIndex);
217                this.updateView();
218            }
219        },
220
221        updateView: function() {
222            const $firstCard = this.$items.first();
223            const cardWidth = $firstCard.length ? $firstCard.outerWidth() : 327;
224            const gap = $(window).width() < 768 ? 40 : 59;
225            const offsetPx = -(this.currentIndex * (cardWidth + gap));
226
227            this.$track.css('transform', `translateX(${offsetPx}px)`);
228            this.updateButtons();
229            this.updateIndicators();
230            this.updateARIA();
231        },
232
233        updateButtons: function() {
234            const maxIndex = this.totalItems - this.itemsPerView;
235
236            if (this.currentIndex === 0) {
237                this.$prevBtn.prop('disabled', true).attr('aria-disabled', 'true');
238            } else {
239                this.$prevBtn.prop('disabled', false).attr('aria-disabled', 'false');
240            }
241
242            if (this.currentIndex >= maxIndex) {
243                this.$nextBtn.prop('disabled', true).attr('aria-disabled', 'true');
244            } else {
245                this.$nextBtn.prop('disabled', false).attr('aria-disabled', 'false');
246            }
247        },
248
249        updateIndicators: function() {
250            this.$indicators.removeClass('active');
251
252            for (let i = 0;
252 i < this.itemsPerView; i++) {
253                const index = this.currentIndex + i;
254                if (index < this.totalItems) {
255                    this.$indicators.eq(index).addClass('active');
256                }
257            }
258        },
259
260        updateARIA: function() {
261            const currentItem = this.currentIndex + 1;
262            const lastVisible = Math.min(this.currentIndex + this.itemsPerView, this.totalItems);
263            this.$carousel.find('.carousel-viewport').attr('aria-label', `Zeige Tarif ${currentItem} bis ${lastVisible} von ${this.totalItems}`);
264        },
265
266        handleResize: function() {
267            const self = this;
268            let resizeTimer;
269            const initialWidth = $(window).width();
270            if (initialWidth < 768) {
271                self.itemsPerView = 1;
272            } else if (initialWidth < 1024) {
273                self.itemsPerView = 2;
274            } else {
275                self.itemsPerView = 3;
276            }
277            self.updateView();
278
279            $(window).on('resize', function() {
280                clearTimeout(resizeTimer);
281                resizeTimer = setTimeout(function() {
282                    const width = $(window).width();
283
284                    if (width < 768) {
285                        self.itemsPerView = 1;
286                    } else if (width < 1024) {
287                        self.itemsPerView = 2;
288                    } else {
289                        self.itemsPerView = 3;
290                    }
291
292                    self.setupLayout();
293                    self.updateView();
294                }, 250);
295            });
296        },
297
298        destroy: function() {
299            if (this.$carousel) {
300                this.$carousel.off('keydown.carousel');
301            }
302            $(window).off('resize');
303
304            if (this.$carousel) {
305                this.$carousel.find('*').off();
306            }
307        }
308    };
309
310    window.DacorCarousel = DacorCarousel;
311
312    $(document).ready(function() {
313        if ($('#dacor-tarif-carousel').length > 0) {
314            DacorCarousel.init({
315                itemsPerView: 3,
316                enableSwipe: true
317            });
318            DacorCarousel.handleResize();
319        }
320    });
321
322})(jQuery);

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.