import isNumber from 'is-number'; import $ from 'jquery'; const $doc = $(document); const { screenSizes } = window.VPData; function getSwiperVersion(Swiper) { let ver = 8; // in version 8 added new parameter `maxBackfaceHiddenSlides`. if (typeof Swiper.defaults.maxBackfaceHiddenSlides === 'undefined') { ver = 7; } // in version 7 added new parameter `rewind`. if (typeof Swiper.defaults.rewind === 'undefined') { ver = 6; } // in version 6 added new parameter `loopPreventsSlide`. if (typeof Swiper.defaults.loopPreventsSlide === 'undefined') { ver = 5; } return ver; } // Extend VP class. $doc.on('extendClass.vpf', (event, VP) => { if (event.namespace !== 'vpf') { return; } /** * Init Swiper plugin * * @param {mixed} options - slider options. */ VP.prototype.initSwiper = function (options = false) { const self = this; if ( self.options.layout === 'slider' && typeof window.Swiper !== 'undefined' ) { const $parent = self.$items_wrap.parent(); $parent.addClass('swiper'); self.$items_wrap.addClass('swiper-wrapper'); self.$items_wrap.children().addClass('swiper-slide'); // calculate responsive. let slidesPerView = self.options.sliderSlidesPerView || 3; const breakPoints = {}; if (self.options.sliderEffect === 'fade') { slidesPerView = 1; } if (isNumber(slidesPerView)) { let count = slidesPerView; let currentPoint = Math.min(screenSizes.length - 1, count - 1); for (; currentPoint >= 0; currentPoint -= 1) { if ( count > 0 && typeof screenSizes[currentPoint] !== 'undefined' ) { breakPoints[screenSizes[currentPoint] + 1] = { slidesPerView: count, }; } count -= 1; } slidesPerView = count || 1; } let optionsThumbs = false; let $thumbsParent = false; options = options || { speed: (parseFloat(self.options.sliderSpeed) || 0) * 1000, autoHeight: self.options.sliderItemsHeight === 'auto', effect: self.options.sliderEffect || 'slide', // fix fade items collapse (mostly in Default items style). fadeEffect: { crossFade: true, }, spaceBetween: parseFloat(self.options.itemsGap) || 0, centeredSlides: self.options.sliderCenteredSlides === 'true', freeMode: { enabled: self.options.sliderFreeMode === 'true', sticky: self.options.sliderFreeModeSticky === 'true', }, loop: self.options.sliderLoop === 'true', // This feature is cool, but not working properly when loop enabled // and fast clicking on previous button is not working properly // https://github.com/nolimits4web/swiper/issues/5945 // loopPreventsSlide: false, autoplay: parseFloat(self.options.sliderAutoplay) > 0 && { delay: parseFloat(self.options.sliderAutoplay) * 1000, disableOnInteraction: false, }, navigation: self.options.sliderArrows === 'true' && { nextEl: '.vp-portfolio__items-arrow-next', prevEl: '.vp-portfolio__items-arrow-prev', }, pagination: self.options.sliderBullets === 'true' && { el: '.vp-portfolio__items-bullets', clickable: true, dynamicBullets: self.options.sliderBulletsDynamic === 'true', renderBullet(index, className) { return ``; }, }, mousewheel: self.options.sliderMousewheel === 'true', slidesPerView, breakpoints: breakPoints, keyboard: true, grabCursor: true, preloadImages: false, // fixes text selection when swipe in the items gap. touchEventsTarget: 'container', }; // fix first load slide position (seems like a conflict with lazySizes) // issue: https://github.com/nk-crew/visual-portfolio/issues/54 if (options.speed === 0) { options.speed = 1; } let positionFix = 0; options.on = { transitionEnd() { if (positionFix === 0) { positionFix = 1; this.setTransition(1); this.setTranslate(this.translate + 0.1); } else if (positionFix === 1) { positionFix = 2; this.slideReset(); } }, // These events used to add fixes for // conflict with custom cursor movement. touchStart(swiper, e) { self.emitEvent('swiperTouchStart', [swiper, e]); }, touchMove(swiper, e) { self.emitEvent('swiperTouchMove', [swiper, e]); }, touchEnd(swiper, e) { self.emitEvent('swiperTouchEnd', [swiper, e]); }, }; self.emitEvent('beforeInitSwiper', [options]); // thumbnails. if (self.$slider_thumbnails_wrap.length) { $thumbsParent = self.$slider_thumbnails_wrap.parent(); $thumbsParent.addClass('swiper'); self.$slider_thumbnails_wrap.addClass('swiper-wrapper'); self.$slider_thumbnails_wrap .children() .addClass('swiper-slide'); // calculate responsive. let thumbnailsPerView = self.options.sliderThumbnailsPerView || 8; const thumbnailsBreakPoints = {}; if (isNumber(thumbnailsPerView)) { let count = thumbnailsPerView; let currentPoint = Math.min( screenSizes.length - 1, count - 1 ); for (; currentPoint >= 0; currentPoint -= 1) { if ( count > 0 && typeof screenSizes[currentPoint] !== 'undefined' ) { thumbnailsBreakPoints[ screenSizes[currentPoint] + 1 ] = { slidesPerView: count, }; } count -= 1; } thumbnailsPerView = count || 1; } optionsThumbs = { autoHeight: self.options.sliderThumbnailsHeight === 'auto', effect: 'slide', spaceBetween: parseFloat(self.options.sliderThumbnailsGap) || 0, loop: false, // This feature is cool, but not working properly when loop enabled // and fast clicking on previous button is not working properly // https://github.com/nolimits4web/swiper/issues/5945 // loopPreventsSlide: false, freeMode: { enabled: true, sticky: true, }, loopedSlides: 5, slidesPerView: thumbnailsPerView, breakpoints: thumbnailsBreakPoints, keyboard: true, grabCursor: true, watchSlidesVisibility: true, watchSlidesProgress: true, preloadImages: false, // fixed text selection when swipe in the items gap. touchEventsTarget: 'container', on: { // These events used to add fixes for // conflict with custom cursor movement. touchStart(swiper, e) { self.emitEvent('swiperTouchStart', [swiper, e]); }, touchMove(swiper, e) { self.emitEvent('swiperTouchMove', [swiper, e]); }, touchEnd(swiper, e) { self.emitEvent('swiperTouchEnd', [swiper, e]); }, }, }; } // Fallbacks for old Swiper versions. (() => { const swiperVersion = getSwiperVersion(window.Swiper); const isThumbsEnabled = optionsThumbs && $thumbsParent && $thumbsParent[0]; // Since v7 used container class `swiper`, we should also add old `swiper-container` class. if (swiperVersion < 7) { $parent.addClass('swiper-container'); if (isThumbsEnabled) { $thumbsParent.addClass('swiper-container'); } } // Since v7 freeMode options moved under `freeMode` object. if (swiperVersion < 7) { options.freeModeSticky = options.freeMode.sticky; options.freeMode = options.freeMode.enabled; if (isThumbsEnabled) { optionsThumbs.freeModeSticky = optionsThumbs.freeMode.sticky; optionsThumbs.freeMode = optionsThumbs.freeMode.enabled; } } // Since v5 `breakpointsInverse` option is removed and it is now `true` by default, but in older versions it was `false`. if (swiperVersion >= 5) { options.breakpointsInverse = true; if (isThumbsEnabled) { optionsThumbs.breakpointsInverse = true; } } })(); // Init Swiper. if (optionsThumbs && $thumbsParent && $thumbsParent[0]) { const swiperThumbs = new window.Swiper( $thumbsParent[0], optionsThumbs ); options.thumbs = { swiper: swiperThumbs, }; } const instance = new window.Swiper($parent[0], options); // Autoplay Hover Pause. if ( self.options.sliderAutoplayHoverPause === 'true' && parseFloat(self.options.sliderAutoplay) > 0 ) { self.$item.on( `mouseenter.vpf-uid-${self.uid}`, '.swiper', () => { $parent[0].swiper.autoplay.stop(); } ); self.$item.on( `mouseleave.vpf-uid-${self.uid}`, '.swiper', () => { $parent[0].swiper.autoplay.start(); } ); } self.emitEvent('initSwiper', [options, instance]); } }; /** * Destroy Swiper plugin */ VP.prototype.destroySwiper = function () { const self = this; const $parent = self.$items_wrap.parent(); const $thumbsParent = self.$slider_thumbnails_wrap.length ? self.$slider_thumbnails_wrap.parent() : false; const SliderSwiper = $parent[0].swiper; const ThumbsSwiper = $thumbsParent ? $thumbsParent[0].swiper : false; let isDestroyed = false; // Thumbnails. if (ThumbsSwiper) { ThumbsSwiper.destroy(); $thumbsParent.removeClass('swiper'); self.$slider_thumbnails_wrap.removeClass('swiper-wrapper'); self.$slider_thumbnails_wrap.children().removeClass('swiper-slide'); isDestroyed = true; } // Slider. if (SliderSwiper) { SliderSwiper.destroy(); $parent.removeClass('swiper'); self.$items_wrap.removeClass('swiper-wrapper'); self.$items_wrap.children().removeClass('swiper-slide'); $parent .find('.vp-portfolio__items-bullets') .removeClass( 'swiper-pagination-clickable swiper-pagination-bullets-dynamic' ) .removeAttr('style') .html(''); isDestroyed = true; } if (isDestroyed) { self.emitEvent('destroySwiper'); } }; }); // Add Items. $doc.on('addItems.vpf', (event, self, $items, removeExisting, $newVP) => { if (event.namespace !== 'vpf') { return; } const Swiper = self.$items_wrap.parent()[0].swiper; if (!Swiper) { return; } // Slider. { if (removeExisting) { Swiper.removeAllSlides(); } const appendArr = []; $items.addClass('swiper-slide').each(function () { appendArr.push(this); }); Swiper.appendSlide(appendArr); } // Thumbnails. const ThumbsSwiper = self.$slider_thumbnails_wrap.length ? self.$slider_thumbnails_wrap.parent()[0].swiper : false; if (ThumbsSwiper) { if (removeExisting) { ThumbsSwiper.removeAllSlides(); } const appendArr = []; $newVP .find('.vp-portfolio__thumbnails > .vp-portfolio__thumbnail-wrap') .clone() .addClass('swiper-slide') .each(function () { appendArr.push(this); }); ThumbsSwiper.appendSlide(appendArr); } }); // Init. $doc.on('init.vpf', (event, self) => { if (event.namespace !== 'vpf') { return; } self.initSwiper(); }); // Destroy. $doc.on('destroy.vpf', (event, self) => { if (event.namespace !== 'vpf') { return; } self.destroySwiper(); });