PluginProbe
Easy Elements for Elementor – Addons & Website Templates / 1.4.5
Easy Elements for Elementor – Addons & Website Templates v1.4.5
1.5.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.6 1.4.7 1.4.8 1.4.5 1.4.4 1.4.3 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 All 47 releases
easy-elements / assets / js / custom.js

custom.js in Easy Elements for Elementor – Addons & Website Templates 1.4.5, at assets/js/custom.js

1,317 lines 45.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function($) {
2 "use strict";
3 function initBackPostSlider($scope) {
4 var $slider = $scope.find('.eel-all-slider');
5 if (!$slider.length) {
6 return;
7 }
8 var loop = $slider.data('loop') === true || $slider.data('loop') === 'true';
9 var autoplay = $slider.data('autoplay') === true || $slider.data('autoplay') === 'true';
10 var autoplayDelay = parseInt($slider.data('autoplay-delay'), 10) || 3000;
11 var slidesPerView = parseInt($slider.data('slides-per-view'), 10) || 3;
12 var spvSkin1 = parseInt($slider.data('slides-per-view-skin1'), 10);
13 var spvSkin4 = parseInt($slider.data('slides-per-view-skin4'), 10);
14 var spvSpecificDefault = parseInt($slider.data('slides-per-view-default'), 10);
15 var finalSlidesPerView;
16 if (spvSkin1) {
17 finalSlidesPerView = spvSkin1;
18 } else if (spvSkin4) {
19 finalSlidesPerView = spvSkin4;
20 } else if (spvSpecificDefault) {
21 finalSlidesPerView = spvSpecificDefault;
22 } else {
23 finalSlidesPerView = slidesPerView;
24 }
25 var speed = parseInt($slider.data('speed'), 10) || 600;
26 var centeredSlides = $slider.data('centered-slides') === true || $slider.data('centered-slides') === 'true';
27 var freeMode = $slider.data('free-mode') === true || $slider.data('free-mode') === 'true';
28 var spaceBetween = parseInt($slider.data('space-between'), 10) || 0;
29 var slidesPerViewTablet = parseInt($slider.data('slides-per-view-tablet'), 10) || 2;
30 var slidesPerViewMobile = parseInt($slider.data('slides-per-view-mobile'), 10) || 1;
31 var paginationType = $slider.data('pagination') !== 'none' ? $slider.data('pagination') : false;
32 var navigation = $slider.data('navigation') === true || $slider.data('navigation') === 'true';
33 var effect = $slider.data('effect') || 'slide';
34 var cubeShadow = $slider.data('cube-shadow') === true || $slider.data('cube-shadow') === 'true';
35 var coverflowRotate = parseInt($slider.data('coverflow-rotate'), 10) || 50;
36 var coverflowStretch = parseInt($slider.data('coverflow-stretch'), 10) || 0;
37
38 // Find navigation elements within the scope
39 var $nextButton = $scope.find('.swiper-next');
40 var $prevButton = $scope.find('.swiper-prev');
41 var $pagination = $scope.find('.swiper-pagination');
42
43 // Prevent multiple instances and duplicated handlers if Elementor re-runs init
44 if ($slider[0] && $slider[0].swiper) {
45 try { $slider[0].swiper.destroy(true, true); } catch (e) {}
46 }
47 if ($nextButton.length) { $nextButton.off('click.easyStopAuto click.easyFadeNav'); }
48 if ($prevButton.length) { $prevButton.off('click.easyStopAuto click.easyFadeNav'); }
49
50 var hasProgressBar = $pagination.hasClass('swiper-pagination-progressbar');
51 var kbDirection = $slider.data('kenburns-direction') || 'in';
52
53 var effectOptions = {};
54
55 if (effect === 'cube') {
56 effectOptions = {
57 cubeEffect: {
58 shadow: cubeShadow,
59 slideShadows: cubeShadow,
60 shadowOffset: 20,
61 shadowScale: 0.94,
62 }
63 };
64 } else if (effect === 'coverflow') {
65 effectOptions = {
66 coverflowEffect: {
67 rotate: coverflowRotate,
68 stretch: coverflowStretch,
69 depth: 100,
70 modifier: 1,
71 slideShadows: true,
72 }
73 };
74 } else if (effect === 'flip') {
75 effectOptions = {
76 flipEffect: {
77 slideShadows: true,
78 limitRotation: true,
79 }
80 };
81 } else if (effect === 'cards') {
82 effectOptions = {
83 cardsEffect: {
84 slideShadows: true,
85 rotate: true,
86 perSlideOffset: 8,
87 }
88 };
89 } else if (effect === 'creative') {
90 var creativeStyle = $slider.data('creative-style') || 'default';
91 if (creativeStyle === 'zoom') {
92 effectOptions = {
93 creativeEffect: {
94 limitProgress: true,
95 prev: { scale: 1.1, opacity: 0, translate: [0, 0, 0] },
96 next: { scale: 1.6, opacity: 0, translate: [0, 0, 0] },
97 },
98 };
99 } else {
100 effectOptions = {
101 creativeEffect: {
102 limitProgress: true,
103 prev: { shadow: true, translate: [0, 0, -400] },
104 next: { translate: ['100%', 0, 0] },
105 },
106 };
107 }
108 } else if (effect === 'fade') {
109 effectOptions = {
110 fadeEffect: { crossFade: true }
111 };
112 } else if (effect === 'kenburns') {
113 effectOptions = {
114 effect: 'fade',
115 fadeEffect: { crossFade: true },
116 on: {
117 init: function () {
118 var swiper = this;
119 setTimeout(function() {
120 var $activeSlide = $(swiper.el).find('.swiper-slide-active');
121 $activeSlide.find('.hero-content-wrapper').addClass('eel-text-animated');
122 $activeSlide.find('.hero-slide-background img').addClass('eel-kb-active-' + kbDirection);
123 }, 200);
124 },
125 slideChange: function () {
126 var $allSlides = $(this.el).find('.swiper-slide');
127 $allSlides.find('.hero-content-wrapper').removeClass('eel-text-animated');
128 $allSlides.find('.hero-slide-background img').removeClass('eel-kb-active-in eel-kb-active-out');
129 },
130 transitionEnd: function () {
131 var swiper = this;
132 var $activeSlide = $(swiper.el).find('.swiper-slide-active');
133 $activeSlide.find('.hero-content-wrapper').addClass('eel-text-animated');
134 $activeSlide.find('.hero-slide-background img').addClass('eel-kb-active-' + kbDirection);
135 }
136 }
137 };
138 }
139
140 // Loop handling tuned to avoid double-advance on loop edges
141 var totalSlides = $slider.find('.swiper-slide').length;
142 var isFade = effect === 'fade';
143 var visibleSlides = isFade ? 1 : slidesPerView;
144 var canLoop = totalSlides > visibleSlides;
145 var loopConfig = {};
146 if (loop && (canLoop || isFade)) {
147 loopConfig = {
148 loop: true,
149 loopAdditionalSlides: isFade ? totalSlides : 20,
150 loopedSlides: isFade ? totalSlides : Math.max(Math.ceil(visibleSlides), Math.min(totalSlides, 10))
151 };
152 } else {
153 loopConfig = { loop: false, rewind: !!loop };
154 }
155
156 var swiper = new Swiper($slider[0], {
157 ...loopConfig,
158 speed: speed,
159 effect: effect,
160 normalizeSlideIndex: true,
161 preventInteractionOnTransition: true,
162 simulateTouch: true,
163 touchRatio: 1,
164 grabCursor: true,
165 mousewheel: hasProgressBar ? {
166 enabled: true,
167 sensitivity: 1,
168 thresholdDelta: 50,
169 thresholdTime: 200,
170 releaseOnEdges: true,
171 invert: false,
172 forceToAxis: false,
173 eventsTarget: 'container'
174 } : false,
175 autoplay: autoplay ? {
176 delay: autoplayDelay,
177 disableOnInteraction: isFade ? true : false,
178 } : false,
179 slidesPerView: visibleSlides,
180 spaceBetween: isFade ? 0 : spaceBetween,
181 centeredSlides: isFade ? false : centeredSlides,
182 freeMode: freeMode,
183 pagination: paginationType && $pagination.length ? {
184 el: $pagination[0],
185 clickable: true,
186 type: paginationType,
187 renderBullet: function (index, className) {
188 let num = (index + 1).toString().padStart(2, '0');
189 return `<span class="${className}">${num}</span>`;
190 }
191 } : false,
192
193 // Progress bar for mouse scroll - only when swiper-pagination-progressbar class is present
194 progressbar: hasProgressBar ? {
195 el: '.swiper-progressbar',
196 type: 'progressbar',
197 } : false,
198
199 navigation: (!isFade) && navigation && ($nextButton.length || $prevButton.length) ? {
200 nextEl: $nextButton.length ? $nextButton[0] : null,
201 prevEl: $prevButton.length ? $prevButton[0] : null,
202 } : false,
203 breakpoints: {
204 0: {
205 slidesPerView: isFade ? 1 : slidesPerViewMobile,
206 },
207 768: {
208 slidesPerView: isFade ? 1 : slidesPerViewTablet,
209 },
210 1024: {
211 slidesPerView: isFade ? 1 : finalSlidesPerView,
212 },
213 },
214 ...effectOptions,
215 });
216
217 // Fade: attach custom navigation so Next/Prev work
218 if (isFade && ($nextButton.length || $prevButton.length)) {
219 var isTransitioning = false;
220 swiper.on('transitionStart', function () { isTransitioning = true; });
221 swiper.on('transitionEnd', function () { isTransitioning = false; });
222 if ($nextButton.length) {
223 $nextButton.off('click.easyFadeNav').on('click.easyFadeNav', function (e) {
224 e.preventDefault();
225 e.stopPropagation();
226 if (swiper && swiper.autoplay && swiper.autoplay.stop) { swiper.autoplay.stop(); }
227 if (isTransitioning || (swiper && swiper.animating)) return;
228 swiper.slideNext();
229 });
230 }
231 if ($prevButton.length) {
232 $prevButton.off('click.easyFadeNav').on('click.easyFadeNav', function (e) {
233 e.preventDefault();
234 e.stopPropagation();
235 if (swiper && swiper.autoplay && swiper.autoplay.stop) { swiper.autoplay.stop(); }
236 if (isTransitioning || (swiper && swiper.animating)) return;
237 swiper.slidePrev();
238 });
239 }
240 }
241
242 // Non-fade: if loop+autoplay, stop autoplay on manual nav to avoid double-advance
243 if (!isFade && loop && autoplay) {
244 if ($nextButton.length) {
245 $nextButton.on('click.easyStopAuto', function () {
246 if (swiper && swiper.autoplay && swiper.autoplay.stop) {
247 swiper.autoplay.stop();
248 }
249 });
250 }
251 if ($prevButton.length) {
252 $prevButton.on('click.easyStopAuto', function () {
253 if (swiper && swiper.autoplay && swiper.autoplay.stop) {
254 swiper.autoplay.stop();
255 }
256 });
257 }
258 }
259 }
260
261
262 if (!window.EEL_AddWidgetTypeBodyClass) {
263 window.EEL_AddWidgetTypeBodyClass = true;
264
265 function addWidgetTypeToBody($scope) {
266 if (!$scope || !$scope.length) return;
267
268 var widgetType = $scope.data('widget_type');
269 if (!widgetType) return;
270
271 var safeWidgetType = widgetType
272 .toString()
273 .replace(/[^a-z0-9_-]/gi, '-')
274 .toLowerCase();
275
276 var className = 'easy-eel-has-widget-' + safeWidgetType;
277
278 if (!document.body.classList.contains(className)) {
279 document.body.classList.add(className);
280 }
281 }
282
283 /* -------------------------------------------------
284 * Elementor Hooks
285 * ------------------------------------------------- */
286 $(window).on('elementor/frontend/init', function () {
287
288 // Slider init
289 elementorFrontend.hooks.addAction(
290 'frontend/element_ready/global',
291 initBackPostSlider
292 );
293
294 // Widget type → body class
295 elementorFrontend.hooks.addAction(
296 'frontend/element_ready/global',
297 function ($scope) {
298 addWidgetTypeToBody($scope);
299 }
300 );
301
302 });
303 }
304
305
306 $(window).on('elementor/frontend/init', function () {
307
308 function checkStickySection() {
309 const stickySection = document.querySelector('.eel-sticky-section');
310 if (stickySection) {
311 document.body.classList.add('sticky-enabled-overlap');
312 } else {
313 document.body.classList.remove('sticky-enabled-overlap');
314 }
315 }
316
317 checkStickySection();
318
319 const observer = new MutationObserver(checkStickySection);
320 observer.observe(document.body, { childList: true, subtree: true });
321 });
322
323
324
325 $(window).on('elementor/frontend/init', function () {
326 function initReveal_ad($scope) {
327 const revealElements = $scope.find(".eel-img-advance-wrap");
328 const revealAnimate = (el) => {
329 const target = el.querySelector(".eel-advance-img-main");
330 if(target && !target.classList.contains("el-reveal-animate")) {
331 target.classList.add("el-reveal-animate");
332 }
333 };
334 const isEditor = window.elementorFrontend && elementorFrontend.isEditMode && elementorFrontend.isEditMode();
335 if(isEditor){
336 revealElements.each(function(){
337 revealAnimate(this);
338 });
339 } else {
340 const observer = new IntersectionObserver((entries, obs) => {
341 entries.forEach(entry => {
342 if(entry.isIntersecting){
343 revealAnimate(entry.target);
344 obs.unobserve(entry.target);
345 }
346 });
347 }, { threshold: 0.3 });
348
349 revealElements.each(function(){
350 observer.observe(this);
351 });
352
353 // Lenis safe
354 if (window.Lenis && typeof window.Lenis.raf === 'function') {
355 window.Lenis.raf(() => {});
356 }
357 }
358 }
359
360 elementorFrontend.hooks.addAction(
361 'frontend/element_ready/eel-advance-image.default',
362 initReveal_ad
363 );
364
365 function initReveal($scope) {
366 const revealElements = $scope.find(".eel-img-reveal-wrap");
367
368 const revealAnimate = (el) => {
369 const target = el.querySelector(".eel-reveal-img-main");
370 if(target && !target.classList.contains("el-reveal-animate")) {
371 target.classList.add("el-reveal-animate");
372 }
373 };
374
375 const isEditor = window.elementorFrontend && elementorFrontend.isEditMode && elementorFrontend.isEditMode();
376
377 if(isEditor){
378 revealElements.each(function(){
379 revealAnimate(this);
380 });
381 } else {
382 const observer = new IntersectionObserver((entries, obs) => {
383 entries.forEach(entry => {
384 if(entry.isIntersecting){
385 revealAnimate(entry.target);
386 obs.unobserve(entry.target);
387 }
388 });
389 }, { threshold: 0.3 });
390
391 revealElements.each(function(){
392 observer.observe(this);
393 });
394
395 // Lenis safe
396 if (window.Lenis && typeof window.Lenis.raf === 'function') {
397 window.Lenis.raf(() => {});
398 }
399 }
400 }
401
402 elementorFrontend.hooks.addAction(
403 'frontend/element_ready/eel-image-reveal.default',
404 initReveal
405 );
406
407 });
408
409
410 $(window).on("elementor/frontend/init", function () {
411 var EasyJarallax = function ($scope, $) {
412 if (typeof jarallax === 'undefined') return;
413
414 var $wrappers = $scope.hasClass('eele-has-jarallax') ? $scope : $scope.find('.eele-has-jarallax, .eele-inner-jarallax');
415 if (!$wrappers.length) return;
416
417 $wrappers.each(function () {
418 var $wrapper = $(this);
419 var bg = $wrapper.data('jarallax-bg');
420 var speed = $wrapper.data('jarallax-speed') || 0.5;
421 if (!bg) return;
422
423 // Destroy previous instance if re-rendering in editor
424 var $oldJarallax = $wrapper.find('.jarallax');
425 if ($oldJarallax.length) {
426 $oldJarallax.each(function () {
427 jarallax(this, 'destroy');
428 });
429 $oldJarallax.remove();
430 }
431
432 var $div = $('<div>', { 'class': 'jarallax eele-jarallax-bg', 'data-speed': speed });
433 var $img = $('<img>', { 'class': 'jarallax-img', src: bg, alt: '', decoding: 'async' });
434 $div.append($img);
435 $wrapper.append($div);
436
437 jarallax($div[0], {
438 speed: speed,
439 imgPosition: '50% 50%',
440 imgRepeat: 'no-repeat',
441 imgSize: 'cover',
442 zIndex: -100
443 });
444 });
445 };
446
447 // Image widget parallax
448 var EasyJarallaxImage = function ($scope, $) {
449 if (typeof jarallax === 'undefined') return;
450 if (!$scope.hasClass('eele-has-jarallax-img-yes')) return;
451
452 var $img = $scope.find('.elementor-widget-container img').first();
453 if (!$img.length || !$img.attr('src')) return;
454
455 var $container = $scope.find('.elementor-widget-container');
456
457 // If already wrapped from a previous run (editor re-render), unwrap to start clean
458 if ($img.parent('.eele-img-jarallax-wrap').length) {
459 try { jarallax($img.parent()[0], 'destroy'); } catch (e) {}
460 $img.unwrap();
461 $img.removeClass('jarallax-img');
462 }
463
464 // Clean up any leftover empty jarallax bg divs from older buggy code
465 $container.find('> .eele-jarallax-bg, > a > .eele-jarallax-bg').each(function () {
466 try { jarallax(this, 'destroy'); } catch (e) {}
467 }).remove();
468
469 // Wrap the original image so it becomes the parallax source itself.
470 // Do NOT add `eele-jarallax-bg` — that class has global CSS
471 // (position:absolute; z-index:-1) for container background parallax,
472 // which would hide the image entirely.
473 $img.addClass('jarallax-img');
474 $img.wrap('<div class="jarallax jarallax-container eele-img-jarallax-wrap"></div>');
475 var $div = $img.parent();
476
477 function initParallax() {
478 // Give the wrapper a size so it doesn't collapse once jarallax positions
479 // the img absolutely. User-set width/height (via panel) uses !important
480 // and will still override these fallbacks.
481 var nW = $img[0].naturalWidth;
482 var nH = $img[0].naturalHeight;
483 if (nW && nH) {
484 if (!$div[0].style.width) $div.css('width', '100%');
485 if (!$div[0].style.height) $div.css('aspect-ratio', nW + ' / ' + nH);
486 } else if (!$div[0].style.height) {
487 var fallbackH = $div[0].getBoundingClientRect().height;
488 if (fallbackH > 0) $div.css('height', fallbackH + 'px');
489 }
490
491 jarallax($div[0], {
492 speed: 0.5,
493 imgPosition: '50% 50%',
494 imgRepeat: 'no-repeat',
495 imgSize: 'cover',
496 zIndex: 0
497 });
498 }
499
500 if ($img[0].complete && $img[0].naturalWidth > 0) {
501 initParallax();
502 } else {
503 $img.one('load', initParallax);
504 }
505 };
506
507 elementorFrontend.hooks.addAction("frontend/element_ready/global", EasyJarallax);
508 elementorFrontend.hooks.addAction("frontend/element_ready/image.default", EasyJarallaxImage);
509 elementorFrontend.hooks.addAction("frontend/element_ready/eel-advance-image.default", EasyJarallaxImage);
510
511 // --- Lenis integration for Jarallax ---
512 function syncJarallaxWithLenis() {
513 if (window.lenis && typeof jarallax !== 'undefined') {
514 window.lenis.on('scroll', function () {
515 jarallax(document.querySelectorAll('.jarallax'), 'onScroll');
516 });
517 } else {
518 setTimeout(syncJarallaxWithLenis, 200);
519 }
520 }
521 syncJarallaxWithLenis();
522 });
523
524
525 jQuery(window).on('elementor/frontend/init', function() {
526
527 elementorFrontend.hooks.addAction('frontend/element_ready/eel-video-popup.default', function($scope, $) {
528
529 const wrapper = $scope.find('.eel-video-popup-wrapper');
530 const overlay = wrapper.find('.eel-video-overlay');
531 // Find video element (try with class first, then without)
532 let videoEl = wrapper.find('video.eel-normal-video');
533 if (!videoEl.length) {
534 videoEl = wrapper.find('video');
535 }
536 // Find iframe element (try with class first, then without)
537 let iframeEl = wrapper.find('iframe.eel-normal-video');
538 if (!iframeEl.length) {
539 iframeEl = wrapper.find('iframe');
540 }
541
542 // Helper function to get YouTube embed URL
543 function getYouTubeEmbedUrl(url) {
544 const regExp = /(?:v=|\/)([0-9A-Za-z_-]{11}).*/;
545 const match = url.match(regExp);
546 if (match && match[1]) {
547 return 'https://www.youtube.com/embed/' + match[1] + '?autoplay=1';
548 }
549 return null;
550 }
551
552 // Helper function to get Vimeo embed URL
553 function getVimeoEmbedUrl(url) {
554 const match = url.match(/(\d+)/);
555 if (match && match[1]) {
556 return 'https://player.vimeo.com/video/' + match[1] + '?autoplay=1';
557 }
558 return null;
559 }
560
561 // Helper function to open lightbox
562 function openLightbox(videoType, videoUrl, popupId, animation) {
563 const lightboxOverlay = $('#' + popupId);
564 const iframeWrapper = lightboxOverlay.find('.eel-video-popup-iframe-wrapper');
565 const lightboxContent = lightboxOverlay.find('.eel-video-popup-content');
566
567 if (!lightboxOverlay.length) return;
568
569 // Prevent duplicate opening if already active
570 if (lightboxOverlay.hasClass('active')) {
571 return;
572 }
573
574 let embedHtml = '';
575
576 if (videoType === 'youtube') {
577 const embedUrl = getYouTubeEmbedUrl(videoUrl);
578 if (embedUrl) {
579 embedHtml = '<iframe src="' + embedUrl + '" frameborder="0" allow="autoplay; fullscreen" allowfullscreen></iframe>';
580 }
581 } else if (videoType === 'vimeo') {
582 const embedUrl = getVimeoEmbedUrl(videoUrl);
583 if (embedUrl) {
584 embedHtml = '<iframe src="' + embedUrl + '" frameborder="0" allow="autoplay; fullscreen" allowfullscreen></iframe>';
585 }
586 } else if (videoType === 'self_hosted') {
587 embedHtml = '<video src="' + videoUrl + '" controls autoplay></video>';
588 }
589
590 if (embedHtml) {
591 iframeWrapper.html(embedHtml);
592
593 // Apply animation if set
594 if (animation && animation.trim() !== '') {
595 // Remove any existing animation classes
596 const currentClasses = lightboxContent.attr('class') || '';
597 const animationClasses = currentClasses.match(/\banimated\s+\S+/g);
598 if (animationClasses) {
599 lightboxContent.removeClass(animationClasses.join(' '));
600 }
601 // Add new animation classes
602 lightboxContent.addClass('animated ' + animation.trim());
603 } else {
604 // Remove animation classes if no animation
605 const currentClasses = lightboxContent.attr('class') || '';
606 const animationClasses = currentClasses.match(/\banimated\s+\S+/g);
607 if (animationClasses) {
608 lightboxContent.removeClass(animationClasses.join(' '));
609 }
610 }
611
612 lightboxOverlay.addClass('active');
613 $('body').css('overflow', 'hidden');
614 }
615 }
616
617 // Close lightbox handler
618 $(document).on('click', '.eel-video-popup-close', function(e) {
619 e.preventDefault();
620 const lightboxOverlay = $(this).closest('.eel-video-popup-overlay');
621 const lightboxContent = lightboxOverlay.find('.eel-video-popup-content');
622 lightboxOverlay.removeClass('active');
623 lightboxContent.removeClass('animated');
624 lightboxOverlay.find('.eel-video-popup-iframe-wrapper').html('');
625 $('body').css('overflow', '');
626 });
627
628 // Close lightbox on overlay click
629 $(document).on('click', '.eel-video-popup-overlay', function(e) {
630 if ($(e.target).hasClass('eel-video-popup-overlay')) {
631 const lightboxContent = $(this).find('.eel-video-popup-content');
632 $(this).removeClass('active');
633 lightboxContent.removeClass('animated');
634 $(this).find('.eel-video-popup-iframe-wrapper').html('');
635 $('body').css('overflow', '');
636 }
637 });
638
639 // Close lightbox on ESC key
640 $(document).on('keydown', function(e) {
641 if (e.keyCode === 27) { // ESC key
642 $('.eel-video-popup-overlay.active').each(function() {
643 const lightboxContent = $(this).find('.eel-video-popup-content');
644 $(this).removeClass('active');
645 lightboxContent.removeClass('animated');
646 $(this).find('.eel-video-popup-iframe-wrapper').html('');
647 $('body').css('overflow', '');
648 });
649 }
650 });
651
652 if (overlay.length) {
653 // Remove existing handler to prevent duplicate
654 overlay.off('click.eelVideoOverlay');
655 overlay.on('click.eelVideoOverlay', function(e) {
656 e.preventDefault();
657 e.stopPropagation();
658
659 const $overlay = $(this);
660 const useLightbox = $overlay.data('lightbox') === 'yes';
661 const videoType = $overlay.data('video-type');
662 const videoUrl = $overlay.data('video-url');
663 const popupId = $overlay.data('popup-id');
664 const animation = $overlay.data('animation') || '';
665
666 // If lightbox is enabled, open lightbox
667 if (useLightbox && videoType && videoUrl && popupId) {
668 // Check if lightbox is already open to prevent duplicate
669 const lightboxOverlay = $('#' + popupId);
670 if (lightboxOverlay.hasClass('active')) {
671 return;
672 }
673 openLightbox(videoType, videoUrl, popupId, animation);
674 return;
675 }
676
677 // Otherwise, play inline (original behavior)
678 // Self-hosted video play
679 if (videoEl.length) {
680 const video = videoEl.get(0);
681
682 // Function to play video
683 const playVideo = function() {
684 const playPromise = video.play();
685 if (playPromise !== undefined) {
686 playPromise.catch(function(error) {
687 console.log('Video play failed:', error);
688 // If play fails due to autoplay policy, try with muted
689 if (error.name === 'NotAllowedError' && !video.muted) {
690 video.muted = true;
691 video.play().then(function() {
692 // Unmute after play starts (if user wants sound)
693 setTimeout(function() {
694 video.muted = false;
695 }, 100);
696 }).catch(function(err) {
697 console.log('Video play with mute also failed:', err);
698 });
699 }
700 });
701 }
702 };
703
704 // Always try to load and play
705 if (video.readyState >= 2) {
706 // Video is already loaded, play immediately
707 playVideo();
708 } else {
709 // Video not loaded, load it first then play
710 video.load();
711 // Wait for video to be ready to play
712 const tryPlay = function() {
713 if (video.readyState >= 2) {
714 playVideo();
715 } else {
716 video.addEventListener('canplay', playVideo, { once: true });
717 video.addEventListener('loadeddata', playVideo, { once: true });
718 }
719 };
720 tryPlay();
721 }
722 }
723
724 // YouTube / Vimeo iframe autoplay
725 if (iframeEl.length) {
726 let src = iframeEl.attr('src');
727 if (src) {
728 // Check if it's YouTube or Vimeo
729 const isYouTube = src.indexOf('youtube.com') !== -1 || src.indexOf('youtu.be') !== -1;
730 const isVimeo = src.indexOf('vimeo.com') !== -1;
731
732 if (isYouTube || isVimeo) {
733 try {
734 // Use URL object for proper parsing
735 const url = new URL(src);
736
737 if (isYouTube) {
738 // YouTube: add autoplay, preserve original mute setting
739 url.searchParams.set('autoplay', '1');
740 // Only set mute=1 if it was already in the URL (preserve original setting)
741 // If mute was not in original URL, don't add it
742 if (!url.searchParams.has('mute')) {
743 // Don't add mute if it wasn't there originally
744 } else {
745 // Keep the original mute value
746 }
747 // Reload iframe with new src
748 iframeEl.attr('src', url.toString());
749 } else if (isVimeo) {
750 // Vimeo: add autoplay (Vimeo doesn't need mute for autoplay)
751 url.searchParams.set('autoplay', '1');
752 // Reload iframe with new src
753 iframeEl.attr('src', url.toString());
754 }
755 } catch (e) {
756 // Fallback for browsers that don't support URL constructor
757 // Remove existing autoplay parameter if present
758 src = src.replace(/[?&]autoplay=[^&]*/g, '');
759
760 if (isYouTube) {
761 // Check if mute parameter exists in original URL
762 const hasMute = /[?&]mute=/.test(src);
763 // Add autoplay=1
764 const separator = src.indexOf('?') === -1 ? '?' : '&';
765 src += separator + 'autoplay=1';
766 // Only add mute if it was already there (preserve original setting)
767 // Don't add mute if it wasn't in original URL
768 } else if (isVimeo) {
769 // Vimeo: just add autoplay=1
770 const separator = src.indexOf('?') === -1 ? '?' : '&';
771 src += separator + 'autoplay=1';
772 }
773
774 // Update src without clearing first
775 iframeEl.attr('src', src);
776 }
777 }
778 }
779 }
780
781 // Remove overlay after a small delay to ensure video starts
782 setTimeout(function() {
783 overlay.fadeOut(300, function() {
784 $(this).remove();
785 });
786 }, 200);
787 });
788 }
789
790 // Handle popup button clicks (for display_type = 'popup')
791 const popupBtn = wrapper.find('.entro-all-video-popup');
792 if (popupBtn.length) {
793 // If the global popup (from pro plugin) exists, let eel-popup.js handle it
794 // to avoid transform/motion effect containment issues with position:fixed
795 const globalPopup = document.querySelector('.entro-all-video-popup-wrap');
796 if (globalPopup) {
797 // Global popup exists — do not bind per-widget handler.
798 // The pro plugin's eel-popup.js will handle it since it's already in body.
799 } else {
800 // No global popup — use per-widget overlay, moved to body
801 const popupOverlay = wrapper.find('.eel-video-popup-overlay');
802 if (popupOverlay.length && !popupOverlay.data('moved-to-body')) {
803 popupOverlay.data('moved-to-body', true);
804 popupOverlay.appendTo('body');
805 }
806
807 // Remove existing handler to prevent duplicate
808 popupBtn.off('click.eelVideoPopup');
809 popupBtn.on('click.eelVideoPopup', function(e) {
810 e.preventDefault();
811 e.stopPropagation();
812 const $btn = $(this);
813 const videoType = $btn.data('video-type');
814 const videoUrl = $btn.data('video-src');
815 const popupId = $btn.data('popup-id');
816 const lightboxOverlay = $('#' + popupId);
817 const animation = lightboxOverlay.data('animation') || '';
818
819 if (videoType && videoUrl && popupId) {
820 // Check if lightbox is already open to prevent duplicate
821 if (lightboxOverlay.hasClass('active')) {
822 return;
823 }
824 openLightbox(videoType, videoUrl, popupId, animation);
825 }
826 });
827 }
828 }
829 });
830
831 });
832
833 // Single Page Navigation
834 document.addEventListener('DOMContentLoaded', function () {
835 const navWrappers = document.querySelectorAll('.eel-single-nav-list');
836 if (!navWrappers.length) return;
837
838 navWrappers.forEach(function (wrapper) {
839
840 const navLinks = wrapper.querySelectorAll('.eel-nav-item a');
841 const navItems = wrapper.querySelectorAll('.eel-nav-item');
842
843 if (!navLinks.length) return;
844
845 wrapper.addEventListener('click', function (e) {
846
847 const link = e.target.closest('.eel-nav-item a');
848 if (!link || !wrapper.contains(link)) return;
849
850 const href = link.getAttribute('href');
851
852 if (!href || href === '#' || !href.startsWith('#') || href.length === 1) {
853 return;
854 }
855
856 const target = document.querySelector(href);
857 if (!target) return;
858
859 e.preventDefault();
860
861 target.scrollIntoView({
862 behavior: 'smooth',
863 block: 'start'
864 });
865 });
866
867 /* ========= SCROLL → ACTIVE CHANGE ========= */
868 const sectionsMap = new Map();
869
870 navLinks.forEach(link => {
871 const href = link.getAttribute('href');
872
873 if (!href || href === '#' || !href.startsWith('#') || href.length === 1) {
874 return;
875 }
876
877 const section = document.querySelector(href);
878 if (section) {
879 sectionsMap.set(section, href);
880 }
881 });
882
883 function setActive(href) {
884 navItems.forEach(item => item.classList.remove('active'));
885
886 navLinks.forEach(link => {
887 if (link.getAttribute('href') === href) {
888 link.closest('.eel-nav-item')?.classList.add('active');
889 }
890 });
891 }
892
893 const observer = new IntersectionObserver(
894 (entries) => {
895 entries.forEach(entry => {
896 if (entry.isIntersecting) {
897 const id = sectionsMap.get(entry.target);
898 if (id) setActive(id);
899 }
900 });
901 },
902 {
903 root: null,
904 rootMargin: '-40% 0px -40% 0px',
905 threshold: 0.1
906 }
907 );
908
909 sectionsMap.forEach((id, section) => {
910 observer.observe(section);
911 });
912
913 });
914
915 });
916
917 // Sticky Navigation single page
918
919 function applyStickyToContainer($container) {
920 if ($container.find('.elementor-widget-eel-single-nav').length === 0) {
921 return;
922 }
923
924 $container.addClass('eel-single-nav-sticky-enabled');
925 }
926
927 /* ===== Frontend ===== */
928 $(window).on('elementor/frontend/init', function () {
929 elementorFrontend.hooks.addAction(
930 'frontend/element_ready/container',
931 function ($scope) {
932 applyStickyToContainer($scope);
933 }
934 );
935 });
936
937 /* ===== Editor ===== */
938 if (window.elementor && elementor.isEditMode()) {
939 elementor.hooks.addAction('editor:init', function () {
940 elementorFrontend.hooks.addAction(
941 'frontend/element_ready/container',
942 function ($scope) {
943 applyStickyToContainer($scope);
944 }
945 );
946 });
947 }
948
949 // Mobile Menu Sidebar
950 if ($('.sidebar-on-mobile-single-nav').length) {
951 // Sidebar toggle
952 $('.eel-nav-menu-icon').on('click', function(e){
953 e.stopPropagation();
954 $('.sidebar-on-mobile-single-nav').toggleClass('easyel-open');
955 $('body').toggleClass('sidebar-on-mobile-single-nav-active');
956 });
957 // Click outside to close sidebar
958 $(document).on('click', function(){
959 $('.sidebar-on-mobile-single-nav').removeClass('easyel-open');
960 $('body').removeClass('sidebar-on-mobile-single-nav-active');
961 $('.sidebar-on-mobile-single-nav ul.sub-menu').slideUp(300);
962 $('.sidebar-on-mobile-single-nav .sub-arrow').removeClass('active').attr('aria-expanded', false);
963 });
964
965 $('.sidebar-on-mobile-single-nav, .eel-nav-menu').on('click', 'li:not(.menu-item-has-children) a', function(e){
966 $('.sidebar-on-mobile-single-nav li a, .eel-nav-menu li a').removeClass('eel-active');
967 $(this).addClass('eel-active');
968 $('.sidebar-on-mobile-single-nav').removeClass('easyel-open');
969 $('body').removeClass('sidebar-on-mobile-single-nav-active');
970 $('body').removeClass('sidebar-on-mobile-active');
971 });
972
973 // Prevent closing when clicking inside sidebar
974 $('.sidebar-on-mobile-single-nav').on('click', function(e){
975 e.stopPropagation();
976 });
977 }
978
979 jQuery(document).ready(function($){
980 var $allSidebars = $('.sidebar-on-mobile-mega-nav');
981 var $body = $('body');
982
983 if(!$allSidebars.length) return;
984
985 /* ========== Inject submenu icon ========== */
986 $allSidebars.find('li.menu-item-has-children').each(function(){
987 if(!$(this).children('.submenu-parent-icon').length){
988 var svgIcon = '<span class="submenu-parent-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M201.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L224 338.7 54.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"/></svg></span>';
989 $(this).prepend(svgIcon);
990 }
991 });
992
993 // Open sidebar — match hamburger to its own sidebar via data attributes
994 $(document).on('click', '.eel-mega-menu-icon-mobile', function(e){
995 e.preventDefault();
996 e.stopPropagation();
997 var targetId = $(this).attr('data-mega-target');
998 var $sidebar;
999 if (targetId) {
1000 $sidebar = $('.sidebar-on-mobile-mega-nav[data-mega-id="' + targetId + '"]');
1001 }
1002 // Fallback for old markup without data attributes
1003 if (!$sidebar || !$sidebar.length) {
1004 $sidebar = $allSidebars.first();
1005 }
1006 $allSidebars.not($sidebar).removeClass('easyel-open');
1007 $sidebar.addClass('easyel-open');
1008 $body.addClass('sidebar-on-mobile-mega-nav-active');
1009 });
1010
1011 // Close sidebar (icon)
1012 $(document).on('click', '.eel-mega-menu-icon-close', function(e){
1013 e.preventDefault();
1014 e.stopPropagation();
1015 $(this).closest('.sidebar-on-mobile-mega-nav').removeClass('easyel-open');
1016 $body.removeClass('sidebar-on-mobile-mega-nav-active');
1017 });
1018
1019 // Click outside sidebar (overlay)
1020 $(document).on('click', function(){
1021 $allSidebars.removeClass('easyel-open');
1022 $body.removeClass('sidebar-on-mobile-mega-nav-active');
1023 });
1024
1025 // Prevent sidebar clicks from closing
1026 $(document).on('click', '.sidebar-on-mobile-mega-nav', function(e){
1027 if (!$(e.target).closest('.eel-mega-menu-icon-close').length) {
1028 e.stopPropagation();
1029 }
1030 });
1031
1032 /* ========== Submenu toggle ========== */
1033 $(document).on('click', '.sidebar-on-mobile-mega-nav .submenu-parent-icon', function(e){
1034 e.preventDefault();
1035 e.stopPropagation();
1036
1037 var $li = $(this).closest('li.menu-item-has-children');
1038 var $submenu = $li.children('ul.sub-menu');
1039
1040 // close other submenus
1041 $li.siblings('li.menu-item-has-children')
1042 .removeClass('eel-sub-open')
1043 .children('ul.sub-menu').slideUp(300);
1044
1045 $li.siblings('li.menu-item-has-children')
1046 .children('.submenu-parent-icon').removeClass('active');
1047
1048 // toggle current
1049 $submenu.slideToggle(300);
1050 $li.toggleClass('eel-sub-open');
1051 $(this).toggleClass('active');
1052 });
1053 });
1054
1055 jQuery(window).on('elementor/frontend/init', function() {
1056 elementorFrontend.hooks.addAction('frontend/element_ready/eel-vertical-navigation.default', function($scope) {
1057
1058 var $sidebar = $scope.find('.sidebar-on-mobile-vertical-nav');
1059 var $body = jQuery('body');
1060
1061 if($sidebar.length) {
1062 $sidebar.find('li.menu-item-has-children').each(function(){
1063 if(!jQuery(this).children('.submenu-parent-icon').length){
1064 var svgIcon = '<span class="submenu-parent-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M201.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L224 338.7 54.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"/></svg></span>';
1065 jQuery(this).prepend(svgIcon);
1066 }
1067 });
1068
1069 $scope.on('click', '.eel-vertical-menu-icon-mobile', function(e){
1070 e.preventDefault();
1071 e.stopPropagation();
1072 $sidebar.addClass('easyel-open');
1073 $body.addClass('sidebar-on-mobile-vertical-nav-active');
1074 });
1075
1076 $scope.on('click', '.eel-vertical-menu-icon-close', function(e){
1077 e.preventDefault();
1078 e.stopPropagation();
1079 closeSidebar();
1080 });
1081
1082 jQuery(document).on('click', function(e){
1083 if (!jQuery(e.target).closest('.sidebar-on-mobile-vertical-nav, .eel-vertical-menu-icon-mobile').length) {
1084 closeSidebar();
1085 }
1086 });
1087
1088 function closeSidebar() {
1089 $sidebar.removeClass('easyel-open');
1090 $body.removeClass('sidebar-on-mobile-vertical-nav-active');
1091 }
1092
1093 /* ========== Submenu Toggle (Accordion) ========== */
1094 $sidebar.find('.submenu-parent-icon').off('click').on('click', function(e){
1095 e.preventDefault();
1096 e.stopPropagation();
1097
1098 var $this = jQuery(this);
1099 var $li = $this.closest('li.menu-item-has-children');
1100 var $submenu = $li.children('ul.sub-menu');
1101 $li.siblings('li.menu-item-has-children').each(function(){
1102 var $sibling = jQuery(this);
1103 $sibling.removeClass('eel-sub-open is-open-vertical');
1104 $sibling.children('ul.sub-menu').slideUp(300);
1105 $sibling.children('.submenu-parent-icon').removeClass('active');
1106 $sibling.find('> a').removeClass('eel-vertical-open');
1107 });
1108 if ($li.hasClass('eel-sub-open')) {
1109 $submenu.slideUp(300);
1110 $li.removeClass('eel-sub-open is-open-vertical');
1111 $this.removeClass('active');
1112 $li.find('> a').removeClass('eel-vertical-open');
1113 } else {
1114 $submenu.slideDown(300);
1115 $li.addClass('eel-sub-open is-open-vertical');
1116 $this.addClass('active');
1117 $li.find('> a').addClass('eel-vertical-open');
1118 }
1119 });
1120 }
1121 });
1122 });
1123
1124 })(jQuery);
1125
1126 (function ($) {
1127 function eelMegaNavigationInit() {
1128 $('.elementor-widget-eel-mega-navigation')
1129 .parent()
1130 .addClass('parant-eel-mega-static');
1131 }
1132 // Frontend
1133 $(window).on('load', function () {
1134 eelMegaNavigationInit();
1135 });
1136
1137 // Elementor Editor
1138 $(window).on('elementor/frontend/init', function () {
1139 if (elementorFrontend.isEditMode()) {
1140 eelMegaNavigationInit();
1141 }
1142 elementorFrontend.hooks.addAction(
1143 'frontend/element_ready/eel-mega-navigation.default',
1144 eelMegaNavigationInit
1145 );
1146 });
1147
1148 })(jQuery);
1149
1150
1151
1152 (function($){
1153
1154 let eelObserverAttached = false;
1155
1156 function updateMegaMenuActive() {
1157 const $mega = $('.easyel--elementor-template-mega-menu');
1158 if (!$mega.length) return;
1159
1160 const $parent = $mega.parent();
1161
1162 if ($mega.hasClass('easyel-mega--current')) {
1163 $parent.addClass('easyel-mega--current-active menu-item-has-children');
1164 } else {
1165 $parent.removeClass('easyel-mega--current-active menu-item-has-children');
1166 }
1167 }
1168
1169 function updateElementorPositions() {
1170 if ($('.easyel-mega--current-active').length) {
1171 $('header .elementor-element').css('position', 'static');
1172 $('.easyel-mega--current-active .elementor-element').css('position', '');
1173 } else {
1174 $('header .elementor-element').css('position', '');
1175 }
1176 }
1177
1178 function attachObserverOnce() {
1179 if (eelObserverAttached) return;
1180 eelObserverAttached = true;
1181
1182 const observer = new MutationObserver(() => {
1183 updateMegaMenuActive();
1184 updateElementorPositions();
1185 });
1186
1187 observer.observe(document.body, {
1188 attributes: true,
1189 subtree: true,
1190 attributeFilter: ['class']
1191 });
1192 }
1193
1194 function initMegaMenuJS(scope) {
1195 if (!scope.find('.elementor-widget-eel-navigation-menu').length) return;
1196
1197 updateMegaMenuActive();
1198 updateElementorPositions();
1199 attachObserverOnce();
1200 }
1201
1202 // Frontend
1203 $(window).on('load', function(){
1204 initMegaMenuJS($(document.body));
1205 });
1206
1207 // Elementor Editor
1208 $(window).on('elementor/frontend/init', function () {
1209 elementorFrontend.hooks.addAction(
1210 'frontend/element_ready/eel-navigation-menu.default',
1211 function($scope){
1212 initMegaMenuJS($scope);
1213 }
1214 );
1215 });
1216
1217
1218 // Image Comparison
1219 var eelImageBeforeAfter = function ($scope, $) {
1220 var $container = $scope.find(".eel_comparison-container");
1221 if (!$container.length) {
1222 return;
1223 }
1224 $container.each(function () {
1225 var $this = $(this);
1226 var offset = $this.data("offset") || 0.5;
1227 var orientation = $this.data("orientation") || "horizontal";
1228 var before_text = $this.data("before_label") || "Before";
1229 var after_text = $this.data("after_label") || "After";
1230 var initeel_comparison = function() {
1231 if ($.isFunction($.fn.eel_comparison)) {
1232 $this.eel_comparison({
1233 default_offset_pct: offset,
1234 orientation: orientation,
1235 before_label: before_text,
1236 after_label: after_text,
1237 no_overlay: false,
1238 move_slider_on_hover: false,
1239 click_to_move: true
1240 });
1241 }
1242 };
1243 if (document.readyState === 'complete') {
1244 initeel_comparison();
1245 } else {
1246 $(window).on('load', initeel_comparison);
1247 }
1248 });
1249 };
1250 $(window).on('elementor/frontend/init', function () {
1251 elementorFrontend.hooks.addAction('frontend/element_ready/eel-image-before-after.default', eelImageBeforeAfter);
1252 });
1253
1254
1255 jQuery(window).on('elementor/frontend/init', function() {
1256 elementorFrontend.hooks.addAction('frontend/element_ready/eel-vertical-navigation.default', function($scope) {
1257 const menuContainer = $scope.find('.eel-vertical-verticalmenu.eel_click');
1258 if (menuContainer.length) {
1259 const icons = $scope.find('.eel-vertical-menu-area .submenu-parent-icon');
1260
1261 icons.on('click', function(e) {
1262 e.preventDefault();
1263 e.stopPropagation();
1264
1265 const $this = jQuery(this);
1266 const parentLi = $this.closest('li');
1267 const subMenu = parentLi.find('> .sub-menu.eel_click');
1268 const parentLink = $this.closest('a');
1269
1270 if (subMenu.length === 0) return;
1271
1272 if (parentLi.hasClass('is-open-vertical')) {
1273 subMenu.css('max-height', subMenu[0].scrollHeight + 'px');
1274 setTimeout(() => {
1275 subMenu.css({
1276 'max-height': '0',
1277 'opacity': '0'
1278 });
1279 }, 10);
1280
1281 parentLi.removeClass('is-open-vertical');
1282 parentLink.removeClass('eel-vertical-open');
1283 } else {
1284 const siblings = parentLi.siblings('.is-open-vertical');
1285 siblings.each(function() {
1286 const siblingSub = jQuery(this).find('> .sub-menu.eel_click');
1287 siblingSub.css({
1288 'max-height': '0',
1289 'opacity': '0'
1290 });
1291 jQuery(this).removeClass('is-open-vertical');
1292 jQuery(this).find('> a').removeClass('eel-vertical-open');
1293 });
1294
1295 parentLi.addClass('is-open-vertical');
1296 parentLink.addClass('eel-vertical-open');
1297 subMenu.css({
1298 'opacity': '1',
1299 'max-height': subMenu[0].scrollHeight + 'px'
1300 });
1301
1302 subMenu.one('transitionend', function() {
1303 if (parentLi.hasClass('is-open-vertical')) {
1304 subMenu.css('max-height', 'none');
1305 }
1306 });
1307 }
1308 });
1309 }
1310 });
1311 });
1312
1313 })(jQuery);
1314
1315
1316
1317