(function($) {
	$( window ).on( 'elementor/frontend/init', () => {
		class TimelineWidget extends elementorModules.frontend.handlers.Base {
			getDefaultSettings() {
				return {
					selectors: {
						timeline: '.pp-timeline',
						verticalTimeline: '.pp-timeline-vertical',
						items: '.pp-timeline-vertical .pp-timeline-item',
						swiperSlide: '.pp-timeline-item',
						connector: '.pp-timeline-vertical .pp-timeline-connector',
						progressBar: '.pp-timeline-vertical .pp-timeline-connector-inner',
						markerClass: '.pp-timeline-vertical .pp-timeline-marker-wrapper',
						carousel: '.pp-timeline-horizontal .pp-timeline-items',
						sliderWrap: '.pp-timeline-wrapper',
						sliderNav: '.pp-timeline-navigation',
					},
					slidesToShow: {
						widescreen: 3,
						desktop: 3,
						laptop: 3,
						tablet_extra: 3,
						tablet: 2,
						mobile_extra: 2,
						mobile: 1
					}
				};
			}

			getDefaultElements() {
				const selectors = this.getSettings( 'selectors' );
				return {
					$timeline: this.$element.find( selectors.timeline ),
					$verticalTimeline: this.$element.find( selectors.verticalTimeline ),
					$items: this.$element.find( selectors.items ),
					$swiperSlide: this.$element.find( selectors.swiperSlide ),
					$connector: this.$element.find( selectors.connector ),
					$progressBar: this.$element.find( selectors.progressBar ),
					$markerClass: this.$element.find( selectors.markerClass ),
					$carousel: this.$element.find( selectors.carousel ),
					$sliderWrap: this.$element.find( selectors.sliderWrap ),
					$sliderNav: this.$element.find( selectors.sliderNav ),
				};
			}

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

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

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

			getDeviceSpaceBetween(device) {
				return elementorFrontend.utils.controls.getResponsiveControlValue(this.getElementSettings(), 'items_spacing', 'size', device) || 0;
			}

			getSwiperOptions() {
				const elementSettings = this.getElementSettings();

				const swiperOptions = {
					slidesPerView:              this.getDeviceSlidesPerView('desktop'),
					slidesPerGroup:             this.getDeviceSlidesToScroll('desktop'),
					spaceBetween:               this.getDeviceSpaceBetween(),
					loop:                       'yes' === elementSettings.infinite_loop,
					centeredSlides:             'yes' === elementSettings.center_mode,
					speed:                      elementSettings.slider_speed.size,
					watchSlidesVisibility:      true,
					watchSlidesProgress:        true,
					preventClicksPropagation:   false,
					slideToClickedSlide:        true,
					handleElementorBreakpoints: true
				};

				if ( 'yes' === elementSettings.autoplay ) {
					swiperOptions.autoplay = {
						delay: elementSettings.autoplay_speed,
						disableOnInteraction: 'yes' === elementSettings.pause_on_hover,
						pauseOnMouseEnter: 'yes' === elementSettings.pause_on_hover
					};
				}

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

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

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

				swiperOptions.breakpoints = breakpointsSettings;

				return swiperOptions;
			}

			updateDirection(currentDeviceMode, elementSettings) {
				const directionMapping = {
					'widescreen': elementSettings.direction_widescreen,
					'desktop': elementSettings.direction,
					'laptop': elementSettings.direction_laptop,
					'tablet_extra': elementSettings.direction_tablet_extra,
					'tablet': elementSettings.direction_tablet,
					'mobile_extra': elementSettings.direction_mobile_extra,
					'mobile': elementSettings.direction_mobile,
				};

				return directionMapping[currentDeviceMode] || elementSettings.direction;
			}

			bindEvents() {
				const elementSettings = this.getElementSettings();

				if ( 'vertical' === elementSettings.layout ) {
					this.initVerticalLayout();
				} else if ( 'horizontal' === elementSettings.layout ) {
					this.initHorizontalLayout();
				}

				this.setPostsCount(1);

				this.initLoadMore();

				this.initNumberedPagination();

				this.initInfiniteScroll();
			}

			initVerticalLayout() {
				const elementSettings = this.getElementSettings();

				let currentDeviceMode = elementorFrontend.getCurrentDeviceMode(),
					direction = this.updateDirection(currentDeviceMode, elementSettings);

				this.updateTimelineDirection(currentDeviceMode, direction);

				$(window).on('resize', $.proxy(function() {
					currentDeviceMode = elementorFrontend.getCurrentDeviceMode();
					direction = this.updateDirection(currentDeviceMode, elementSettings);

					this.updateTimelineDirection(currentDeviceMode, direction);

					this.winHeight = $(window).height();
					this.scrollTop = $(window).scrollTop();

					this.requestAnimation();
				}, this));

				this.winHeight = $(window).height();
				this.scrollTop = $(window).scrollTop();
				this.window    = $(window);

				this.window.on('scroll', $.proxy(function() {
					this.scrollTop = this.window.scrollTop();
					this.requestAnimation();
					this.revealItems();
				}, this));

				this.requestAnimation();
				this.revealItems();
			}

			initHorizontalLayout() {
				this.initCarousel();
			}

			updateTimelineDirection(deviceMode, elementDirection) {
				if (undefined !== elementDirection) {
					const $timeline = this.elements.$timeline;
					$timeline.removeClass('pp-timeline-left pp-timeline-center pp-timeline-right');
					$timeline.addClass('pp-timeline-' + elementDirection);
				}
			}

			requestAnimation() {
				if (!this.isAnimating) {
					requestAnimationFrame(this.reboot.bind(this));
				}
				this.isAnimating = true;
			}

			revealItems() {
				this.animationClass = 'bounce-in';

				this.elements.$items.each((index, item) => {
					const $item = $(item);
					if ($item.offset().top <= this.scrollTop + $(window).outerHeight() * 0.95 && $item.hasClass('pp-timeline-item-hidden')) {
						$item.removeClass('pp-timeline-item-hidden').addClass(this.animationClass);
					}
				});
			}

			refreshTimelineItems() {
				const selectors = this.getSettings('selectors');

				// Refresh items collection
				this.elements.$items = this.$element.find(selectors.items);

				// Hide newly loaded items
				this.elements.$items
					.not('.pp-timeline-item-hidden')
					.addClass('pp-timeline-item-hidden');

				// Reset scroll values
				this.winHeight = $(window).height();
				this.scrollTop = $(window).scrollTop();

				// Trigger reveal
				this.requestAnimation();
				this.revealItems();
			}

			setup() {
				const $timeline  = this.elements.$verticalTimeline;
				const $items     = $timeline.find('.pp-timeline-item');
				const $connector = $timeline.find('.pp-timeline-connector');

				if ( !$items.length || !$connector.length ) {
					return;
				}

				const $firstItem = $items.first();
				const $lastItem  = $items.last();

				if ( !$firstItem.length || !$lastItem.length ) {
					return;
				}

				const $firstMarker = $firstItem.find('.pp-timeline-marker-wrapper');
				const $lastMarker  = $lastItem.find('.pp-timeline-marker-wrapper');

				if ( !$firstMarker.length || !$lastMarker.length ) {
					return;
				}

				const lastMarkerHeight = $lastMarker.outerHeight();

				$connector.css({
					top:
						$firstMarker.offset().top - $firstItem.offset().top,
					bottom:
						( $timeline.offset().top + $timeline.outerHeight() ) -
						$lastMarker.offset().top -
						( lastMarkerHeight / 2 ),
				});
			}

			reboot() {
				this.isAnimating = false;

				if ( this.winHeight !== this.lastWinHeight ) {
					this.setup();
				}

				this.start();
			}

			start() {
				if ( this.scrollTop !== this.lastScrollTop || this.winHeight !== this.lastWinHeight ) {
					this.lastScrollTop = this.scrollTop;
					this.lastWinHeight = this.winHeight;

					this.progress();
				}

				this.isAnimating = false;
			}

			progress() {
				const win = $(window);
				const selectors = this.getSettings('selectors');
				const $progressBar = this.$element.find(selectors.progressBar);

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

				this.elements.$items.each((index, item) => {
					const $marker = $(item).find(this.elements.$markerClass);

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

					const markerOffset = $marker.offset();

					if ( !markerOffset ) {
						return;
					}

					if ( markerOffset.top < ( this.window.scrollTop() + win.outerHeight() / 2 ) ) {
						$(item).addClass('pp-timeline-item-active');
					} else {
						$(item).removeClass('pp-timeline-item-active');
					}
				});

				const $lastItem   = this.elements.$items.last();
				const $lastMarker = $lastItem.find('.pp-timeline-marker-wrapper');

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

				const lastMarkerOffset = $lastMarker.offset();

				if ( !lastMarkerOffset ) {
					return;
				}

				let lastMarkerPos    = lastMarkerOffset.top;
				let lastMarkerHeight = $lastMarker.outerHeight();
				let progressPos      = ( this.window.scrollTop() - $progressBar.offset().top ) + ( win.outerHeight() / 2 );

				if (lastMarkerPos <= (this.window.scrollTop() + win.outerHeight() / 2)) {
					progressPos = lastMarkerPos - $progressBar.offset().top;
				}

				progressPos += lastMarkerHeight / 2;

				$progressBar.css('height', progressPos + 'px');
			}

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

			async initCarousel() {
				const elementSettings = this.getElementSettings(),
					self = this;

				// Initialize timeline Swiper
				let swiperOptions = this.getSwiperOptions();

				// Initialize timeline Nav Swiper
				let swiperNavOptions = this.getSwiperOptions();

				if ( 'yes' === elementSettings.dots ) {
					swiperOptions.pagination = {
						el: `.swiper-pagination-${this.getID()}`,
						type: 'bullets',
						clickable: true
					};
				}

				swiperNavOptions.pagination = false;
				
				if ( 'yes' === elementSettings.arrows ) {
					swiperNavOptions.navigation = {
						prevEl: '.swiper-button-prev-' + this.getID(),
						nextEl: '.swiper-button-next-' + this.getID(),
					};
				}

				const Swiper = elementorFrontend.utils.swiper;
				this.timelineSwiper    = await new Swiper(this.elements.$carousel, swiperOptions);
				this.timelineNavSwiper = await new Swiper(this.elements.$sliderNav, swiperNavOptions);

				// Connect both Swipers so they can control each other
				this.timelineSwiper.controller.control = this.timelineNavSwiper;
				this.timelineNavSwiper.controller.control = this.timelineSwiper;

				// When user hover then pause and after hover start Slider.
				if ( 'yes' === elementSettings.pause_on_hover ) {
					this.togglePauseOnHover(true);
				}

				this.elements.$carousel.on( 'init', function () {
					self.setEqualHeight();
				} );

				$(window).on( 'resize', function () {
					setTimeout( function () {
						self.setEqualHeight();
					}, 300);
				} );
			}

			setEqualHeight() {
				let maxHeight = 0;

				// Reset heights to find the tallest slide
				this.elements.$carousel.find('.swiper-slide').each(function () {
					$(this).css('height', ''); // Reset height
					let slideHeight = $(this).find('.pp-timeline-card').outerHeight();
					if (slideHeight > maxHeight) {
						maxHeight = slideHeight;
					}
				});

				// Apply max height to all slides
				this.elements.$carousel.find('.swiper-slide').each(function () {
					$(this).css('height', maxHeight + 'px');
				});
			}

			initNumberedPagination() {
				const self = this;

				$('body').on( 'click', '.pp-timeline-pagination-ajax .page-numbers', function(e) {
					const $posts_scope = $(this).closest( '.elementor-widget-pp-timeline' );

					let $scope = $posts_scope,
						lastItem = $scope.find( '.pp-timeline-item' ).last(),
						container = $scope.find( '.pp-timeline' ),
						pageID = container.data('page');

					if ( 'main' == container.data( 'query-type' ) ) {
						return;
					}

					e.preventDefault();

					lastItem.after( '<div class="pp-post-loader"><div class="pp-loader"></div><div class="pp-loader-overlay"></div></div>' );

					let pageNumber = 1,
						curr = parseInt( $scope.find( '.pp-timeline-pagination .page-numbers.current' ).html() );

					if ( $(this).hasClass( 'next' ) ) {
						pageNumber = curr + 1;
					} else if ( $(this).hasClass( 'prev' ) ) {
						pageNumber = curr - 1;
					} else {
						pageNumber = $(this).html();
					}

					let $args = {
						'page_id':     pageID,
						'widget_id':   self.getID(),
						'filter':      $scope.find( '.pp-filter-current' ).data( 'filter' ),
						'taxonomy':    $scope.find( '.pp-filter-current' ).data( 'taxonomy' ),
						'page_number': pageNumber,
						'ajax_for':    'timeline'
					};

					$('html, body').animate({
						scrollTop: ( ( $scope.find( '.pp-timeline-wrapper' ).offset().top ) - 30 )
					}, 'slow');

					self.callAjax( self, $args );
				} );
			}

			initLoadMore() {
				const self = this;
				this.loadStatus = true;

				self.$element.on( 'click', '.pp-post-load-more', function(e) {
					const $posts_scope = $(this).closest( '.elementor-widget-pp-timeline' );

					e.preventDefault();

					if ( elementorFrontend.isEditMode() ) {
						loader.show();

						return false;
					}

					let $scope    = $posts_scope,
						loader    = $scope.find( '.pp-posts-loader' ),
						pageCount = self.getPostsCount(),
						category  = $scope.find( '.pp-filter-current' ).data( 'filter' ),
						taxonomy  = $scope.find( '.pp-filter-current' ).data( 'taxonomy' ),
						pageID    = $scope.find( '.pp-timeline' ).data('page');

					let $args = {
						'page_id':     pageID,
						'widget_id':   self.getID(),
						'filter':      category ?? '',
						'taxonomy':    taxonomy ?? '',
						'page_number': ( pageCount + 1 ),
						'ajax_for':    'timeline'
					};

					self.total = $scope.find( '.pp-timeline-pagination' ).data( 'total' );

					if ( true == self.loadStatus ) {
						if ( pageCount < self.total ) {
							loader.show();
							$(this).hide();
							self.callAjax( self, $args, true, pageCount );
							pageCount++;
							self.loadStatus = false;
						}
					}
				} );
			}

			initInfiniteScroll() {
				let self   = this,
					count  = 1,
					loader = this.$element.find( '.pp-posts-loader' );

				this.loadStatus = true;

				if ( this.elements.$sliderWrap.hasClass( 'pp-posts-infinite-scroll' ) ) {
					let windowHeight50 = $(window).outerHeight() / 1.25;

					$(window).scroll( function () {
						if ( elementorFrontend.isEditMode() ) {
							loader.show();
							return false;
						}

						let $container = self.$element,
							$wrapper   = self.elements.$timeline,
							$lastItem  = $container.find( '.pp-timeline-item:last' );

						let $args = {
							'page_id':     $wrapper.data('page'),
							'widget_id':   self.getID(),
							'filter':      $container.find( '.pp-filter-current' ).data( 'filter' ),
							'taxonomy':    $container.find( '.pp-filter-current' ).data( 'taxonomy' ),
							'page_number': $container.find( '.page-numbers.current' ).next( 'a' ).html(),
							'ajax_for':    'timeline'
						};

						self.total = $container.find( '.pp-timeline-pagination' ).data( 'total' );

						if ( ( $(window).scrollTop() + windowHeight50 ) >= ( $lastItem.offset().top ) ) {

							if ( true == self.loadStatus ) {
								if ( count < self.total ) {
									loader.show();
									self.callAjax( self, $args, true );
									count++;
									self.loadStatus = false;
								}
							}
						}
					} );
				}
			}

			setPostsCount(count) {
				this.$element.find('.pp-post-load-more').attr('data-count', count);
			}

			getPostsCount() {
				return this.$element.find('.pp-post-load-more').data('count');
			}

			callAjax( self, $obj, $append, $count ) {
				let loader = this.$element.find( '.pp-posts-loader' );
				const elementSettings = this.getElementSettings();

				var that = this;

				$.ajax({
					url: ppTimelineScript.ajax_url,
					data: {
						action:      'pp_get_post',
						page_id:     $obj.page_id,
						widget_id:   $obj.widget_id,
						category:    $obj.filter,
						search:      $obj.search,
						taxonomy:    $obj.taxonomy,
						skin:        $obj.skin,
						page_number: $obj.page_number,
						ajax_for:    $obj.ajax_for,
						nonce:       ppTimelineScript.timeline_nonce,
					},
					dataType: 'json',
					type: 'POST',
					success: function( data ) {
						let $container = self.elements.$sliderWrap,
							sel = $container.find( '.pp-timeline' );

						let not_found = $container.find( '.pp-posts-empty' );

						not_found.remove();

						if ( $(not_found).length == 0 ) {
							$(data.data.not_found).insertBefore(sel);
						}

						if ( true == $append ) {
							let html_str = data.data.html;
							sel.append( html_str );
						} else {
							sel.html( data.data.html );
						}

						self.refreshTimelineItems();
						self.setup();

						/* that.requestAnimation();
						that.revealItems(); */

						$container.find( '.pp-timeline-pagination-wrap' ).html( data.data.pagination );

						//	Complete the process 'loadStatus'
						self.loadStatus = true;
						if ( true == $append ) {
							loader.hide();
							$container.find( '.pp-post-load-more' ).show();
						}

						self.setPostsCount( $obj.page_number );

						$count = $count + 1;

						if ( $count == self.total ) {
							$container.find( '.pp-post-load-more' ).hide();
						}

						self.$element.trigger('posts.rendered', [self.$element]);

						that.initVerticalLayout();
					}
				} ).done( function() {
					if ( self.$element.find( '.elementor-invisible' ).length > 0 ) {
						self.$element.find( '.elementor-invisible' ).removeClass( 'elementor-invisible' );
					}
				} );
			}
		}

		elementorFrontend.elementsHandler.attachHandler( 'pp-timeline', TimelineWidget );
	} );
})(jQuery);