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.