(function($) {

	PPAnimatedHeadlines = function( settings ) {
		if ( $( '.fl-node-' + settings.id ).length === 0 ) {
			return;
		}

		this.settings           = settings;
		this.nodeClass          = '.fl-node-' + settings.id;
		this.headline			= this.nodeClass + ' .pp-headline';
		this.headlineText 		= $( this.nodeClass + ' .pp-animated-headlines' ).attr( 'data-text' );
		this.headlineText 		= this.headlineText.replaceAll( "\\'", "'" );
		this.dynamicWrapper		= this.nodeClass + ' .pp-headline-dynamic-wrapper';
		this.animationDelay     = settings.durations.animationDelay;
		// letters effect
		this.lettersDelay		= settings.durations.lettersDelay;
		// typing effect
		this.typeLettersDelay	= settings.durations.typeLettersDelay;
		this.selectionDuration	= settings.durations.selectionDuration;
		// clip effect
		this.revealDuration		= settings.durations.revealDuration,
		this.revealAnimationDelay = settings.durations.revealAnimationDelay;

		this.typeAnimationDelay = this.selectionDuration + 800;

		this.highlightAnimationDuration = settings.durations.highlightAnimationDuration;
		this.highlightAnimationDelay = settings.durations.highlightAnimationDelay;

		this.classes			= {
			dynamicText: 'pp-headline-dynamic-text',
			dynamicLetter: 'pp-headline-dynamic-letter',
			textActive: 'pp-headline-text-active',
			textInactive: 'pp-headline-text-inactive',
			letters: 'pp-headline-letters',
			animationIn: 'pp-headline-animation-in',
			typeSelected: 'pp-headline-typing-selected',
			activateHighlight: 'pp-headline-animated',
      		hideHighlight: 'pp-headline-hide-highlight'
		};

		this.elements			= {};
		this.isInitialized 		= false;
		this.observer 			= null;

		this._destroy();

		$( window ).on( 'load', this._activateScrollListener.bind( this ) );
	};

  	PPAnimatedHeadlines.prototype = {
	    settings        		: {},
	    nodeClass       		: '',
	    headline				: '',
		dynamicWrapper			: '',
		animationDelay			: 2500,
		lettersDelay			: 50,
		typeLettersDelay		: 150,
		selectionDuration		: 500,
		revealDuration			: 600,
		revealAnimationDelay	: 1500,
		highlightAnimationDuration: 2500,
		highlightAnimationDelay: 8000,

		svgPaths: {
			circle: [ 'M325,18C228.7-8.3,118.5,8.3,78,21C22.4,38.4,4.6,54.6,5.6,77.6c1.4,32.4,52.2,54,142.6,63.7 c66.2,7.1,212.2,7.5,273.5-8.3c64.4-16.6,104.3-57.6,33.8-98.2C386.7-4.9,179.4-1.4,126.3,20.7' ],
			curly: [ 'M3,146.1c17.1-8.8,33.5-17.8,51.4-17.8c15.6,0,17.1,18.1,30.2,18.1c22.9,0,36-18.6,53.9-18.6 c17.1,0,21.3,18.5,37.5,18.5c21.3,0,31.8-18.6,49-18.6c22.1,0,18.8,18.8,36.8,18.8c18.8,0,37.5-18.6,49-18.6c20.4,0,17.1,19,36.8,19 c22.9,0,36.8-20.6,54.7-18.6c17.7,1.4,7.1,19.5,33.5,18.8c17.1,0,47.2-6.5,61.1-15.6' ],
			strikethrough: ['M3,75h493.5'],
			underline: [ 'M7.7,145.6C109,125,299.9,116.2,401,121.3c42.1,2.2,87.6,11.8,87.3,25.7' ],
			underline_zigzag: [ 'M9.3,127.3c49.3-3,150.7-7.6,199.7-7.4c121.9,0.4,189.9,0.4,282.3,7.2C380.1,129.6,181.2,130.6,70,139 c82.6-2.9,254.2-1,335.9,1.3c-56,1.4-137.2-0.3-197.1,9' ],
			diagonal: ['M13.5,15.5c131,13.7,289.3,55.5,475,125.5'],
			double: ['M8.4,143.1c14.2-8,97.6-8.8,200.6-9.2c122.3-0.4,287.5,7.2,287.5,7.2', 'M8,19.4c72.3-5.3,162-7.8,216-7.8c54,0,136.2,0,267,7.8'],
			double_underline: ['M5,125.4c30.5-3.8,137.9-7.6,177.3-7.6c117.2,0,252.2,4.7,312.7,7.6', 'M26.9,143.8c55.1-6.1,126-6.3,162.2-6.1c46.5,0.2,203.9,3.2,268.9,6.4'],
			x: ['M497.4,23.9C301.6,40,155.9,80.6,4,144.4', 'M14.1,27.6c204.5,20.3,393.8,74,467.3,111.7']
		},

		_initHeadlines: function() {
			this._fillWords();

			if ( 'rotate' === this.settings.headline_style && ! this.isInitialized ) {
				this._rotateHeadline();
			}

			this.isInitialized = true;
		},

		_fillWords: function() {
			var classes 		= this.classes,
				dynamicWrapper 	= $(this.dynamicWrapper),
				settings		= this.settings;

			if ( 'rotate' == this.settings.headline_style ) {
				dynamicWrapper.html( '' );

				var rotatingText = this.headlineText.split('|');

				rotatingText.forEach( function( word, index ) {
					var dynamicText = $('<span>', { 'class': classes.dynamicText, 'data-index': index }).html( word.replace( / /g, '&nbsp;' ) );

					if ( ! index ) {
						dynamicText.addClass( classes.textActive );
					}

					dynamicWrapper.append( dynamicText );
				} );
			} else {
				this._addHighlight();
				this._activateHighlightAnimation();
			}

			this.elements.dynamicText = dynamicWrapper.children( '.' + classes.dynamicText );
		},

		_addHighlight: function() {
			var svg = $('<svg>', {
				xmlns: 'http://www.w3.org/2000/svg',
				viewBox: '0 0 500 150',
				preserveAspectRatio: 'none',
				'aria-hidden': true
			}).html(this._getSvgPaths( this.settings.headline_shape ));

			if ( $(this.dynamicWrapper).find('.' + this.classes.dynamicText).length > 0 ) {
				$(this.dynamicWrapper).append( svg[0].outerHTML );
			}
		},

		_activateHighlightAnimation: function() {
			var $headline = $(this.headline);
			$headline.removeClass( this.classes.hideHighlight ).addClass( this.classes.activateHighlight );
			if ( ! this.settings.loop ) {
				return;
			}
			// remove animation class and hide highlight after animation is done,
			// then re-activate animation after a delay to create a loop effect.
			setTimeout(function() {
				$headline.removeClass( this.classes.activateHighlight ).addClass( this.classes.hideHighlight );
			}.bind(this), this.highlightAnimationDuration + this.highlightAnimationDelay * .8);
			setTimeout(function() {
				this._activateHighlightAnimation();
			}.bind(this), this.highlightAnimationDuration + this.highlightAnimationDelay);
		},

		_rotateHeadline: function() {
			//insert <span> element for each letter of a changing word
			if ( $(this.headline).hasClass( this.classes.letters ) ) {
				this._singleLetters();
			}

			//initialise headline animation
			this._animateHeadline();
		},

		_singleLetters: function() {
			var classes = this.classes;

			this.elements.dynamicText.each( function() {
				var $word = $( this ),
					letters = $word.text().split( '' ),
					isActive = $word.hasClass( classes.textActive );

				$word.empty();

				letters.forEach( function( letter ) {
					var $letter = jQuery( '<span>', { 'class': classes.dynamicLetter } ).text( letter );

					if ( isActive ) {
						$letter.addClass( classes.animationIn );
					}

					$word.append( $letter );
				} );

				$word.css( 'opacity', 1 );
			} );
		},

		_animateHeadline: function() {
			var self 			= this,
				animationType 	= self.settings.animation_type,
				dynamicWrapper 	= $(self.dynamicWrapper);

			if ( 'clip' === animationType ) {
				dynamicWrapper.width( dynamicWrapper.width() + 10 );
			} else if ( 'typing' !== animationType ) {
				//assign to .pp-headline-dynamic-wrapper the width of its longest word
				self._setWidth();
			}

			//trigger animation
			setTimeout( function() {
				self._hideWord( self.elements.dynamicText.eq( 0 ) );
			}, self.animationDelay );
		},

		_showLetter: function( $letter, $word, bool, duration ) {
			var self 			= this,
				classes 		= self.classes,
				animationType 	= self.settings.animation_type;

			$letter.addClass( classes.animationIn );

			if ( ! $letter.is( ':last-child' ) ) {
				setTimeout( function() {
					self._showLetter( $letter.next(), $word, bool, duration );
				}, duration );
			} else {
				if ( ! bool ) {
					setTimeout( function() {
						self._hideWord( $word );
					}, self.animationDelay );
				}
			}
		},

		_hideLetter: function( $letter, $word, bool, duration ) {
			var self = this;

			$letter.removeClass( self.classes.animationIn );

			if ( ! $letter.is( ':last-child' ) ) {
				setTimeout( function() {
					self._hideLetter( $letter.next(), $word, bool, duration );
				}, duration );
			} else if ( bool ) {
				setTimeout( function() {
					self._hideWord( self._getNextWord( $word ) );
				}, self.animationDelay );
			}
		},

		_showWord: function( $word, duration ) {
			var self 			= this,
				animationType 	= self.settings.animation_type;

			if ( 'typing' === animationType ) {
				self._showLetter( $word.find( '.' + self.classes.dynamicLetter ).eq( 0 ), $word, false, duration );

				$word
					.addClass( self.classes.textActive )
					.removeClass( self.classes.textInactive );
			} else if ( 'clip' === animationType ) {
				$(self.dynamicWrapper).animate( { 'width': $word.width() + 10 }, self.revealDuration, function() {
					setTimeout( function() {
						self._hideWord( $word );
					}, self.revealAnimationDelay );
				} );
			}

			if ( $(self.nodeClass).hasClass( 'no-loop' ) && $word.hasClass( self.classes.textActive ) ) {
				if ( $word.attr('data-index') == self.elements.dynamicText.length - 1 ) {
					self.isLast = true;
				}
			}
		},

		_hideWord: function( $word ) {
			var self 			= this,
				classes 		= self.classes,
				letterSelector 	= '.' + classes.dynamicLetter,
				animationType 	= self.settings.animation_type,
				nextWord 		= self._getNextWord( $word );
			
			if ( self.isLast ) {
				$(self.dynamicWrapper).addClass( 'has-stopped' ).removeAttr('style');
				return;
			}

			if ( 'typing' === animationType ) {
				$(self.dynamicWrapper).addClass( classes.typeSelected );

				setTimeout( function() {
					$(self.dynamicWrapper).removeClass( classes.typeSelected );

					$word
						.addClass( classes.textInactive )
						.removeClass( classes.textActive )
						.children( letterSelector )
						.removeClass( classes.animationIn );
				}, self.selectionDuration );
				setTimeout( function() {
					self._showWord( nextWord, self.typeLettersDelay );
				}, self.typeAnimationDelay );

			} else if ( $(self.headline).hasClass( classes.letters ) ) {
				var bool = $word.children( letterSelector ).length >= nextWord.children( letterSelector ).length;

				self._hideLetter( $word.find( letterSelector ).eq( 0 ), $word, bool, self.lettersDelay );

				$word.removeClass( classes.textActive );

				self._showLetter( nextWord.find( letterSelector ).eq( 0 ), nextWord, bool, self.lettersDelay );

				nextWord.addClass( classes.textActive );

				setTimeout( function() {
					$(self.dynamicWrapper).css( { width: nextWord.width() } );
				}, 200 );

			} else if ( 'clip' === animationType ) {
				$(self.dynamicWrapper).animate( { width: '2px' }, self.revealDuration, function() {
					self._switchWord( $word, nextWord );
					self._showWord( nextWord );
				} );
			} else {
				self._switchWord( $word, nextWord );

				setTimeout( function() {
					self._hideWord( nextWord );
					//$(self.dynamicWrapper).removeAttr('style').css( { width: nextWord.width() } );
				}, self.animationDelay );
			}
		},

		_getNextWord: function( $word ) {
			return $word.is( ':last-child' ) ? $word.parent().children().eq( 0 ) : $word.next();
		},

		_switchWord: function( $oldWord, $newWord ) {
			$oldWord
				.removeClass( 'pp-headline-text-active' )
				.addClass( 'pp-headline-text-inactive' );

			$newWord
				.removeClass( 'pp-headline-text-inactive' )
				.addClass( 'pp-headline-text-active' );

			this._setDynamicWrapperWidth( $newWord );
		},

		_getSvgPaths: function( pathName ) {
			var pathsInfo = this.svgPaths[ pathName ],
				$paths = jQuery();

			pathsInfo.forEach( function( pathInfo ) {
				$paths = $paths.add( $( '<path>', { d: pathInfo } ) );
			} );

			return $paths;
		},

		_setDynamicWrapperWidth: function( $newWord ) {
			var animationType = this.settings.animation_type;

			if ('clip' !== animationType && 'typing' !== animationType) {
				$(this.dynamicWrapper).css('width', $newWord.width());
			}
		},

		_setWidth: function() {
			var self = this;
			var width = $(self.dynamicWrapper).width();

			$(self.elements.dynamicText).each( function() {
				var wordWidth = $( this ).width();

				if ( wordWidth > width ) {
					width = wordWidth;
				}
			} );

			$(self.dynamicWrapper).removeAttr('style').css( 'width', width );
		},

		_destroy: function() {
			if ( 'rotate' == this.settings.headline_style ) {
				$(this.dynamicWrapper).removeClass( 'has-stopped' ).html('').removeAttr('style');
				this.isInitialized = false;
			}
		},

		_quoteString: function (str) {
			if (str.match(escape)) {
				return '"' + str.replace(escape, function (a) {
					var c = meta[a];
					if (typeof c === 'string') {
						return c;
					}
					c = a.charCodeAt();
					return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
				}) + '"';
			}
			return '"' + str + '"';
		},

		_isInViewport: function( element, offset ) {
			// Default offset to 50 if not provided
			var offset = typeof offset !== 'undefined' ? offset : 50;

			var elementTop = $(element).offset().top;
			var elementBottom = elementTop + $(element).outerHeight();
			
			var viewportTop = $(window).scrollTop() + offset; // Apply offset to the top of the viewport
			var viewportBottom = viewportTop + $(window).height() - (offset * 2); // Adjust bottom boundary accordingly

			// Check if any part of the element is within the offset boundaries
			return elementBottom > viewportTop && elementTop < viewportBottom;
		},

		_scrollObserver: function(obj) {
			var lastScrollY = 0;

			// Generating thresholds points along the animation height
			// More thresholds points = more trigger points of the callback
			var buildThresholds = function() {
				var sensitivityPercentage = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
				var thresholds = [];
				if (sensitivityPercentage > 0 && sensitivityPercentage <= 100) {
					var increment = 100 / sensitivityPercentage;
					for (var i = 0; i <= 100; i += increment) {
						thresholds.push(i / 100);
					}
				} else {
					thresholds.push(0);
				}
				return thresholds;
			};
			var options = {
				root: obj.root || null,
				rootMargin: obj.offset || '0px',
				threshold: buildThresholds(obj.sensitivity)
			};
			function handleIntersect(entries) {
				var currentScrollY = entries[0].boundingClientRect.y,
					isInViewport = entries[0].isIntersecting,
					intersectionScrollDirection = currentScrollY < lastScrollY ? 'down' : 'up',
					scrollPercentage = Math.abs(parseFloat((entries[0].intersectionRatio * 100).toFixed(2)));
				obj.callback({
					sensitivity: obj.sensitivity,
					isInViewport: isInViewport,
					scrollPercentage: scrollPercentage,
					intersectionScrollDirection: intersectionScrollDirection
				});
				lastScrollY = currentScrollY;
			}
			return new IntersectionObserver(handleIntersect, options);
		},

		_activateScrollListener: function() {
			var scrollBuffer = -100;
			this.observer = this._scrollObserver({
				offset: `0px 0px ${scrollBuffer}px`,
				callback: event => {
					if (event.isInViewport) {
						this._initHeadlines();
					} else {
						this._destroy();
					}
				}
			});
			this.observer.observe($(this.headline)[0]);
		},

		_deactivateScrollListener: function() {
			if (this.observer) {
				this.observer.unobserve($(this.headline)[0]);
			}
		}
	
  	};

})(jQuery);
