1(function($, Drupal, drupalSettings) { 2 3 'use strict'; 4 5 Drupal.behaviors.sbdCarousel = { 6 attach: function(context, drupalSettings) { 7 8 function updateCurrentSlides($page_indicator, num) { 9 $page_indicator.find('.current').text(num); 10 } 11 12 $('.sbd-carousel-component', context).once('sbd-carousel-init').each(function () { 13 var $this = $(this); 14 var $page_indicator = $this.find('.sbd-carousel-page-indicator'); 15 var $sbd_carousel = $this.find('.sbd-carousel'); 16 var $sbd_slider = $this.find('.sbd-slider'); 17 var $sbd_pdpSlider = $this.find('#product-slider'); 18 var $sbd_pdpThumbnailSlider = $this.find('#thumbnail-slider'); 19 var $sbd_pdpThumbnailSliderMobile = $this.find('#thumbnail-slider-mobile'); 20 // Override variable for CoreMDS2024. 21 if (drupalSettings.isCoreMds2024) { 22 var $sbd_pdpSlider = null; 23 var $sbd_pdpThumbnailSlider = null; 24 var $sbd_pdpThumbnailSliderMobile = null; 25 var $sbd_pdpSlider = $this.find('.slider-main #block-duplo-pdp-slider'); 26 var $sbd_pdpThumbnailSlider = $this.find('.thumbnail-slider-desktop #block-duplo-pdp-slider-thumbnail'); 27 var $sbd_pdpThumbnailSliderMobile = $this.find('.thumbnail-slider-mobile #block-duplo-pdp-slider'); 28 } 29 var numSlides = $sbd_carousel.data('slides-to-show'); 30 var numSliderSlides = $sbd_slider.data('slides-to-show'); 31 var currentSlides = 0; 32 var $progressBar = $this.find('.progress'); 33 var $progressBarLabel = $this.find( '.slider__label' ); 34 35 // Sets PDP slider sync, the asNavFor feature is deprecated in Accessible Slick 36 // so we're replicating the behavior here 37 function setSlides(e, slider) { 38 $(slider.$slides.find('.thumbnail-image').first()).each(function(index) { 39 $(this).attr('aria-current', true); 40 }); 41 42 $(slider.$slides.find('.thumbnail-image')).each(function(index) { 43 $(this).on('click', function() { 44 // Move aria-current="true" to this button 45 $(slider.$slides.find('.thumbnail-image').removeAttr('aria-current')); 46 $(this).attr('aria-current', true); 47 48 // Change the main image to match this thumbnail button 49 var index = $(this).closest('.slick-slide').data('slick-index'); 50 $('#product-slider').slick('slickGoTo', index); 51 if(drupalSettings.isCoreMds2024) { 52 $('.slider-main #block-duplo-pdp-slider').slick('slickGoTo', index); 53 } 54 }); 55 }); 56 57 if (drupalSettings.isCoreMds2024 && slider.$slides.length > 8) { 58 var viewLessSelector = $('#block-duplo-pdp-slider-thumbnail .view-less'); 59 var viewMoreSelector = $('#block-duplo-pdp-slider-thumbnail .view-more-wrapper'); 60 var thumbnailSliderDesktop = $('.thumbnail-slider-desktop'); 61 62 if (slider.$slider.is('#block-duplo-pdp-slider-thumbnail')) { 63 var thumbnailSlides = $('.slick-slide', thumbnailSliderDesktop).slice(8); 64 thumbnailSlides.hide(); 65 66 viewMoreSelector.on('click', function() { 67 $(this).hide(); 68 thumbnailSlides.show(); 69 viewLessSelector.show(); 70 }); 71 72 viewLessSelector.on('click', function() { 73 $(this).hide(); 74 viewMoreSelector.show(); 75 thumbnailSlides.hide(); 76 }); 77 } 78 } 79 }; 80 81 // Change the main image whenever a thumbnail button is activated 82 $sbd_pdpThumbnailSlider.on('init', setSlides); 83 // Change the main image whenever a thumbnail button is activated 84 $sbd_pdpThumbnailSliderMobile.on('init', setSlides); 85 86 // Initialize Slick carousel 87 $sbd_carousel.on('init', function(event, slick) { 88 // Dynamically calculate current slides per carousel
89 currentSlides = slick.$slides.filter('.slick-active').length; 90 $page_indicator.find('.total').text(slick.slideCount); // Update total slide count 91 updateCurrentSlides($page_indicator, currentSlides); // Update the current slide number 92 }); 93 94 $sbd_carousel.on('afterChange', function(event, slick, currentSlide) { 95 // Calculate the current slide based on slick's internal state 96 currentSlides = currentSlide + slick.$slides.filter('.slick-active').length; 97 updateCurrentSlides($page_indicator, currentSlides); 98 99 var $firstCarouselItem = $(this).find(".slick-active").first().find("a").first(); 100 if ($firstCarouselItem.length) { 101 $firstCarouselItem.trigger("focus"); 102 } 103 }); 104 105 // Progress bar for SBD carousel in core_2_0 106 $sbd_carousel.on('beforeChange', function(event, slick, currentSlide, nextSlide) { 107 Drupal.behaviors.sbdCarousel.sliderProgressCal(slick, nextSlide, $progressBar, $progressBarLabel); 108 }); 109 110 $sbd_carousel.on('init', function(event, slick) { 111 Drupal.behaviors.sbdCarousel.sliderProgressCal(slick, 0, $progressBar, $progressBarLabel); 112 }); 113 114 // SBD Carousel is used to render product tiles and other product carousels in various pages 115 $sbd_carousel.slick({ 116 autoplay: false, 117 infinite: false, 118 slidesToShow: numSlides, 119 slidesToScroll: 1, 120 rtl: ($('html').attr('dir') === 'rtl' || $this.hasClass('rtl-test')), 121 prevArrow: ($this.find('.slick-prev')), 122 nextArrow: ($this.find('.slick-next')), 123 responsive: [ 124 { 125 breakpoint: 1280, 126 settings: { 127 slidesToShow: numSlides === 1 ? 1 : 3 128 } 129 }, 130 { 131 breakpoint: 1024, 132 settings: { 133 slidesToShow: numSlides === 1 ? 1 : 2 134 } 135 }, 136 { 137 breakpoint: 728, 138 settings: { 139 slidesToShow: 1 140 } 141 } 142 ] 143 }); 144 145 // Keydown event for handling keyboard navigation (Enter/Space for prev/next) 146 $(document).on("keydown", ".slick-prev, .slick-next", function (e) { 147 if (e.key === "Enter" || e.key === "Space") { 148 e.preventDefault(); 149 150 var $carouselWrapper = $(this).closest(".sbd-carousel-page-indicator").siblings(".sbd-carousel").first(); 151 152 // Determine the direction (previous or next) 153 if ($(this).hasClass("slick-prev")) { 154 $carouselWrapper.slick("slickPrev"); 155 } else if ($(this).hasClass("slick-next")) { 156 $carouselWrapper.slick("slickNext"); 157 } 158 } 159 }); 160 161 // SBD Slider is used in hero slider sections 162 $sbd_slider.slick({ 163 infinite: true, 164 dots: true, 165 speed: 800, 166 slidesToShow: numSliderSlides, 167 slidesToScroll: 1, 168 rtl: ($('html').attr('dir') === 'rtl' || $this.hasClass('rtl-test')), 169 pauseOnDotsHover: true, 170 pauseOnHover: true, 171 swipe: true, 172 touchMove: true, 173 autoplay: false, 174 slidesToScroll: 1, 175 useTransform: false, 176 regionLabel: 'Hero Slider', 177 responsive: [ 178 { 179 breakpoint: 1280, 180 settings: { 181 slidesToShow: numSliderSlides === 1 ? 1 : 3 182 } 183 }, 184 { 185 breakpoint: 1024, 186 settings: { 187 slidesToShow: numSliderSlides === 1 ? 1 : 2 188 } 189 }, 190 { 191 breakpoint: 728, 192 settings: { 193 slidesToShow: 1 194 } 195 } 196 ] 197 }); 198 199 // SBD PDP Slider is used in PDP pages to render product images in a gallery 200 $sbd_pdpSlider.slick({ 201 regionLabel: 'Product Carousel', 202 touchMove: true, 203 infinite: true, 204 slidesToShow: 1, 205 slidesToScroll: 1, 206 autoplay: false, 207 draggable:true, 208 swipe:true, 209 fade:false, 210 speed:300, 211 cssEase:"ease", 212 pauseOnHover:false, 213 arrows:true, 214 rows: 0, 215 useTransform: true, 216 matchHeights: {"target":"slide"}, 217 rtl: ($('html').attr('dir') === 'rtl' || $this.hasClass('rtl-test')), 218 prevArrow:"<button aria-label=\"Previous\" type=\"button\" class=\"slick-prev\"></button>", 219 nextArrow:"<button aria-label=\"Next\" type=\"button\" class=\"slick-next\"></button>", 220 responsive: [ 221 { 222 breakpoint: 728, 223 settings: {
224 arrows: false, 225 dots: true, 226 appendDots: drupalSettings.isCoreMds2024 ? ".slider-main #block-duplo-pdp-slider" : ".pdp-product-slider", 227 } 228 } 229 ] 230 }); 231 232 // SBD PDP Thumbnail Slider controls the navigation on PDP slider 233 $sbd_pdpThumbnailSlider.slick({ 234 rows: 0, 235 regionLabel: 'Product Thumbnail Carousel', 236 slidesToShow: 99, 237 slidesToScroll: 99, 238 cssEase: "ease", 239 draggable: false, 240 rtl: ($('html').attr('dir') === 'rtl' || $this.hasClass('rtl-test')), 241 }); 242 243 // SBD PDP Thumbnail Slider controls the navigation on PDP slider 244 $sbd_pdpThumbnailSliderMobile.slick({ 245 rows: 0, 246 regionLabel: 'Product Thumbnail Mobile Carousel', 247 slidesToShow: drupalSettings.isCoreMds2024 ? 4.3 : 3, 248 slidesToScroll: 1, 249 draggable:true, 250 swipe: true, 251 speed:300, 252 cssEase: "ease", 253 infinite: false, 254 rtl: ($('html').attr('dir') === 'rtl' || $this.hasClass('rtl-test')), 255 }); 256 257 updateCurrentSlides($page_indicator, currentSlides); 258 259 // Update the thumbnail slider when the user changes the main slider directly. 260 $('#product-slider, .slider-main #block-duplo-pdp-slider').on('beforeChange', function(e, slider, currentSlide, nextSlide) { 261 // Remove aria-current from the last selected thumbnail image button 262 $('#thumbnail-slider .thumbnail-image[aria-current="true"]').removeAttr('aria-current'); 263 $('#thumbnail-slider-mobile .thumbnail-image[aria-current="true"]').removeAttr('aria-current'); 264 265 // Select the thumbnail image button that goes with this main image. Most importantly, this updates Slick's internal state to be consistent with the visual change. 266 $('#thumbnail-slider').slick('slickGoTo', nextSlide); 267 $('#thumbnail-slider-mobile').slick('slickGoTo', nextSlide); 268 269 // Add aria-current="true" to the correct thumbnail image button to convey to screen readers that it's active. 270 $('#thumbnail-slider .thumbnail-image').eq(nextSlide).attr('aria-current', true); 271 $('#thumbnail-slider-mobile .thumbnail-image').eq(nextSlide).attr('aria-current', true); 272 273 // Only trigger for core 2024 274 if (drupalSettings.isCoreMds2024) { 275 // Remove aria-current from the last selected thumbnail image button 276 $('.thumbnail-slider-desktop .thumbnail-image[aria-current="true"]').removeAttr('aria-current'); 277 $('.thumbnail-slider-mobile .thumbnail-image[aria-current="true"]').removeAttr('aria-current'); 278 279 // Select the thumbnail image button that goes with this main image. Most importantly, this updates Slick's internal state to be consistent with the visual change. 280 $('.thumbnail-slider-desktop #block-duplo-pdp-slider-thumbail').slick('slickGoTo', nextSlide); 281 $('.thumbnail-slider-mobile #block-duplo-pdp-slider').slick('slickGoTo', nextSlide); 282 283 // Add aria-current="true" to the correct thumbnail image button to convey to screen readers that it's active. 284 $('.thumbnail-slider-desktop .thumbnail-image').eq(nextSlide).attr('aria-current', true); 285 $('.thumbnail-slider-mobile .thumbnail-image').eq(nextSlide).attr('aria-current', true); 286 287 var slides = slider.$slides; 288 if (slides.length > 8) { 289 var viewLessSelector = $('#block-duplo-pdp-slider-thumbnail .view-less'); 290 var viewMoreSelector = $('#block-duplo-pdp-slider-thumbnail .view-more-wrapper'); 291 var thumbnailSlides = $('.thumbnail-slider-desktop .slick-slide:gt(7)'); 292 if (nextSlide > 6) { 293 thumbnailSlides.show(); 294 viewMoreSelector.hide(); 295 viewLessSelector.show(); 296 } else if (nextSlide < 7) { 297 thumbnailSlides.hide(); 298 viewMoreSelector.show(); 299 viewLessSelector.hide(); 300 } 301 } 302 } 303 }); 304 // Only trigger for core 2024 305 if (drupalSettings.isCoreMds2024) { 306 // Button title translateable. 307 $('.sbd-carousel-page-indicator').each(function() { 308 // Button title translateable. 309 var title_next = $(this).find('.slick-next').attr('title'); 310 var title_previous = $(this).find('.slick-prev .slick-sr-only').text(); 311 $(this).find('.slick-next').attr('title', Drupal.t(title_next)); 312 $(this).find('.slick-prev').attr('title', title_previous); 313 }); 314 } 315 316 // Remove empty ID's from cloned slides 317 const slickSlideClones = document.querySelectorAll('.slick-cloned');
318 slickSlideClones.forEach(slickSlideClone => { 319 slickSlideClone.removeAttribute('id'); 320 }); 321 322 // Set aria disabled on inactive carousel slides 323 const slickSlides = document.querySelectorAll('.sbd-carousel .slick-slide'); 324 slickSlides.forEach(slickSlide => { 325 if (slickSlide.classList.contains('slick-active')) { 326 slickSlide.setAttribute('aria-disabled', false); 327 } else { 328 slickSlide.setAttribute('aria-disabled', true); 329 } 330 }); 331 332 // Temporarily removing this, it negatively affects all carousels including carousels in tabs 333 // We Need a better solution 334 // Customs slider logic to remove non existing product tiles 335 // if ($('.custom-slider').find('.product-teaser-section').length > 0) { 336 337 // $('.custom-slider').each(function () { 338 // if ($(this).find('.product-teaser-section').length > 0) { 339 // var count = $(this).find('.product-teaser-section').length; 340 // var counter = 0; 341 // $this = $(this); 342 // $(this).find('.product-teaser-section').each(function () { 343 // if ($(this).children().length == 0) { 344 // $(this).closest('.slick-slide').remove(); 345 // counter = counter + 1; 346 // $(this).closest('.sbd-carousel').addClass('hide-carousel'); 347 // } 348 // }); 349 350 // if ($this.find('.sbd-carousel .slick-track .slick-slide').length == 0) { 351 // console.log('length is 0'); 352 // $this.closest('.sbd-carousel-component').hide(); 353 // } else { 354 // if (count > counter) { 355 // var newCount = count - counter; 356 // } 357 // $(this).find('.sbd-carousel').attr('data-slides-to-show', newCount); 358 // $(this).parent().find('.top-header-container .total').text(newCount); 359 // if (newCount < 3) { 360 // $(this).parent().find('.top-header-container .current').text(newCount); 361 // } 362 // $(this).find(".sbd-carousel.slick-initialized").slick('reinit'); 363 // } 364 // } 365 // }); 366 // } 367 // if ($('.coh-style-grid-container').find('.product-teaser-section').length > 0) { 368 // $('.coh-style-grid-container').each(function () { 369 // if ($(this).find('.product-teaser-section').length > 0) { 370 // var count = $(this).find('.product-teaser-section').length; 371 // var counter = 0; 372 // $this = $(this); 373 // $($this).find('.product-teaser-section').each(function () { 374 // console.log($(this).parent); 375 // if ($(this).children().length == 0) { 376 // $(this).parent().remove(); 377 // counter = counter + 1; 378 // } 379 // }); 380 // if (count == counter) { 381 // $(this).remove(); 382 // } 383 // } 384 // }); 385 // } 386 }); 387 388 // Reinitialize slick on tab change, this solves an issue in which 389 // the slider did not display properly when opening a new tab 390 $('.coh-style-tab-items:not(.is-active) > a').once().on('click', function (e) { 391 $(".sbd-carousel.slick-initialized").slick('reinit'); 392 }); 393 394 // Reinitialize slick on accordion for mobile 395 $('.coh-style-accordion-items').once().on('click', function () { 396 $('.sbd-carousel').slick('reinit'); 397 398 // add a opacity on accordion for gsap fadein sections. 399 if (drupalSettings.isCoreMds2024) { 400 $(".page-node-type-sku .gsap-fadein-section, .page-node-type-sku .gsap-fadein-section .fifty-fifty-component").css({ 401 "opacity": 1, 402 "visibility" : "visible", 403 }); 404 } 405 }); 406 407 // Changing the aria label on sbd carousels as this cannot be the same for each element. 408 // This is appending a number count at the end of the aria label. 409 var count = 1; 410 $('.sbd-carousel').each(function(){ 411 var label = this.ariaLabel; 412 $(this).attr('aria-label', label + '-' + count++); 413 }); 414 }, 415 sliderProgressCal: function (slick, nextSlide=0, $progressBar, $progressBarLabel){ 416 var slickIntCount = slick.$slider.find('.slick-active').length; 417 var calc = ( (nextSlide + slickIntCount) / (slick.slideCount) ) * 100; 418 419 $progressBar 420 .css('background-size', calc + '% 100%') 421 .attr('aria-valuenow', calc ); 422 $progressBarLabel.text( calc + '% completed' ); 423 } 424} 425})(jQuery, Drupal, drupalSettings);
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.