| 1 |
/** |
| 2 |
* Block Carousel |
| 3 |
*/ |
| 4 |
import getParents from '../../utils/get-parents'; |
| 5 |
import getSiblings from '../../utils/get-siblings'; |
| 6 |
|
| 7 |
const { |
| 8 |
GHOSTKIT: { screenSizes, events }, |
| 9 |
} = window; |
| 10 |
|
| 11 |
function getSwiperVersion(Swiper) { |
| 12 |
let ver = 8; |
| 13 |
|
| 14 |
// in version 8 added new parameter `maxBackfaceHiddenSlides`. |
| 15 |
if (typeof Swiper.defaults.maxBackfaceHiddenSlides === 'undefined') { |
| 16 |
ver = 7; |
| 17 |
} |
| 18 |
|
| 19 |
// in version 7 added new parameter `rewind`. |
| 20 |
if (typeof Swiper.defaults.rewind === 'undefined') { |
| 21 |
ver = 6; |
| 22 |
} |
| 23 |
|
| 24 |
// in version 6 added new parameter `loopPreventsSlide`. |
| 25 |
if (typeof Swiper.defaults.loopPreventsSlide === 'undefined') { |
| 26 |
ver = 5; |
| 27 |
} |
| 28 |
|
| 29 |
return ver; |
| 30 |
} |
| 31 |
|
| 32 |
/** |
| 33 |
* Prepare Carousels. |
| 34 |
*/ |
| 35 |
events.on(document, 'init.blocks.gkt', () => { |
| 36 |
if (typeof window.Swiper === 'undefined') { |
| 37 |
return; |
| 38 |
} |
| 39 |
|
| 40 |
document |
| 41 |
.querySelectorAll('.ghostkit-carousel:not(.ghostkit-carousel-ready)') |
| 42 |
.forEach(($carousel) => { |
| 43 |
const $items = $carousel.querySelector( |
| 44 |
':scope > .ghostkit-carousel-items' |
| 45 |
); |
| 46 |
const effect = $carousel.getAttribute('data-effect') || 'slide'; |
| 47 |
const slidesPerView = parseInt( |
| 48 |
$carousel.getAttribute('data-slides-per-view'), |
| 49 |
10 |
| 50 |
); |
| 51 |
|
| 52 |
const options = { |
| 53 |
speed: |
| 54 |
(parseFloat($carousel.getAttribute('data-speed')) || 0) * |
| 55 |
1000, |
| 56 |
effect, |
| 57 |
fadeEffect: { |
| 58 |
// fixed fade out for previous slider. |
| 59 |
crossFade: true, |
| 60 |
}, |
| 61 |
spaceBetween: |
| 62 |
parseFloat($carousel.getAttribute('data-gap')) || 0, |
| 63 |
centeredSlides: |
| 64 |
$carousel.getAttribute('data-centered-slides') === 'true', |
| 65 |
freeMode: { |
| 66 |
enabled: |
| 67 |
$carousel.getAttribute('data-free-scroll') === 'true', |
| 68 |
}, |
| 69 |
loop: $carousel.getAttribute('data-loop') === 'true', |
| 70 |
// This feature is cool, but not working properly when loop enabled |
| 71 |
// and fast clicking on previous button is not working properly |
| 72 |
// https://github.com/nolimits4web/swiper/issues/5945 |
| 73 |
// loopPreventsSlide: false, |
| 74 |
autoplay: parseFloat($carousel.getAttribute('data-autoplay')) > |
| 75 |
0 && { |
| 76 |
delay: |
| 77 |
parseFloat($carousel.getAttribute('data-autoplay')) * |
| 78 |
1000, |
| 79 |
disableOnInteraction: false, |
| 80 |
}, |
| 81 |
navigation: $carousel.getAttribute('data-show-arrows') === |
| 82 |
'true' && { |
| 83 |
nextEl: '.ghostkit-carousel-arrow-next', |
| 84 |
prevEl: '.ghostkit-carousel-arrow-prev', |
| 85 |
}, |
| 86 |
pagination: $carousel.getAttribute('data-show-bullets') === |
| 87 |
'true' && { |
| 88 |
el: '.ghostkit-carousel-bullets', |
| 89 |
clickable: true, |
| 90 |
dynamicBullets: |
| 91 |
$carousel.getAttribute('data-dynamic-bullets') === |
| 92 |
'true', |
| 93 |
}, |
| 94 |
slidesPerView: 1, |
| 95 |
keyboard: true, |
| 96 |
grabCursor: true, |
| 97 |
preloadImages: false, |
| 98 |
|
| 99 |
// fixes text selection when swipe in the items gap. |
| 100 |
touchEventsTarget: 'container', |
| 101 |
|
| 102 |
// disable swiping on some elements. |
| 103 |
noSwipingSelector: 'input, textarea, .ghostkit-image-compare', |
| 104 |
}; |
| 105 |
|
| 106 |
$carousel.classList.add('ghostkit-carousel-ready', 'swiper'); |
| 107 |
$items.classList.add('swiper-wrapper'); |
| 108 |
$items |
| 109 |
.querySelectorAll(':scope > .ghostkit-carousel-slide') |
| 110 |
.forEach(($slide) => { |
| 111 |
$slide.classList.add('swiper-slide'); |
| 112 |
}); |
| 113 |
|
| 114 |
// add arrows |
| 115 |
if (options.navigation) { |
| 116 |
const $prevArrowIcon = $carousel.querySelector( |
| 117 |
'.ghostkit-carousel-arrow-prev-icon' |
| 118 |
); |
| 119 |
const $nextArrowIcon = $carousel.querySelector( |
| 120 |
'.ghostkit-carousel-arrow-next-icon' |
| 121 |
); |
| 122 |
|
| 123 |
// Create and add arrow previous. |
| 124 |
const $prevArrow = document.createElement('div'); |
| 125 |
$prevArrow.classList.add( |
| 126 |
'ghostkit-carousel-arrow', |
| 127 |
'ghostkit-carousel-arrow-prev' |
| 128 |
); |
| 129 |
$prevArrow.innerHTML = $prevArrowIcon.innerHTML; |
| 130 |
$carousel.append($prevArrow); |
| 131 |
|
| 132 |
// Create and add arrow next. |
| 133 |
const $nextArrow = document.createElement('div'); |
| 134 |
$nextArrow.classList.add( |
| 135 |
'ghostkit-carousel-arrow', |
| 136 |
'ghostkit-carousel-arrow-next' |
| 137 |
); |
| 138 |
$nextArrow.innerHTML = $nextArrowIcon.innerHTML; |
| 139 |
$carousel.append($nextArrow); |
| 140 |
} |
| 141 |
|
| 142 |
// Add bullets. |
| 143 |
if (options.pagination) { |
| 144 |
const $bullets = document.createElement('div'); |
| 145 |
$bullets.classList.add('ghostkit-carousel-bullets'); |
| 146 |
$carousel.append($bullets); |
| 147 |
} |
| 148 |
|
| 149 |
// Calculate responsive. |
| 150 |
const breakPoints = {}; |
| 151 |
if (effect !== 'fade' && !Number.isNaN(slidesPerView)) { |
| 152 |
let count = slidesPerView; |
| 153 |
let currentPoint = Math.min(screenSizes.length - 1, count - 1); |
| 154 |
|
| 155 |
for (; currentPoint >= 0; currentPoint -= 1) { |
| 156 |
if ( |
| 157 |
count > 0 && |
| 158 |
typeof screenSizes[currentPoint] !== 'undefined' |
| 159 |
) { |
| 160 |
breakPoints[screenSizes[currentPoint] + 1] = { |
| 161 |
slidesPerView: count, |
| 162 |
}; |
| 163 |
} |
| 164 |
count -= 1; |
| 165 |
} |
| 166 |
|
| 167 |
options.slidesPerView = count || 1; |
| 168 |
} |
| 169 |
options.breakpoints = breakPoints; |
| 170 |
|
| 171 |
// Events. |
| 172 |
options.on = { |
| 173 |
// These events used to add fixes for |
| 174 |
// conflict with custom cursor movement. |
| 175 |
touchStart(swiper, evt) { |
| 176 |
events.trigger($carousel, 'touchStart.carousel.gkt', { |
| 177 |
originalEvent: evt, |
| 178 |
}); |
| 179 |
}, |
| 180 |
touchMove(swiper, evt) { |
| 181 |
events.trigger($carousel, 'touchMove.carousel.gkt', { |
| 182 |
originalEvent: evt, |
| 183 |
}); |
| 184 |
}, |
| 185 |
touchEnd(swiper, evt) { |
| 186 |
events.trigger($carousel, 'touchEnd.carousel.gkt', { |
| 187 |
originalEvent: evt, |
| 188 |
}); |
| 189 |
}, |
| 190 |
}; |
| 191 |
|
| 192 |
// Fallbacks for old Swiper versions. |
| 193 |
(() => { |
| 194 |
const swiperVersion = getSwiperVersion(window.Swiper); |
| 195 |
|
| 196 |
// Since v7 used container class `swiper`, we should also add old `swiper-container` class. |
| 197 |
if (swiperVersion < 7) { |
| 198 |
$carousel.classList.add('swiper-container'); |
| 199 |
} |
| 200 |
|
| 201 |
// Since v7 freeMode options moved under `freeMode` object. |
| 202 |
if (swiperVersion < 7) { |
| 203 |
options.freeMode = options.freeMode.enabled; |
| 204 |
} |
| 205 |
|
| 206 |
// Since v5 `breakpointsInverse` option is removed and it is now `true` by default, but in older versions it was `false`. |
| 207 |
if (swiperVersion >= 5) { |
| 208 |
options.breakpointsInverse = true; |
| 209 |
} |
| 210 |
})(); |
| 211 |
|
| 212 |
events.trigger($carousel, 'init.carousel.gkt', { options }); |
| 213 |
|
| 214 |
// init swiper |
| 215 |
new window.Swiper($carousel, options); |
| 216 |
|
| 217 |
events.trigger($carousel, 'inited.carousel.gkt', { options }); |
| 218 |
}); |
| 219 |
}); |
| 220 |
|
| 221 |
/** |
| 222 |
* Autoplay pause/play on hover. |
| 223 |
*/ |
| 224 |
events.on( |
| 225 |
document, |
| 226 |
'mouseenter', |
| 227 |
'.ghostkit-carousel-ready[data-autoplay-hover-pause="true"]', |
| 228 |
(e) => { |
| 229 |
const $slider = e.delegateTarget; |
| 230 |
|
| 231 |
if ( |
| 232 |
!$slider?.swiper?.autoplay || |
| 233 |
!$slider?.swiper?.params?.autoplay?.enabled |
| 234 |
) { |
| 235 |
return; |
| 236 |
} |
| 237 |
|
| 238 |
$slider.swiper.autoplay.stop(); |
| 239 |
} |
| 240 |
); |
| 241 |
events.on( |
| 242 |
document, |
| 243 |
'mouseleave', |
| 244 |
'.ghostkit-carousel-ready[data-autoplay-hover-pause="true"]', |
| 245 |
(e) => { |
| 246 |
const $slider = e.delegateTarget; |
| 247 |
|
| 248 |
if ( |
| 249 |
!$slider?.swiper?.autoplay || |
| 250 |
!$slider?.swiper?.params?.autoplay?.enabled |
| 251 |
) { |
| 252 |
return; |
| 253 |
} |
| 254 |
|
| 255 |
$slider.swiper.autoplay.start(); |
| 256 |
} |
| 257 |
); |
| 258 |
|
| 259 |
/** |
| 260 |
* Re-create slides duplicates. |
| 261 |
* https://github.com/nolimits4web/swiper/issues/2629 |
| 262 |
*/ |
| 263 |
events.on( |
| 264 |
document, |
| 265 |
'shown.tab.gkt shown.accordion.gkt move.image-compare.gkt closed.alert.gkt animated.counter.gkt', |
| 266 |
(e) => { |
| 267 |
const $el = e.delegateTarget; |
| 268 |
const $carousels = getParents($el); |
| 269 |
|
| 270 |
$carousels.forEach(($this) => { |
| 271 |
if ( |
| 272 |
$this.classList.contains('ghostkit-carousel-ready') && |
| 273 |
$this.getAttribute('data-loop') === 'true' && |
| 274 |
$this.swiper |
| 275 |
) { |
| 276 |
const $slide = $el.closest('.swiper-slide'); |
| 277 |
|
| 278 |
// Copy the content of duplicated slide to original when changed content inside duplicated slide. |
| 279 |
if ( |
| 280 |
$slide && |
| 281 |
$slide.classList.contains('swiper-slide-duplicate') |
| 282 |
) { |
| 283 |
getSiblings($slide).forEach(($slideSibling) => { |
| 284 |
if ( |
| 285 |
$slideSibling.getAttribute( |
| 286 |
'data-swiper-slide-index' |
| 287 |
) === |
| 288 |
$slide.getAttribute( |
| 289 |
'data-swiper-slide-index' |
| 290 |
) && |
| 291 |
$slideSibling.classList.contains( |
| 292 |
':not(.swiper-slide-duplicate):first-child' |
| 293 |
) |
| 294 |
) { |
| 295 |
$slideSibling.innerHTML = $slide.innerHTML; |
| 296 |
} |
| 297 |
}); |
| 298 |
|
| 299 |
// Recreate loop when changed content inside original slide. |
| 300 |
} else { |
| 301 |
$this.swiper.loopDestroy(); |
| 302 |
$this.swiper.loopCreate(); |
| 303 |
} |
| 304 |
} |
| 305 |
}); |
| 306 |
} |
| 307 |
); |
| 308 |
|