(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;
			}

			/**
			 * Whether the visitor asked the OS to reduce motion.
			 *
			 * An auto-rotating carousel is exactly the kind of movement that
			 * setting is meant to stop (WCAG 2.2.2).
			 */
			prefersReducedMotion() {
				return window.matchMedia && window.matchMedia( '(prefers-reduced-motion: reduce)' ).matches;
			}

			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 && ! this.prefersReducedMotion() ) {
					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();

				this.initKeyboardControls();
			}

			/**
			 * Release everything bound outside this widget's own element.
			 *
			 * The base handler's onDestroy() calls this, so it runs when the
			 * element goes away and between editor re-renders. Without it every
			 * render left behind another window scroll handler, another resize
			 * handler and a live Swiper instance.
			 */
			unbindEvents() {
				const ns = this.getEventNamespace();

				// Two namespaces: jQuery treats "<ns>-infinite" as its own.
				$(window).off( ns ).off( ns + '-infinite' );

				clearTimeout( this.equalHeightTimer );

				this.destroySwipers();
			}

			destroySwipers() {
				[ 'timelineSwiper', 'timelineNavSwiper' ].forEach( ( key ) => {
					const swiper = this[ key ];

					if ( swiper && 'function' === typeof swiper.destroy && ! swiper.destroyed ) {
						swiper.destroy( true, true );
					}

					this[ key ] = null;
				} );
			}

			/**
			 * Make the mouse-operable controls operable by keyboard.
			 *
			 * The arrows and the navigation markers are focusable divs with
			 * role="button", but Swiper only binds `click`, so Enter and Space
			 * did nothing. Translate both into a click on the same element.
			 */
			initKeyboardControls() {
				this.$element.on( 'keydown', '.pp-slider-arrow, .pp-timeline-nav-item', function(e) {
					if ( 'Enter' !== e.key && ' ' !== e.key && 'Spacebar' !== e.key ) {
						return;
					}

					e.preventDefault();
					$(this).trigger( 'click' );
				} );
			}

			/**
			 * An event namespace unique to this widget instance.
			 *
			 * Window-level handlers are bound with it so they can be removed
			 * again in unbindEvents() — without it, every editor re-render and
			 * every AJAX page load left another scroll and resize handler behind.
			 */
			getEventNamespace() {
				return '.ppTimeline' + this.getID();
			}

			initVerticalLayout() {
				const elementSettings = this.getElementSettings(),
					ns = this.getEventNamespace();

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

				this.updateTimelineDirection(currentDeviceMode, direction);

				this.window = $(window);

				// Re-entrant: drop any handler from a previous call before binding.
				this.window.off('scroll' + ns + ' resize' + ns);

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

					this.updateTimelineDirection(currentDeviceMode, direction);

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

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

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

				this.window.on('scroll' + ns, $.proxy(function() {
					this.scrollTop = this.window.scrollTop();

					// revealItems() reads .offset() for every item, so it is
					// driven from the rAF callback rather than run per scroll event.
					this.requestAnimation();
				}, 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';

				const $hidden = this.elements.$items.filter('.pp-timeline-item-hidden');

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

				// Hoisted out of the loop: it was a forced layout per item.
				const threshold = this.scrollTop + ( this.window || $(window) ).outerHeight() * 0.95;

				// Read every offset first, then write — interleaving them made
				// the browser re-layout once per item.
				const toReveal = [];

				$hidden.each((index, item) => {
					if ($(item).offset().top <= threshold) {
						toReveal.push(item);
					}
				});

				if ( toReveal.length ) {
					$(toReveal).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 — reboot() calls revealItems() for us.
				this.requestAnimation();
			}

			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();

				// Batched into the animation frame with the progress work rather
				// than running on every scroll event.
				this.revealItems();
			}

			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');
				}
			}

			/**
			 * Hold autoplay while the keyboard is inside the carousel.
			 *
			 * Pause-on-hover only helps a pointer user; without this, a keyboard
			 * user tabbing through the cards has the slider move out from under
			 * them mid-read.
			 */
			togglePauseOnFocus() {
				this.$element.on( 'focusin', () => {
					this.timelineSwiper?.autoplay?.stop();
					this.timelineNavSwiper?.autoplay?.stop();
				} );

				this.$element.on( 'focusout', () => {
					this.timelineSwiper?.autoplay?.start();
					this.timelineNavSwiper?.autoplay?.start();
				} );
			}

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

				// Tear down a previous instance before building another one —
				// the editor re-runs this on every settings change.
				this.destroySwipers();

				// 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(),
					};
				}

				if ( 'yes' === elementSettings.equal_height ) {
					// Swiper's own init event — the jQuery 'init' handler this
					// used to rely on was never triggered, so the heights were
					// only ever equalised by a later window resize.
					swiperOptions.on = {
						init: () => this.setEqualHeight(),
						slideChangeTransitionEnd: () => this.setEqualHeight(),
					};
				}

				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.togglePauseOnFocus();

				if ( 'yes' === elementSettings.equal_height ) {
					$(window).off( 'resize' + ns ).on( 'resize' + ns, function () {
						clearTimeout( self.equalHeightTimer );
						self.equalHeightTimer = setTimeout( function () {
							self.setEqualHeight();
						}, 300 );
					} );
				}
			}

			setEqualHeight() {
				// Only when the option is on: this used to run on every resize
				// regardless, stamping fixed pixel heights onto every slide even
				// with Equal Height switched off.
				if ( 'yes' !== this.getElementSettings( 'equal_height' ) ) {
					return;
				}

				const $slides = this.elements.$carousel.find('.swiper-slide');

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

				// Write pass: clear the heights.
				$slides.css('height', '');

				// Read pass: measure once everything is back to natural height.
				let maxHeight = 0;

				$slides.each(function () {
					const slideHeight = $(this).find('.pp-timeline-card').outerHeight();

					if (slideHeight > maxHeight) {
						maxHeight = slideHeight;
					}
				});

				// Write pass: apply. Splitting the three passes keeps the browser
				// from re-laying-out once per slide.
				$slides.css('height', maxHeight + 'px');
			}

			initNumberedPagination() {
				const self = this;

				// Delegated from this widget's own element: bound to `body` it
				// fired once per Timeline on the page, so a click in one widget
				// paginated every other one too.
				self.$element.on( 'click', '.pp-timeline-pagination-ajax .page-numbers:not(.dots)', 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();

					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');

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

						return false;
					}

					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,
					ns     = this.getEventNamespace(),
					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,
						ticking        = false;

					const check = function () {
						ticking = false;

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

						// Nothing left to fetch — skip the layout read entirely.
						self.total = self.$element.find( '.pp-timeline-pagination' ).data( 'total' );

						if ( true !== self.loadStatus || count >= self.total ) {
							return;
						}

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

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

						if ( ( $(window).scrollTop() + windowHeight50 ) < ( $lastItem.offset().top ) ) {
							return;
						}

						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'
						};

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

					// Coalesce scroll events into one animation frame: the raw
					// handler ran a forced layout read on every single event.
					$(window).off( 'scroll' + ns + '-infinite' ).on( 'scroll' + ns + '-infinite', function () {
						if ( ticking ) {
							return;
						}

						ticking = true;
						requestAnimationFrame( check );
					} );

					$(window).off( 'resize' + ns + '-infinite' ).on( 'resize' + ns + '-infinite', function () {
						windowHeight50 = $(window).outerHeight() / 1.25;
					} );
				}
			}

			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,
						taxonomy:    $obj.taxonomy,
						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,
							$timeline  = $container.find( '.pp-timeline' ),
							// The response carries items only, so it goes inside the
							// items container — appending it to .pp-timeline used to
							// duplicate the connector rail on every Load More.
							sel        = $container.find( '.pp-timeline-items' ).first();

						if ( ! sel.length ) {
							sel = $timeline;
						}

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

						not_found.remove();

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

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

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

						$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);