(function() { 'use strict'; let initialized = false; let windowWidth = window.outerWidth; const REQUIRED_LIBS = ['$', 'gsap', 'ScrollTrigger', 'SplitText']; const MOBILE_BREAKPOINT = 768; function checkLibraries() { return REQUIRED_LIBS.every(lib => { if (lib === '$') return typeof window.jQuery !== 'undefined'; if (lib === 'gsap') return typeof window.gsap !== 'undefined'; if (lib === 'ScrollTrigger') return typeof window.ScrollTrigger !== 'undefined'; if (lib === 'SplitText') return typeof window.SplitText !== 'undefined'; return typeof window[lib] !== 'undefined'; }); } function waitForLibraries(callback, maxAttempts = 100, attempt = 0) { if (checkLibraries()) { setTimeout(callback, 500); } else if (attempt < maxAttempts) { setTimeout(() => waitForLibraries(callback, maxAttempts, attempt + 1), 100); } else { console.error('Required libraries not loaded'); } } function getTargetSelector(splitType) { if (splitType.includes('lines')) return '.split-lines'; if (splitType.includes('words') && splitType.includes('chars')) return '.split-chars'; if (splitType.includes('words')) return '.split-words'; return '.split-chars'; } function getAnimationParams(revealType, splitType) { const baseParams = { opacity: 1, ease: 'power1.inOut' }; const charsParams = { duration: 0.7, stagger: { amount: 0.7, from: '0' } }; if (revealType === 'reveal' || revealType === 'reveal-1' || !revealType) { const base = { ...baseParams, y: '0%', rotationX: 0 }; if (splitType.includes('lines')) { return { ...base, duration: 0.6, stagger: { amount: 0.3, from: '0' } }; } return { ...base, ...charsParams }; } if (revealType === 'reveal-2' || revealType === 'reveal-fade') { if (splitType.includes('lines')) { return { ...baseParams, duration: 0.6, stagger: { amount: 0.3, from: '0' } }; } return { ...baseParams, ...charsParams }; } if (revealType === 'reveal-3' || revealType === 'reveal-slide') { const base = { ...baseParams, y: '0%' }; if (splitType.includes('lines')) { return { ...base, duration: 0.6, stagger: { amount: 0.3, from: '0' } }; } return { ...base, ...charsParams }; } return { ...baseParams, y: '0%', rotationX: 0, ...charsParams }; } function getInitialState(revealType) { if (revealType === 'reveal' || revealType === 'reveal-1' || !revealType) { return { opacity: 0, y: '60%', rotationX: -90 }; } if (revealType === 'reveal-2' || revealType === 'reveal-fade') { return { opacity: 0 }; } if (revealType === 'reveal-3' || revealType === 'reveal-slide') { return { opacity: 0, y: '60%' }; } return { opacity: 0, y: '60%', rotationX: -90 }; } function createSplitText($element, splitType) { const instance = $element.data('splitTextInstance'); if (instance) { try { if (instance.revert) { instance.revert(); } } catch(e) { console.warn('Error reverting SplitText:', e); } } try { const tagName = $element.prop('tagName') ? $element.prop('tagName').toLowerCase() : ''; const isParagraph = tagName === 'p' || $element.hasClass('paragraph') || $element.hasClass('text'); const isHeading = /^h[1-6]$/.test(tagName) || $element.hasClass('heading') || $element.hasClass('title'); let finalSplitType = splitType; if ((isHeading || isParagraph) && splitType === 'chars') { finalSplitType = 'words,chars'; } const newInstance = new SplitText($element, { type: finalSplitType, charsClass: 'split-chars', wordsClass: 'split-words', linesClass: 'split-lines' }); $element.data('splitTextInstance', newInstance); return newInstance; } catch(e) { console.warn('Error creating SplitText:', e); return null; } } function initTextAnimations() { if (initialized || window.innerWidth < MOBILE_BREAKPOINT) return; if (!checkLibraries()) { console.warn('Not all libraries loaded for text animations'); return; } try { gsap.registerPlugin(ScrollTrigger); } catch(e) {} $('[data-animation]').each(function() { const $element = $(this); const splitType = $element.attr('data-split-type') || 'chars'; const instance = createSplitText($element, splitType); if (instance && (splitType === 'chars' || splitType.includes('words'))) { setTimeout(() => { const $words = $element.find('.split-words'); $words.each(function() { const $word = $(this); $word.css({ 'display': 'inline-block', 'white-space': 'nowrap', 'word-break': 'keep-all', 'overflow-wrap': 'normal', 'hyphens': 'none' }); $word.find('.split-chars').css({ 'display': 'inline-block', 'white-space': 'normal' }); }); }, 10); } }); $('[data-animation]').each(function(index) { const $trigger = $(this); let revealType = $trigger.attr('data-animation') || 'reveal'; let splitType = $trigger.attr('data-split-type') || 'chars'; const tagName = $trigger.prop('tagName') ? $trigger.prop('tagName').toLowerCase() : ''; const isParagraph = tagName === 'p' || $trigger.hasClass('paragraph') || $trigger.hasClass('text'); const isHeading = /^h[1-6]$/.test(tagName) || $trigger.hasClass('heading') || $trigger.hasClass('title'); if ((isHeading || isParagraph) && splitType === 'chars') { splitType = 'words,chars'; } const $targets = $trigger.find(getTargetSelector(splitType)); if ($targets.length === 0) { console.warn('No elements found for animation in:', $trigger); return; } const isImmediate = $trigger.attr('data-immediate') === 'true' || $trigger.attr('data-onload') === 'true' || $trigger.attr('data-immediate') === '' || $trigger.attr('data-onload') === ''; const initialState = getInitialState(revealType); gsap.set($targets, initialState); const animationParams = getAnimationParams(revealType, splitType); let timeline; if (isImmediate) { timeline = gsap.timeline(); timeline.to($targets, animationParams); } else { timeline = gsap.timeline({ scrollTrigger: { trigger: $trigger[0], start: 'top 90%', end: 'top 45%', scrub: 1, toggleActions: 'restart none none none', id: 'text-animation-' + index, invalidateOnRefresh: true } }); timeline.to($targets, animationParams); } }); let resizeTimer; $(window).on('resize', function() { clearTimeout(resizeTimer); resizeTimer = setTimeout(function() { if (window.outerWidth !== windowWidth) { $('[data-animation]').each(function() { const instance = $(this).data('splitTextInstance'); if (instance) { try { if (instance.revert) { instance.revert(); } } catch(e) { console.warn('Error reverting on resize:', e); } } }); windowWidth = window.outerWidth; location.reload(); } }, 150); }); if (!$('#text-animations-styles').length) { $('head').append(` `); } gsap.set('[data-animation]', { opacity: 1 }); setTimeout(function() { ScrollTrigger.refresh(); initialized = true; console.log('Text animations initialized'); }, 100); } const init = () => waitForLibraries(initTextAnimations); if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } window.addEventListener('load', function() { setTimeout(function() { if (!initialized) initTextAnimations(); }, 1000); }); })();