1const $ = typeof jQuery !== 'undefined' ? jQuery : null; 2const { typesTesters, typesParsers } = dataAttrHelpers; 3 4const parseDataAttr = (dataAttr, types) => { 5 const options = {}; 6 for (const row of dataAttr.split(';')) { 7 const match = row.trim().match(/^(.*?):([\s\S]*)$/); 8 if (!match) continue; 9 let [key, value] = [match[1], match[2]].map(a => a.trim()); 10 if (aliases[key]) { 11 key = aliases[key]; 12 } 13 for (const type in types) { 14 if (types[type].includes(key) && typesTesters[type](value)) { 15 value = typesParsers[type](value); 16 break; 17 } 18 } 19 if (typeof value === 'string') { 20 if (/^(\[|\{|anime\.|"|')/.test(value)) { 21 value = new Function(`return (${value})`)(); 22 } 23 } 24 const parts = key.split('-'); 25 let opts = options; 26 parts.forEach((part, i) => { 27 if (i < parts.length - 1) { 28 opts[part] = opts[part] || {}; 29 opts = opts[part]; 30 } else { 31 opts[part] = value; 32 } 33 }); 34 } 35 return options; 36}; 37 38const dataAnimeAttrType = { 39 string: [ 40 "targets", 41 "onscroll", 42 "onscroll-target", 43 ], 44 number: [ 45 "onview", 46 ], 47 boolean: [ 48 "loop", 49 "onclick", 50 "onview", 51 "autoplay", 52 "onscroll", 53 "onscroll-target", 54 "onscroll-pen", 55 ] 56}; 57 58const aliases = { 59 'onscroll': 'onscroll-target', 60 'onscroll-trigger': 'onscroll-triggerHook', 61}; 62 63const runInstance = (el, instance, direction = 'restart') => { 64 if (direction === 'alternate') { 65 if (!el.animeToggleOpen) { 66 if (instance.reversed) { 67 instance.reverse(); 68 } 69 } else { 70 if (!instance.reversed) { 71 instance.reverse(); 72 } 73 } 74 el.animeToggleOpen = !el.animeToggleOpen; 75 instance.play(); 76 } else if (direction === 'restart') { 77 instance.restart(); 78 } else if (direction === 'reset') { 79 instance.reset(); 80 } else { 81 throw 'invalid direction'; 82 } 83}; 84 85const loadingPromise = new Promise(r => { 86 document.addEventListener('DOMContentLoaded', e => { 87 setTimeout(() => { 88 r(true); 89 }, 1000 + 300); 90 }); 91}); 92 93const runHelper = async (el, options, instance) => { 94 const run = (direction = 'restart') => { 95 runInstance(el, instance, direction); 96 }; 97 let autoRun = options.autoplay !== false; 98 if (options.onclick) { 99 const toggle = options.onclick === 'alternate'; 100 el.addEventListener('click', e => { 101 e.preventDefault(); 102 run(toggle ? 'alternate' : 'restart'); 103 }); 104 autoRun = false; 105 } 106 if (options.onscroll) { 107 const scrollMagicOptions = typeof options.onscroll === 'boolean' ? { triggerElement: el } : options.onscroll; 108 if (scrollMagicOptions.target) { 109 scrollMagicOptions.triggerElement = scrollMagicOptions.target; 110 delete scrollMagicOptions.target; 111 } 112 const controller = new ScrollMagic.Controller({ 113 ...(scrollMagicOptions.controller || {}), 114 }); 115 const penOption = scrollMagicOptions.pen; 116 delete scrollMagicOptions.controller; 117 delete scrollMagicOptions.pen; 118 const sceneOptions = scrollMagicOptions || {}; 119 const triggerElement = sceneOptions.triggerElement ? ( 120 typeof sceneOptions.triggerElement === 'string' 121 ? document.querySelector(sceneOptions.triggerElement) 122 : sceneOptions.triggerElement 123 ) : el; 124 delete sceneOptions.triggerElement; 125 const scene = new ScrollMagic.Scene({ 126 triggerElement, 127 duration: '100%', 128 triggerHook: 1, 129 ...sceneOptions, 130 }); 131 if (penOption) { 132 const pen = penOption === true ? triggerElement : document.querySelector(penOption); 133 scene.setPin(pen); 134 } 135 scene.on('progress', e => { 136 instance.seek(e.progress * instance.duration); 137 }).addTo(controller); 138 139 // Store the ScrollMagic scene and controller 140 el.animeScrollMagicScene = scene;
141 el.animeScrollMagicController = controller; 142 143 delete options.onscroll; 144 autoRun = false; 145 } 146 if (options.onhover) { 147 $(el).on('mouseenter mouseleave', () => { 148 run('alternate'); 149 }); 150 autoRun = false; 151 } 152 await loadingPromise; 153 if (typeof options.onview !== 'undefined' && options.onview !== false) { 154 const offset = typeof options.onview === 'number' ? options.onview : 0; 155 const handler = () => { 156 if (window.innerHeight > el.getBoundingClientRect().top - offset) { 157 window.removeEventListener('scroll', handler); 158 window.removeEventListener('resize', handler); 159 run(); 160 } 161 }; 162 window.addEventListener('scroll', handler); 163 window.addEventListener('resize', handler); 164 handler(); 165 autoRun = false; 166 } 167 if (options.media) { 168 const mediaArray = typeof options.media === 'string' ? [options.media] : options.media; 169 const mediaList = []; 170 for (let i = 0; i < mediaArray.length; i++) { 171 const bp = mediaArray[i]; 172 const group = i % 2 === 0 ? (mediaList[i / 2] = []) : mediaList[(i - 1) / 2]; 173 group.push({ bp, type: i % 2 === 0 ? 'min' : 'max' }); 174 } 175 const mediaText = mediaList.map(mediaArray => '(' + mediaArray.map(({ bp, type }) => { 176 const bpSize = breakpoints[bp] || 0; 177 return `(${type}-width: ${type === 'max' ? bpSize - 1 : bpSize}px)`; 178 }).join(' and ') + ')').join(' or '); 179 const media = matchMedia(mediaText); 180 const update = () => { 181 run(media.matches ? "restart" : "reset"); 182 }; 183 media.onchange = update; 184 update(); 185 autoRun = false; 186 } 187 if (autoRun) { 188 run(); 189 } else if (options.animejs_animation_type === 'disable') { 190 removeAnimationStyles($(el)); 191 destroyScrollMagicInstance($(el)); 192 } 193}; 194 195const runDataAnime = async (el) => { 196 if (!AnimeJSHelper.isDeviceAllowed(el)) { 197 return; // Exit if the animation is not allowed on this device 198 } 199 const options = parseDataAttr(el.getAttribute('data-anime') || '', dataAnimeAttrType); 200 const targets = options.targets ? [...$(options.targets, el)] : el; 201 Object.assign(options, { targets }); 202 let instance; 203 if (options.timeline) { 204 const timelineName = options.timeline; 205 delete options.timeline; 206 if (!timelines[timelineName]) { 207 if (!timelinesPromises[timelineName]) { 208 timelinesPromises[timelineName] = new Promise(resolve => { 209 timelinesResolvers[timelineName] = resolve; 210 }); 211 } 212 await timelinesPromises[timelineName]; 213 } 214 instance = timelines[timelineName](el, options); 215 } else { 216 instance = anime(options); 217 } 218 instance.pause(); 219 220 el.animeInstance = instance; 221 runHelper(el, options, instance); 222}; 223 224const timelines = {}; 225const timelinesPromises = {}; 226const timelinesResolvers = {}; 227 228const defineAnimeTimelineHelper = (name, fn) => { 229 timelines[name] = fn; 230 if (timelinesResolvers[name]) { 231 timelinesResolvers[name](fn); 232 } 233}; 234 235Object.assign(window, { defineAnimeTimelineHelper }); 236 237const runDataAnimeToggle = async (el) => { 238 const toggleSelector = el.getAttribute('data-anime-toggle') || ''; 239 el.addEventListener('click', e => { 240 e.preventDefault(); 241 const els = [...$(toggleSelector)]; 242 els.forEach(other => { 243 const instance = other.animeTimelineInstance || other.animeInstance; 244 if (!instance) return; 245 runInstance(other, instance, 'alternate'); 246 }); 247 }); 248}; 249 250// Stop and reset the animation 251const resetAnimation = function($element) { 252 const instance = $element[0].animeInstance; 253 if (instance) { 254 instance.pause(); 255 instance.reset(); 256 instance.remove(); 257 anime.remove($element[0]); 258 } 259}; 260 261// Remove inline styles 262const removeAnimationStyles = function($element) { 263 $element.css({ 264 opacity: '', 265 transform: '' 266 }); 267}; 268 269// Stop and destroy ScrollMagic scene and controller 270const destroyScrollMagicInstance = function($element) { 271 const scrollMagicScene = $element[0].animeScrollMagicScene; 272 const scrollMagicController = $element[0].animeScrollMagicController; 273 if (scrollMagicScene && scrollMagicController) { 274 scrollMagicScene.off('progress'); 275 scrollMagicScene.remove(); 276 scrollMagicController.destroy(true); 277 $element[0].animeScrollMagicScene = null; 278 $element[0].animeScrollMagicController = null; 279 } 280}; 281 282const getCurrentDevice = () => { 283 const width = window.innerWidth; 284 if (width < 767) { 285 return 'mobile'; 286 } else if (width < 1023) { 287 return 'tablet'; 288 } else { 289 return 'desktop'; 290 } 291}; 292 293const isDeviceAllowed = (el) => { 294 const allowedDevices = el.getAttribute('data-anime-devices'); 295 if (!allowedDevices) return true; // If no devices specified, allow all 296 297 const deviceList = allowedDevices.split(','); 298 const currentDevice = getCurrentDevice(); 299 return deviceList.includes(currentDevice); 300}; 301 302const AnimeJSHelper = { 303 currentDevice: null, 304 305 init: function() { 306 this.currentDevice = getCurrentDevice(); 307 308 if (window.elementorFrontend && elementorFrontend.isEditMode()) { 309 elementor.channels.editor.on('change', this.onElementorChange); 310 } else { 311 this.applyAnimations(); 312 } 313 314 // Add resize event listener with debounce 315 let resizeTimer; 316 window.addEventListener('resize', () => { 317 clearTimeout(resizeTimer); 318 resizeTimer = setTimeout(() => this.handleResize(), 250); 319 }); 320 }, 321 322 handleResize: function() { 323 const newDevice = getCurrentDevice(); 324 if (newDevice !== this.currentDevice) { 325 this.currentDevice = newDevice; 326 this.applyAnimations(); 327 } 328 }, 329 330 updateElementAnimation: function(el) { 331 const allowedDevices = el.getAttribute('data-anime-devices'); 332 333 if (allowedDevices) { 334 const deviceList = allowedDevices.split(','); 335 if (deviceList.includes(this.currentDevice)) { 336 this.enableAnimation(el); 337 } else { 338 this.disableAnimation(el); 339 } 340 } else { 341 // If no devices are specified, run the animation on all devices 342 this.enableAnimation(el); 343 } 344 }, 345 346 isDeviceAllowed: function(el) { 347 const allowedDevices = el.getAttribute('data-anime-devices'); 348 if (!allowedDevices) return true; // If no devices specified, allow all 349 350 const deviceList = allowedDevices.split(','); 351 return deviceList.includes(this.currentDevice); 352 }, 353 354 enableAnimation: function(el) { 355 // Remove any existing instance 356 if (el.animeInstance) { 357 el.animeInstance.pause(); 358 el.animeInstance.reset(); 359 anime.remove(el); 360 } 361 // Remove any existing ScrollMagic scene and controller 362 if (el.animeScrollMagicScene) { 363 el.animeScrollMagicScene.destroy(true); 364 el.animeScrollMagicScene = null; 365 } 366 if (el.animeScrollMagicController) { 367 el.animeScrollMagicController.destroy(true); 368 el.animeScrollMagicController = null; 369 } 370 // Run the animation 371 runDataAnime(el); 372 }, 373 374 disableAnimation: function(el) { 375 if (el.animeInstance) { 376 el.animeInstance.pause(); 377 el.animeInstance.reset(); 378 anime.remove(el); 379 el.animeInstance = null; 380 } 381 removeAnimationStyles($(el)); 382 if (el.animeScrollMagicScene) { 383 el.animeScrollMagicScene.destroy(true); 384 el.animeScrollMagicScene = null; 385 } 386 if (el.animeScrollMagicController) { 387 el.animeScrollMagicController.destroy(true); 388 el.animeScrollMagicController = null; 389 } 390 }, 391 392 applyAnimations: function() { 393 $('[data-anime]').each((index, el) => { 394 this.updateElementAnimation(el); 395 }); 396 }, 397 398 applyAnimation: async function($element) { 399 if ($element.length === 0) { 400 console.warn('No element found to apply animation'); 401 return; 402 } 403 const el = $element[0]; 404 await runDataAnime(el); 405 }, 406
407 onElementorChange: function(controlView, elementView) { 408 const changed = controlView.model.get('name'); 409 if (changed.indexOf('animejs_') === 0) { 410 const $element = elementView.$el; 411 AnimeJSHelper.updateAnimation($element, elementView); 412 } 413 }, 414 415 updateAnimation: function($element, elementView, isInitial = false) { 416 const settings = elementView.getEditModel().get('settings').toJSON(); 417 418 if (settings.animejs_animation_type !== 'disable') { 419 if (settings.animejs_animation_type || !isInitial) { 420 const dataAnime = AnimeJSHelper.generateDataAnime(settings); 421 422 $element.attr('data-anime', dataAnime); 423 $element.addClass('animejs-element'); 424 425 // Add selected devices as a data attribute 426 if (settings.animejs_devices && settings.animejs_devices.length > 0) { 427 $element.attr('data-anime-devices', settings.animejs_devices.join(',')); 428 } else { 429 $element.removeAttr('data-anime-devices'); 430 } 431 432 // Check if the current device is allowed before applying the animation 433 if (this.isDeviceAllowed($element[0])) { 434 // Re-apply animation 435 this.enableAnimation($element[0]); 436 } else { 437 // Disable animation if the current device is not allowed 438 this.disableAnimation($element[0]); 439 } 440 } 441 } else { 442 // Remove data-anime attribute and classes 443 $element.removeAttr('data-anime'); 444 $element.removeAttr('data-anime-devices'); 445 $element.removeClass('animejs-element'); 446 447 this.disableAnimation($element[0]); 448 } 449 450 if (settings.animejs_animation_type !== 'onscroll') { 451 destroyScrollMagicInstance($element); 452 } 453 }, 454 455 generateDataAnime: function(settings) { 456 let dataAnime = []; 457 458 if (settings.animejs_onscroll_viewport === 'top') { 459 settings.animejs_onscroll_viewport = 0; 460 } else if (settings.animejs_onscroll_viewport === 'center') { 461 settings.animejs_onscroll_viewport = 0.5; 462 } else { 463 settings.animejs_onscroll_viewport = 1; 464 } 465 466 if (settings.animejs_animation_type === 'onview') { 467 dataAnime.push(`onview: ${settings.animejs_onview_trigger_viewport}`); 468 if (settings.animejs_onview_repeat === 'yes') { 469 dataAnime.push(`loop: true`); 470 } else { 471 dataAnime.push(`loop: false`); 472 } 473 if (settings.animejs_onview_direction === 'alternate') { 474 dataAnime.push(`direction: alternate`); 475 } else if (settings.animejs_onview_direction === 'reverse') { 476 dataAnime.push(`direction: reverse`); 477 } 478 } else if (settings.animejs_animation_type === 'onscroll') { 479 dataAnime.push(`onscroll: ${settings.animejs_onscroll_scene || 'true'}`); 480 dataAnime.push(`onscroll-trigger: ${settings.animejs_onscroll_viewport}`); 481 dataAnime.push(`onscroll-duration: ${settings.animejs_onscroll_duration.size}%`); 482 dataAnime.push(`onscroll-offset: ${settings.animejs_onscroll_offset}`); 483 } 484 485 if (settings.animejs_animation_type !== 'disable') { 486 487 const properties = ['opacity', 'translateX', 'translateY', 'scale', 'rotate', 'skew']; 488 properties.forEach(prop => { 489 const from = settings[`animejs_animation_from_${prop}`]; 490 const to = settings[`animejs_animation_to_${prop}`]; 491 if (from !== '' && to !== '') { 492 dataAnime.push(`${prop}: [${from}, ${to}]`); 493 } else if (from !== '') { 494 dataAnime.push(`${prop}: ${from}`); 495 } else if (to !== '') { 496 dataAnime.push(`${prop}: ${to}`); 497 } 498 }); 499 500 let easing = settings.animejs_animation_easing; 501 if (easing === 'custom') { 502 easing = settings.animejs_animation_easing_custom; 503 } 504 dataAnime.push(`easing: '${easing}'`); 505 dataAnime.push(`duration: ${settings.animejs_animation_speed}`); 506 dataAnime.push(`delay: ${settings.animejs_animation_delay}`); 507 508 if (settings.animejs_onview_targets && settings.animejs_onview_staggering) {
509 let stagger = `anime.stagger(${settings.animejs_onview_staggering}`; 510 if (settings.animejs_onview_staggering_start_after) { 511 stagger += `, {start: ${settings.animejs_onview_staggering_start_after}`; 512 if (settings.animejs_onview_staggering_from && settings.animejs_onview_staggering_from !== 'first') { 513 stagger += `, from: '${settings.animejs_onview_staggering_from}'`; 514 } 515 stagger += `}`; 516 } 517 stagger += `)`; 518 dataAnime.push(`delay: ${stagger}`); 519 } 520 521 } 522 523 return dataAnime.join('; '); 524 } 525}; 526 527// Initialize AnimeJSHelper 528$(document).ready(AnimeJSHelper.init.bind(AnimeJSHelper)); 529 530// Keep the existing event listeners and function calls 531dataAttrHelpers.watchDataAttr('data-anime', runDataAnime); 532dataAttrHelpers.watchDataAttr('data-anime-toggle', runDataAnimeToggle);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.