(function ($) {
	$( window ).on( 'elementor/frontend/init', () => {
		class PpCarouselWidget extends elementorModules.frontend.handlers.Base {
			getDefaultSettings() {
				return {
					selectors: {
						swiperContainer: '.pp-swiper-slider',
						swiperSlide: '.swiper-slide',
					},
					slidesPerView: {
						widescreen: 3,
						desktop: 3,
						laptop: 3,
						tablet_extra: 3,
						tablet: 2,
						mobile_extra: 2,
						mobile: 1
					}
				};
			}

			getDefaultElements() {
				const selectors = this.getSettings( 'selectors' ),
					$swiperContainer = this.$element.find( selectors.swiperContainer );

				return {
					$swiperContainer: $swiperContainer,
					// Scoped to the main slider so thumbnail carousel slides are not counted as slides.
					$swiperSlide: $swiperContainer.find( selectors.swiperSlide ),
				};
			}

			getSliderSettings(prop) {
				const sliderSettings = ( undefined !== this.elements.$swiperContainer.data('slider-settings') ) ? this.elements.$swiperContainer.data('slider-settings') : '';

				if ( 'undefined' !== typeof prop && 'undefined' !== sliderSettings[prop] ) {
					return sliderSettings[prop];
				}

				return sliderSettings;
			}

			getSlidesCount() {
				return this.elements.$swiperSlide.length;
			}

			getEffect() {
				return this.getSliderSettings('effect') || 'slide';
			}

			getDeviceSlidesPerView(device) {
				const slidesPerViewKey = 'slides_per_view' + ('desktop' === device ? '' : '_' + device);
				return Math.min(this.getSlidesCount(), +this.getSliderSettings(slidesPerViewKey) || this.getSettings('slidesPerView')[device]);
			}

			getSlidesPerView(device) {
				if ('slide' === this.getEffect() || 'coverflow' === this.getEffect()) {
					return this.getDeviceSlidesPerView(device);
				}
				return 1;
			}

			getDeviceSlidesToScroll(device) {
				const slidesToScrollKey = 'slides_to_scroll' + ('desktop' === device ? '' : '_' + device);
				return Math.min(this.getSlidesCount(), +this.getElementSettings(slidesToScrollKey) || 1);
			}

			getSlidesToScroll(device) {
				if ('slide' === this.getEffect()) {
					return this.getDeviceSlidesToScroll(device);
				}
				return 1;
			}

			getSpaceBetween(device) {
				let propertyName = 'space_between';
				if (device && 'desktop' !== device) {
					propertyName += '_' + device;
				}
				// return this.getSliderSettings(propertyName) || '';
				return elementorFrontend.utils.controls.getResponsiveControlValue(this.getSliderSettings(), 'space_between', 'size', device) || 0;
			}

			getSwiperOptions() {
				const sliderSettings = this.getSliderSettings();
				// const swiperOptions = ( undefined !== this.elements.$swiperContainer.data('slider-settings') ) ? this.elements.$swiperContainer.data('slider-settings') : '';

				const swiperOptions = {
					grabCursor:                'yes' === sliderSettings.grab_cursor,
					// initialSlide:               this.getInitialSlide(),
					slidesPerView:              this.getSlidesPerView('desktop'),
					slidesPerGroup:             this.getSlidesToScroll('desktop'),
					spaceBetween:               this.getSpaceBetween(),
					loop:                       'yes' === sliderSettings.loop,
					centeredSlides:             'yes' === sliderSettings.centered_slides,
					speed:                      sliderSettings.speed,
					autoHeight:                 sliderSettings.auto_height,
					effect:                     this.getEffect(),
					watchSlidesVisibility:      true,
					watchSlidesProgress:        true,
					preventClicksPropagation:   false,
					slideToClickedSlide:        true,
					handleElementorBreakpoints: true
				};

				if ( 'fade' === this.getEffect() ) {
					swiperOptions.fadeEffect = {
						crossFade: true,
					};
				}

				if ( sliderSettings.show_arrows ) {
					var prevEle = ( this.isEdit ) ? '.elementor-swiper-button-prev' : '.swiper-button-prev-' + this.getID();
					var nextEle = ( this.isEdit ) ? '.elementor-swiper-button-next' : '.swiper-button-next-' + this.getID();

					swiperOptions.navigation = {
						prevEl: prevEle,
						nextEl: nextEle,
					};
				}

				if ( sliderSettings.pagination ) {
					var paginationEle = ( this.isEdit ) ? '.swiper-pagination' : '.swiper-pagination-' + this.getID();

					swiperOptions.pagination = {
						el: paginationEle,
						type: sliderSettings.pagination,
						clickable: true
					};
				}

				if ('cube' !== this.getEffect()) {
					const breakpointsSettings = {},
					breakpoints = elementorFrontend.config.responsive.activeBreakpoints;

					Object.keys(breakpoints).forEach(breakpointName => {
						breakpointsSettings[breakpoints[breakpointName].value] = {
							slidesPerView: this.getSlidesPerView(breakpointName),
							slidesPerGroup: this.getSlidesToScroll(breakpointName),
						};

						if ( this.getSpaceBetween(breakpointName) ) {
							breakpointsSettings[breakpoints[breakpointName].value].spaceBetween = this.getSpaceBetween(breakpointName);
						}
					});

					swiperOptions.breakpoints = breakpointsSettings;
				}

				if ( !this.isEdit && sliderSettings.autoplay ) {
					swiperOptions.autoplay = {
						delay: sliderSettings.autoplay_speed,
						disableOnInteraction: !!sliderSettings.pause_on_interaction
					};
				}

				return swiperOptions;
			}

			bindEvents() {
				if ( this.elements.$swiperContainer.hasClass('pp-swiper-slider') ) {
					this.initSlider();
				}
			}

			async initSlider() {
				const elementSettings = this.getElementSettings();

				var slideindex = 1;
				this.elements.$swiperSlide.each( function() {
					// Add ARIA attributes for accessibility.
					$( this ).attr( {
						'aria-roledescription': 'slide',
					} );

					// Set tabindex for keyboard navigation.
					// Only the first slide should be focusable.
					// The rest should be hidden from screen readers.
					if ( 1 === slideindex ) {
						$( this ).attr( {
							'aria-hidden': 'false',
							'tabindex': '0',
						} );
					} else {
						$( this ).attr( {
							'aria-hidden': 'true',
							'tabindex': '-1',
						} );
					}
					slideindex++;
				} );

				const Swiper = elementorFrontend.utils.swiper;
    			this.swiper = await new Swiper(this.elements.$swiperContainer, this.getSwiperOptions());

				this.thumbsNav();

				await this.initThumbsCarousel( Swiper );
				this.bindThumbsCarousel();

				if ('yes' === elementSettings.pause_on_hover) {
					this.togglePauseOnHover(true);
				}

				if ( 'yes' === elementSettings.equal_height_boxes || 'yes' === elementSettings.equal_height ) {
					this.setEqualHeight();
				}

				this.swiper.on( 'slideChange', function() {
					if ( 'yes' === elementSettings.equal_height_boxes || 'yes' === elementSettings.equal_height ) {
						this.setEqualHeight();
					}

					// Slide Visibility for Screen Readers.
					const slides      = this.elements.$swiperContainer.find( '.swiper-slide' );
					const activeIndex = this.swiper.realIndex;
					const current     = this.swiper.realIndex + 1;
					const total       = this.getSlidesCount();

					slides.each( ( index, slide ) => {
						if ( index === activeIndex ) {
							slide.setAttribute( 'aria-hidden', 'false' );
							slide.setAttribute( 'tabindex', '0' );
						} else {
							slide.setAttribute( 'aria-hidden', 'true' );
							slide.setAttribute( 'tabindex', '-1' );
						}
					});

					// Update the screen reader text.
					const statusFormat = ( 'undefined' !== typeof ppCarouselScript && ppCarouselScript.slide_status )
						? ppCarouselScript.slide_status
						: 'Showing Slide %1$s of %2$s';

					this.elements.$swiperContainer.find( '.pp-screen-only' ).text(
						statusFormat.replace( '%1$s', current ).replace( '%2$s', total )
					);
				}.bind( this ) );

				// Keyboard Navigation
				this.elements.$swiperContainer.on( 'keydown', function( e ) {
					if ( e.key === 'ArrowRight' ) this.swiper.slideNext();
					if ( e.key === 'ArrowLeft' ) this.swiper.slidePrev();
				}.bind( this ) );

				this.initFancybox();
			}

			thumbsNav() {
				const elementSettings = this.getElementSettings(),
					thumbsNav         = this.$element.find( '.pp-image-slider-thumb-item-wrap' ),
					swiper            = this.swiper,
					scrollToTop       = ( undefined !== this.elements.$swiperContainer.data('scroll-top') ) ? this.elements.$swiperContainer.data('scroll-top') : '';

				// Carousel thumbnails are wired up by initThumbsCarousel()/bindThumbsCarousel() instead.
				if ( this.getThumbsElement().length ) {
					return;
				}

				if ( 'slideshow' === elementSettings.skin ) {
					thumbsNav.removeClass('pp-active-slide');
					thumbsNav.eq(0).addClass('pp-active-slide');

					swiper.on( 'slideChange', function () {
						const activeSlide = ( 'yes' === elementSettings.infinite_loop ) ? swiper.realIndex : swiper.activeIndex;

						thumbsNav.removeClass('pp-active-slide');
						thumbsNav.eq( activeSlide ).addClass('pp-active-slide');
					});

					const offset = elementSettings.infinite_loop ? 1 : 0;
					var container = this.elements.$swiperContainer;

					$(thumbsNav).on( 'click', function() {
						swiper.slideTo( $(this).index() + offset, 500 );

						if ( 'yes' === scrollToTop ) {
							$('html, body').animate({
								scrollTop: ( container.offset().top - 50 ) + 'px'
							}, 100 );
						}
					});
				}
			}

			getThumbsElement() {
				return this.$element.find( '.pp-image-slider-thumbs-slider' );
			}

			getThumbsSettings(prop) {
				const $thumbs = this.getThumbsElement();

				if ( ! $thumbs.length ) {
					return '';
				}

				const thumbsSettings = ( undefined !== $thumbs.data('thumbs-settings') ) ? $thumbs.data('thumbs-settings') : '';

				if ( thumbsSettings && 'undefined' !== typeof prop ) {
					return thumbsSettings[prop];
				}

				return thumbsSettings;
			}

			/**
			 * Thumbnails sit in a narrow column beside the feature image, so they run vertically there.
			 * Once the layout stacks, that column becomes full width and they have to run horizontally.
			 */
			getThumbsDirection() {
				if ( 'vertical' !== this.getThumbsSettings('direction') ) {
					return 'horizontal';
				}

				const stackOn      = this.getThumbsSettings('stack_on') || 'tablet',
					breakpoints    = elementorFrontend.config.responsive.activeBreakpoints,
					fallbackWidths = { tablet: 1024, mobile: 767 },
					stackWidth     = ( breakpoints[stackOn] ) ? breakpoints[stackOn].value : fallbackWidths[stackOn];

				return ( stackWidth && window.innerWidth <= stackWidth ) ? 'horizontal' : 'vertical';
			}

			getThumbsSlidesPerView(device) {
				const key = 'slides_per_view' + ('desktop' === device ? '' : '_' + device);
				return Math.min( this.getSlidesCount(), +this.getThumbsSettings(key) || +this.getThumbsSettings('slides_per_view') || 4 );
			}

			getThumbsSlidesToScroll(device) {
				const key = 'slides_per_group' + ('desktop' === device ? '' : '_' + device);
				return Math.min( this.getSlidesCount(), +this.getThumbsSettings(key) || 1 );
			}

			getThumbsSpaceBetween(device) {
				const key = 'space_between' + ('desktop' === device ? '' : '_' + device),
					value = this.getThumbsSettings(key);

				return ( undefined !== value && '' !== value ) ? +value : +this.getThumbsSettings('space_between') || 0;
			}

			getThumbsOptions() {
				const thumbsSettings = this.getThumbsSettings();

				const thumbsOptions = {
					direction:                  this.getThumbsDirection(),
					slidesPerView:              this.getThumbsSlidesPerView('desktop'),
					slidesPerGroup:             this.getThumbsSlidesToScroll('desktop'),
					spaceBetween:               this.getThumbsSpaceBetween('desktop'),
					watchOverflow:              true,
					watchSlidesVisibility:      true,
					watchSlidesProgress:        true,
					preventClicksPropagation:   false,
					handleElementorBreakpoints: true
				};

				if ( thumbsSettings.free_mode ) {
					// Object form keeps both the legacy and the current Swiper API happy.
					thumbsOptions.freeMode = { enabled: true };
				}

				// Nothing to loop through when every thumbnail already fits, and Swiper duplicates badly there.
				if ( thumbsSettings.loop && this.getSlidesCount() > this.getThumbsSlidesPerView('desktop') ) {
					thumbsOptions.loop = true;
					thumbsOptions.loopedSlides = this.getSlidesCount();
				}

				if ( thumbsSettings.arrows ) {
					thumbsOptions.navigation = {
						prevEl: '.pp-thumbs-button-prev-' + this.getID(),
						nextEl: '.pp-thumbs-button-next-' + this.getID(),
					};
				}

				const breakpointsSettings = {},
					breakpoints = elementorFrontend.config.responsive.activeBreakpoints;

				Object.keys(breakpoints).forEach(breakpointName => {
					breakpointsSettings[breakpoints[breakpointName].value] = {
						slidesPerView:  this.getThumbsSlidesPerView(breakpointName),
						slidesPerGroup: this.getThumbsSlidesToScroll(breakpointName),
						spaceBetween:   this.getThumbsSpaceBetween(breakpointName),
					};
				});

				thumbsOptions.breakpoints = breakpointsSettings;

				return thumbsOptions;
			}

			async initThumbsCarousel(Swiper) {
				const $thumbs = this.getThumbsElement();

				if ( ! $thumbs.length ) {
					return;
				}

				this.onThumbsResize = this.onThumbsResize.bind( this );
				this.matchThumbsHeight = this.matchThumbsHeight.bind( this );

				// The markup is rendered for the desktop direction, so correct it before Swiper measures anything.
				this.setThumbsDirectionClass( this.getThumbsDirection() );
				this.matchThumbsHeight();

				this.thumbsSwiper = await new Swiper( $thumbs, this.getThumbsOptions() );

				this.observeFeatureImageHeight();

				// Bound by reference rather than by namespace so a re-render cannot unbind its replacement.
				elementorFrontend.elements.$window.on( 'resize', this.onThumbsResize );
			}

			/**
			 * Sole owner of the thumbnail strip's movement. Swiper's thumbs module does the same job, but
			 * running both means two slideTo calls per change, each interrupting the other's animation.
			 */
			bindThumbsCarousel() {
				const mainSwiper = this.swiper,
					thumbsSwiper = this.thumbsSwiper;

				if ( ! mainSwiper || ! thumbsSwiper ) {
					return;
				}

				const scrollToTop = !! this.getThumbsSettings('scroll_top'),
					$container    = this.elements.$swiperContainer;

				this.scrollThumbsIntoView = this.scrollThumbsIntoView.bind( this );
				this.lastThumbRealIndex = mainSwiper.realIndex;

				// Looping duplicates slides, so every copy of the active thumbnail gets highlighted.
				this.syncActiveThumb = () => {
					const slides = thumbsSwiper.slides;

					for ( let index = 0; index < slides.length; index++ ) {
						$( slides[index] ).toggleClass(
							'pp-active-slide',
							this.getSlideRealIndex( slides[index], index ) === mainSwiper.realIndex
						);
					}
				};

				this.syncActiveThumb();

				// Delegated, because Swiper rebuilds its loop duplicates whenever a breakpoint changes.
				this.getThumbsElement().on( 'click.ppThumbs', '.swiper-wrapper > .swiper-slide', ( event ) => {
					const realIndex = this.getSlideRealIndex( event.currentTarget, $( event.currentTarget ).index() );

					mainSwiper.slideTo(
						mainSwiper.params.loop ? this.getNearestSlideIndex( mainSwiper, realIndex ) : realIndex
					);

					if ( scrollToTop ) {
						$('html, body').animate({
							scrollTop: ( $container.offset().top - 50 ) + 'px'
						}, 100 );
					}
				});

				mainSwiper.on( 'slideChange', () => {
					this.syncActiveThumb();
					this.scrollThumbsIntoView();
				});
			}

			getSlideRealIndex(slide, fallback) {
				const realIndex = slide.getAttribute( 'data-swiper-slide-index' );

				return ( null === realIndex ) ? fallback : parseInt( realIndex, 10 );
			}

			/**
			 * DOM position of the slide for a real index. Working in DOM positions rather than real indexes
			 * is what keeps a looping strip travelling the short way instead of unwinding back across
			 * itself. Pass `forward` when following a moving slider: the copy nearest in absolute terms can
			 * sit behind the view once the copies ahead run out, which reads as a backwards lurch.
			 */
			getNearestSlideIndex(swiper, realIndex, forward) {
				const current = swiper.activeIndex,
					slides    = swiper.slides;

				let nearest = -1,
					ahead   = -1,
					behind  = -1;

				for ( let index = 0; index < slides.length; index++ ) {
					if ( this.getSlideRealIndex( slides[index], index ) !== realIndex ) {
						continue;
					}

					if ( index >= current && ( ahead < 0 || index < ahead ) ) {
						ahead = index;
					}

					if ( index <= current && ( behind < 0 || index > behind ) ) {
						behind = index;
					}

					if ( nearest < 0 || Math.abs( index - current ) < Math.abs( nearest - current ) ) {
						nearest = index;
					}
				}

				if ( true === forward && ahead >= 0 ) {
					return ahead;
				}

				if ( false === forward && behind >= 0 ) {
					return behind;
				}

				return ( nearest < 0 ) ? realIndex : nearest;
			}

			/**
			 * Keep the active thumbnail in view, with a one-thumb look-ahead in the direction of travel.
			 */
			scrollThumbsIntoView() {
				const thumbs = this.thumbsSwiper;

				if ( ! thumbs || thumbs.destroyed ) {
					return;
				}

				const realIndex = this.swiper.realIndex,
					total       = this.getSlidesCount(),
					previous    = this.lastThumbRealIndex;

				// Which way the feature image just moved, counted the short way round so that stepping off
				// the end still reads as forward rather than as a jump back to the start.
				let forward;

				if ( undefined !== previous && previous !== realIndex && total > 1 ) {
					forward = ( ( realIndex - previous + total ) % total ) <= total / 2;
				}

				this.lastThumbRealIndex = realIndex;

				const index    = thumbs.params.loop
						? this.getNearestSlideIndex( thumbs, realIndex, forward )
						: realIndex,
					perView    = Math.ceil( thumbs.params.slidesPerView ) || 1,
					maxIndex   = Math.max( thumbs.slides.length - perView, 0 ),
					firstVisible = thumbs.activeIndex,
					lastVisible  = firstVisible + perView - 1;

				let newIndex;

				if ( index >= lastVisible ) {
					newIndex = this.snapThumbIndex( Math.min( index - perView + 2, maxIndex ), true );
				} else if ( index <= firstVisible ) {
					newIndex = this.snapThumbIndex( Math.max( index - 1, 0 ), false );
				} else {
					return;
				}

				// The feature image loops but the strip does not, so wrapping past either end asks the strip
				// to travel its whole length at once. Animating that reads as a lurch, where a jump is over
				// before it registers. Ordinary steps still slide.
				const isJump = Math.abs( newIndex - firstVisible ) > perView;

				thumbs.slideTo( newIndex, isJump ? 0 : undefined );
			}

			/**
			 * Swiper floors every slideTo onto a group boundary, so with more than one thumbnail per scroll
			 * a forward nudge lands short while the mirrored backward one overshoots, and the same step
			 * feels different each way. Round towards the direction of travel so both move alike.
			 */
			snapThumbIndex(index, forward) {
				const thumbs = this.thumbsSwiper,
					group    = thumbs.params.slidesPerGroup || 1;

				if ( group < 2 ) {
					return index;
				}

				const snapped = forward
					? Math.ceil( index / group ) * group
					: Math.floor( index / group ) * group;

				return Math.max( 0, Math.min( snapped, Math.max( thumbs.slides.length - 1, 0 ) ) );
			}

			setThumbsDirectionClass(direction) {
				this.$element.find( '.pp-image-slider-thumbs' )
					.removeClass( 'pp-image-slider-thumbs-horizontal pp-image-slider-thumbs-vertical' )
					.addClass( 'pp-image-slider-thumbs-' + direction );

				if ( 'vertical' !== direction ) {
					this.thumbsMatchedHeight = null;
					this.getThumbsElement().css( 'height', '' );
				}
			}

			/**
			 * The feature image settles its height late — cached images report no layout at handler init, and in
			 * the editor the widget is re-rendered over AJAX. Watching the box keeps the vertical strip in step,
			 * where a one-off measurement leaves it collapsed.
			 */
			observeFeatureImageHeight() {
				if ( 'undefined' === typeof ResizeObserver || ! this.elements.$swiperContainer.length ) {
					return;
				}

				this.thumbsResizeObserver = new ResizeObserver( this.matchThumbsHeight );
				this.thumbsResizeObserver.observe( this.elements.$swiperContainer[0] );
			}

			matchThumbsHeight() {
				if ( ! this.getThumbsSettings('match_height') || 'vertical' !== this.getThumbsDirection() ) {
					return;
				}

				const height = this.elements.$swiperContainer.outerHeight();

				if ( ! height || height === this.thumbsMatchedHeight ) {
					return;
				}

				this.thumbsMatchedHeight = height;
				this.getThumbsElement().css( 'height', height + 'px' );

				if ( ! this.thumbsSwiper ) {
					return;
				}

				// Recalculating mid-transition snaps the strip out of its animation, and a feature image
				// of changing height fires the observer on exactly those frames. Let it settle first.
				if ( this.thumbsSwiper.animating ) {
					this.thumbsSwiper.once( 'transitionEnd', () => {
						if ( this.thumbsSwiper && ! this.thumbsSwiper.destroyed ) {
							this.thumbsSwiper.update();
						}
					});
				} else {
					this.thumbsSwiper.update();
				}
			}

			onThumbsResize() {
				if ( ! this.thumbsSwiper ) {
					return;
				}

				const direction = this.getThumbsDirection();

				if ( direction !== this.thumbsSwiper.params.direction ) {
					this.setThumbsDirectionClass( direction );
					this.thumbsSwiper.changeDirection( direction );
				}

				this.matchThumbsHeight();

				// A breakpoint change can rebuild the loop duplicates, dropping the active highlight with them.
				if ( this.syncActiveThumb ) {
					this.syncActiveThumb();
				}
			}

			togglePauseOnHover(toggleOn) {
				if (toggleOn) {
					this.elements.$swiperContainer.on({
						mouseenter: () => {
							this.swiper.autoplay.stop();
						},
						mouseleave: () => {
							this.swiper.autoplay.start();
						}
					});
				} else {
					this.elements.$swiperContainer.off('mouseenter mouseleave');
				}
			}

			setEqualHeight() {
				const swiperOptions = this.getSwiperOptions(),
					effect          = swiperOptions.effect,
					activeSlide     = this.elements.$swiperContainer.find( '.swiper-slide-visible' );

				let maxHeight = -1;

				activeSlide.each( function() {
					let containerHeight = $(this).outerHeight();

					if ( maxHeight < containerHeight ) {
						maxHeight = containerHeight;
					}
				});

				activeSlide.each( function() {
					if ( 'coverflow' === effect ) {
						$(this).css({ height: maxHeight } );
					} else {
						$(this).animate({ height: maxHeight }, { duration: 200, easing: 'linear' });
					}
				});
			}

			initFancybox() {
				const sliderId       = this.elements.$swiperContainer.attr( 'id' ),
					fancyboxSettings = this.elements.$swiperContainer.data('fancybox-settings'),
					lightboxSelector = '.pp-swiper-slide:not(.swiper-slide-duplicate) .pp-image-slider-slide-link[data-fancybox="' + sliderId + '"]';
	
				if ( $(lightboxSelector).length > 0 ) {
					$(lightboxSelector).fancybox( fancyboxSettings );
				}
			}

			onDestroy() {
				// An own, bound copy once the carousel has initialised; otherwise nothing matches.
				elementorFrontend.elements.$window.off( 'resize', this.onThumbsResize );

				if ( this.thumbsResizeObserver ) {
					this.thumbsResizeObserver.disconnect();
					this.thumbsResizeObserver = null;
				}

				if ( this.thumbsSwiper ) {
					this.thumbsSwiper.destroy( true, true );
					this.thumbsSwiper = null;
				}

				super.onDestroy();
			}
		}

		const widgets = {
			'business-reviews':     [ 'default', 'classic', 'card' ],
			'content-ticker':       '',
			'image-slider':         '',
			'info-box-carousel':    '',
			'logo-carousel':        '',
			'magazine-slider':      '',
			'team-member-carousel': '',
			'woo-categories':       '',
		}

		$.each( widgets, function( widget, skin ) {
			if ( 'object' ===  typeof skin ) {
				$.each( skin, function( index, wSkin ) {
					elementorFrontend.elementsHandler.attachHandler( 'pp-' + widget, PpCarouselWidget, wSkin );
				});
			} else {
				elementorFrontend.elementsHandler.attachHandler( 'pp-' + widget, PpCarouselWidget );
			}
		});
	} );
})(jQuery);