PageSourceSearch

https://www.cowdenbeathfc.com/modules/custom/cowden_views_carousel/js/carousel.js?tl5s95

js cowdenbeathfc.com collected 2026-09-24 10:03:32 UTC 9,460 bytes, 271 lines download raw bytes

1(function ($, Drupal, drupalSettings, once) {
2  Drupal.behaviors.cowdenViewsCarousel = {
3    attach: function (context, settings) {
4      $(once('cowden-carousel', '.cowden-views-carousel', context)).each(function () {
5        const $carousel = $(this);
6        const viewId = $carousel.data('view-id');
7        const displayId = $carousel.data('display-id');
8        const options = drupalSettings.cowdenViewsCarousel[viewId][displayId];
9
10        const $track = $carousel.find('.carousel-track');
11        const $items = $carousel.find('.carousel-item-wrapper');
12        let currentIndex = 0;
13        let isDragging = false;
14        let startX = 0;
15        let currentTranslate = 0;
16        let prevTranslate = 0;
17        let animationID = 0;
18        let autoScrollInterval;
19
20        function getItemsPerScreen() {
21          const width = window.innerWidth;
22          if (width >= 992) return options.items_desktop;
23          if (width >= 768) return options.items_tablet;
24          return options.items_mobile;
25        }
26
27        function updateItemWidths() {
28          if (options.fluid_width) {
29            $items.css('width', 'auto');
30            // Re-check if scrolling is needed and start/stop autoscroll accordingly.
31            const trackWidth = $track.get(0).scrollWidth;
32            const carouselWidth = $carousel.width();
33            if (trackWidth > carouselWidth) {
34              $carousel.removeClass('carousel-disabled');
35              startAutoScroll();
36            } else {
37              $carousel.addClass('carousel-disabled');
38              stopAutoScroll();
39            }
40            setPositionByIndex();
41            return;
42          }
43
44          const itemsPerScreen = getItemsPerScreen();
45          const itemWidth = 100 / itemsPerScreen;
46          $items.css('width', `${itemWidth}%`);
47
48          if ($items.length <= itemsPerScreen) {
49            $carousel.addClass('carousel-disabled');
50            stopAutoScroll();
51            currentIndex = 0;
52          }
53          else {
54            $carousel.removeClass('carousel-disabled');
55            startAutoScroll();
56          }
57
58          setPositionByIndex();
59        }
60
61        function setPositionByIndex() {
62          const itemsPerScreen = getItemsPerScreen();
63
64          if (options.fluid_width) {
65            let offset = 0;
66            const maxOffset = Math.max(0, $track.get(0).scrollWidth - $carousel.width());
67
68            for (let i = 0; i < currentIndex; i++) {
69              offset += $($items[i]).outerWidth(true);
70            }
71
72            if (offset > maxOffset) {
73              offset = maxOffset;
74              // Sync currentIndex if we've hit the end
75              // This is a bit tricky with fluid width, but let's at least prevent it from being impossible
76            }
77
78            currentTranslate = -offset;
79            prevTranslate = currentTranslate;
80            setSliderPosition();
81            return;
82          }
83
84          const maxIndex = Math.max(0, $items.length - itemsPerScreen);
85          if (currentIndex > maxIndex) currentIndex = maxIndex;
86
87          currentTranslate = currentIndex * -(100 / itemsPerScreen);
88          prevTranslate = currentTranslate;
89          setSliderPosition();
90        }
91
92        function setSliderPosition() {
93          const unit = options.fluid_width ? 'px' : '%';
94          $track.css('transform', `translateX(${currentTranslate}${unit})`);
95        }
96
97        // Dragging logic
98        $carousel.on('mousedown touchstart', function (e) {
99          if ($carousel.hasClass('carousel-disabled')) return;
100          dragStart(e);
101        });
102        $carousel.on('mouseup touchend', function (e) {
103          if ($carousel.hasClass('carousel-disabled')) return;
104          dragEnd(e);
105        });
106        $carousel.on('mousemove touchmove', function (e) {
107          if ($carousel.hasClass('carousel-disabled')) return;
108          dragAction(e);
109        });
110
111        function dragStart(event) {
112          if ($(event.target).closest('.carousel-nav').length) return;
113          isDragging = true;
114          startX = getPositionX(event);
115          $track.css('transition', 'none');
116          stopAutoScroll();
117          animationID = requestAnimationFrame(animation);
118        }
119
120        function dragEnd() {
121          isDragging = false;
122          cancelAnimationFrame(animationID);
123          $track.css('transition', 'transform 0.5s ease-in-out');
124
125          const movedBy = currentTranslate - prevTranslate;
126
127          if (options.fluid_width) {
128            const threshold = 50; // pixels
129            if (movedBy < -threshold && currentIndex < $items.length - 1) {
130              const maxOffset = Math.max(0, $track.get(0).scrollWidth - $carousel.width());
131              let currentOffset = 0;
132              for (let i = 0; i < currentIndex; i++) {
133                currentOffset += $($items[i]).outerWidth(true);
134              }
135
136              if (currentOffset < maxOffset) {
137                currentIndex += 1;
138              }
139            }
140            if (movedBy > threshold && currentIndex > 0) currentIndex -= 1;
141          } else {
142            const itemsPerScreen = getItemsPerScreen();
143            const threshold = 5 / itemsPerScreen; // % of total width
144            if (movedBy < -threshold && currentIndex < $items.length - itemsPerScreen) currentIndex += 1;
145            if (movedBy > threshold && currentIndex > 0) currentIndex -= 1;
146          }
147
148          setPositionByIndex();
149          startAutoScroll();
150        }
151
152        function dragAction(event) {
153          if (isDragging) {
154            const currentX = getPositionX(event);
155            const diffX = currentX - startX;
156
157            if (options.fluid_width) {
158              currentTranslate = prevTranslate + diffX;
159            } else {
160              const containerWidth = $carousel.width();
161              const diffPerc = (diffX / containerWidth) * 100;
162              currentTranslate = prevTranslate + diffPerc;
163            }
164          }
165        }
166
167        function getPositionX(event) {
168          return event.type.includes('mouse') ? event.pageX : event.touches[0].clientX;
169        }
170
171        function animation() {
172          setSliderPosition();
173          if (isDragging) requestAnimationFrame(animation);
174        }
175
176        function startAutoScroll() {
177          if (!options.autoscroll || autoScrollInterval) return;
178
179          const checkNeeded = () => {
180            const trackWidth = $track.get(0).scrollWidth;
181            const carouselWidth = $carousel.width();
182            return options.fluid_width
183              ? trackWidth > carouselWidth
184              : $items.length > getItemsPerScreen();
185          };
186
187          if (checkNeeded()) {
188            autoScrollInterval = setInterval(() => {
189              if (options.fluid_width) {
190                const maxOffset = Math.max(0, $track.get(0).scrollWidth - $carousel.width());
191                let nextOffset = 0;
192                for (let i = 0; i <= currentIndex; i++) {
193                  nextOffset += $($items[i]).outerWidth(true);
194                }
195
196                if (currentIndex < $items.length - 1 && nextOffset < maxOffset + 5) {
197                  currentIndex++;
198                } else {
199                  currentIndex = 0;
200                }
201              } else {
202                const itemsPerScreen = getItemsPerScreen();
203                if (currentIndex < $items.length - itemsPerScreen) {
204                  currentIndex++;
205                } else {
206                  currentIndex = 0;
207                }
208              }
209              setPositionByIndex();
210            }, options.autoscroll_speed);
211          }
212        }
213
214        function stopAutoScroll() {
215          if (autoScrollInterval) {
216            clearInterval(autoScrollInterval);
217            autoScrollInterval = null;
218          }
219        }
220
221        $(window).on('resize', updateItemWidths);
222        $(window).on('load', updateItemWidths);
223        // Also listen for images inside the track loading.
224        $track.find('img').on('load', updateItemWidths);
225
226        // Initial setup
227        updateItemWidths();
228
229        // Navigation buttons
230        $carousel.find('.carousel-prev').on('click', function(e) {
231          e.preventDefault();
232          e.stopPropagation();
233          stopAutoScroll();
234          if (currentIndex > 0) {
235            currentIndex--;
236          } else {
237            // Loop to end
238            const itemsPerScreen = getItemsPerScreen();
239            currentIndex = Math.max(0, $items.length - (options.fluid_width ? 1 : itemsPerScreen)
239);
240          }
241          setPositionByIndex();
242          startAutoScroll();
243        });
244
245        $carousel.find('.carousel-next').on('click', function(e) {
246          e.preventDefault();
247          e.stopPropagation();
248          stopAutoScroll();
249          const itemsPerScreen = getItemsPerScreen();
250          const maxIndex = options.fluid_width
251            ? $items.length - 1
252            : Math.max(0, $items.length - itemsPerScreen);
253
254          if (currentIndex < maxIndex) {
255            currentIndex++;
256          } else {
257            // Loop to start
258            currentIndex = 0;
259          }
260          setPositionByIndex();
261          startAutoScroll();
262        });
263
264        // Give it several delays to ensure images/layouts are settled for fluid width
265        setTimeout(updateItemWidths, 100);
266        setTimeout(updateItemWidths, 500);
267        setTimeout(updateItemWidths, 2000);
268      });
269    }
270  };
271})(jQuery, Drupal, drupalSettings, once);

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.