PluginProbe
Easy Elements for Elementor – Addons & Website Templates / 1.3.2
Easy Elements for Elementor – Addons & Website Templates v1.3.2
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.3.2, at assets/js/custom.js

1,006 lines 34.1 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 speed = parseInt($slider.data('speed'), 10) || 600;
13 var centeredSlides = $slider.data('centered-slides') === true || $slider.data('centered-slides') === 'true';
14 var freeMode = $slider.data('free-mode') === true || $slider.data('free-mode') === 'true';
15 var spaceBetween = parseInt($slider.data('space-between'), 10) || 0;
16 var slidesPerViewTablet = parseInt($slider.data('slides-per-view-tablet'), 10) || 2;
17 var slidesPerViewMobile = parseInt($slider.data('slides-per-view-mobile'), 10) || 1;
18 var paginationType = $slider.data('pagination') !== 'none' ? $slider.data('pagination') : false;
19 var navigation = $slider.data('navigation') === true || $slider.data('navigation') === 'true';
20 var effect = $slider.data('effect') || 'slide';
21 var cubeShadow = $slider.data('cube-shadow') === true || $slider.data('cube-shadow') === 'true';
22 var coverflowRotate = parseInt($slider.data('coverflow-rotate'), 10) || 50;
23 var coverflowStretch = parseInt($slider.data('coverflow-stretch'), 10) || 0;
24
25 // Find navigation elements within the scope
26 var $nextButton = $scope.find('.swiper-next');
27 var $prevButton = $scope.find('.swiper-prev');
28 var $pagination = $scope.find('.swiper-pagination');
29
30 // Prevent multiple instances and duplicated handlers if Elementor re-runs init
31 if ($slider[0] && $slider[0].swiper) {
32 try { $slider[0].swiper.destroy(true, true); } catch (e) {}
33 }
34 if ($nextButton.length) { $nextButton.off('click.easyStopAuto click.easyFadeNav'); }
35 if ($prevButton.length) { $prevButton.off('click.easyStopAuto click.easyFadeNav'); }
36
37 var hasProgressBar = $pagination.hasClass('swiper-pagination-progressbar');
38
39 var effectOptions = {};
40
41 if (effect === 'cube') {
42 effectOptions = {
43 cubeEffect: {
44 shadow: cubeShadow,
45 slideShadows: cubeShadow,
46 shadowOffset: 20,
47 shadowScale: 0.94,
48 }
49 };
50 } else if (effect === 'coverflow') {
51 effectOptions = {
52 coverflowEffect: {
53 rotate: coverflowRotate,
54 stretch: coverflowStretch,
55 depth: 100,
56 modifier: 1,
57 slideShadows: true,
58 }
59 };
60 } else if (effect === 'flip') {
61 effectOptions = {
62 flipEffect: {
63 slideShadows: true,
64 limitRotation: true,
65 }
66 };
67 } else if (effect === 'cards') {
68 effectOptions = {
69 cardsEffect: {
70 slideShadows: true,
71 rotate: true,
72 perSlideOffset: 8,
73 }
74 };
75 } else if (effect === 'creative') {
76 var creativeStyle = $slider.data('creative-style') || 'default';
77 if (creativeStyle === 'zoom') {
78 effectOptions = {
79 creativeEffect: {
80 limitProgress: true,
81 prev: { scale: 1.1, opacity: 0, translate: [0, 0, 0] },
82 next: { scale: 1.6, opacity: 0, translate: [0, 0, 0] },
83 },
84 };
85 } else {
86 effectOptions = {
87 creativeEffect: {
88 limitProgress: true,
89 prev: { shadow: true, translate: [0, 0, -400] },
90 next: { translate: ['100%', 0, 0] },
91 },
92 };
93 }
94 } else if (effect === 'fade') {
95 effectOptions = {
96 fadeEffect: { crossFade: true }
97 };
98 }
99
100 // Loop handling tuned to avoid double-advance on loop edges
101 var totalSlides = $slider.find('.swiper-slide').length;
102 var isFade = effect === 'fade';
103 var visibleSlides = isFade ? 1 : slidesPerView;
104 var canLoop = totalSlides > visibleSlides;
105 var loopConfig = {};
106 if (loop && (canLoop || isFade)) {
107 loopConfig = {
108 loop: true,
109 loopAdditionalSlides: isFade ? totalSlides : 20,
110 loopedSlides: isFade ? totalSlides : Math.max(Math.ceil(visibleSlides), Math.min(totalSlides, 10))
111 };
112 } else {
113 loopConfig = { loop: false, rewind: !!loop };
114 }
115
116 var swiper = new Swiper($slider[0], {
117 ...loopConfig,
118 speed: speed,
119 effect: effect,
120 normalizeSlideIndex: true,
121 preventInteractionOnTransition: true,
122 simulateTouch: true,
123 touchRatio: 1,
124 grabCursor: true,
125 mousewheel: hasProgressBar ? {
126 enabled: true,
127 sensitivity: 1,
128 thresholdDelta: 50,
129 thresholdTime: 200,
130 releaseOnEdges: true,
131 invert: false,
132 forceToAxis: false,
133 eventsTarget: 'container'
134 } : false,
135 autoplay: autoplay ? {
136 delay: autoplayDelay,
137 disableOnInteraction: isFade ? true : false,
138 } : false,
139 slidesPerView: visibleSlides,
140 spaceBetween: isFade ? 0 : spaceBetween,
141 centeredSlides: isFade ? false : centeredSlides,
142 freeMode: freeMode,
143 pagination: paginationType && $pagination.length ? {
144 el: $pagination[0],
145 clickable: true,
146 type: paginationType,
147 renderBullet: function (index, className) {
148 let num = (index + 1).toString().padStart(2, '0');
149 return `<span class="${className}">${num}</span>`;
150 }
151 } : false,
152
153 // Progress bar for mouse scroll - only when swiper-pagination-progressbar class is present
154 progressbar: hasProgressBar ? {
155 el: '.swiper-progressbar',
156 type: 'progressbar',
157 } : false,
158
159 navigation: (!isFade) && navigation && ($nextButton.length || $prevButton.length) ? {
160 nextEl: $nextButton.length ? $nextButton[0] : null,
161 prevEl: $prevButton.length ? $prevButton[0] : null,
162 } : false,
163 breakpoints: {
164 0: {
165 slidesPerView: isFade ? 1 : slidesPerViewMobile,
166 },
167 768: {
168 slidesPerView: isFade ? 1 : slidesPerViewTablet,
169 },
170 1024: {
171 slidesPerView: isFade ? 1 : slidesPerView,
172 },
173 },
174 ...effectOptions,
175 });
176
177 // Fade: attach custom navigation so Next/Prev work
178 if (isFade && ($nextButton.length || $prevButton.length)) {
179 var isTransitioning = false;
180 swiper.on('transitionStart', function () { isTransitioning = true; });
181 swiper.on('transitionEnd', function () { isTransitioning = false; });
182 if ($nextButton.length) {
183 $nextButton.off('click.easyFadeNav').on('click.easyFadeNav', function (e) {
184 e.preventDefault();
185 e.stopPropagation();
186 if (swiper && swiper.autoplay && swiper.autoplay.stop) { swiper.autoplay.stop(); }
187 if (isTransitioning || (swiper && swiper.animating)) return;
188 swiper.slideNext();
189 });
190 }
191 if ($prevButton.length) {
192 $prevButton.off('click.easyFadeNav').on('click.easyFadeNav', function (e) {
193 e.preventDefault();
194 e.stopPropagation();
195 if (swiper && swiper.autoplay && swiper.autoplay.stop) { swiper.autoplay.stop(); }
196 if (isTransitioning || (swiper && swiper.animating)) return;
197 swiper.slidePrev();
198 });
199 }
200 }
201
202 // Non-fade: if loop+autoplay, stop autoplay on manual nav to avoid double-advance
203 if (!isFade && loop && autoplay) {
204 if ($nextButton.length) {
205 $nextButton.on('click.easyStopAuto', function () {
206 if (swiper && swiper.autoplay && swiper.autoplay.stop) {
207 swiper.autoplay.stop();
208 }
209 });
210 }
211 if ($prevButton.length) {
212 $prevButton.on('click.easyStopAuto', function () {
213 if (swiper && swiper.autoplay && swiper.autoplay.stop) {
214 swiper.autoplay.stop();
215 }
216 });
217 }
218 }
219 }
220
221
222 if (!window.EEL_AddWidgetTypeBodyClass) {
223 window.EEL_AddWidgetTypeBodyClass = true;
224
225 function addWidgetTypeToBody($scope) {
226 if (!$scope || !$scope.length) return;
227
228 var widgetType = $scope.data('widget_type');
229 if (!widgetType) return;
230
231 var safeWidgetType = widgetType
232 .toString()
233 .replace(/[^a-z0-9_-]/gi, '-')
234 .toLowerCase();
235
236 var className = 'easy-eel-has-widget-' + safeWidgetType;
237
238 if (!document.body.classList.contains(className)) {
239 document.body.classList.add(className);
240 }
241 }
242
243 /* -------------------------------------------------
244 * Elementor Hooks
245 * ------------------------------------------------- */
246 $(window).on('elementor/frontend/init', function () {
247
248 // Slider init
249 elementorFrontend.hooks.addAction(
250 'frontend/element_ready/global',
251 initBackPostSlider
252 );
253
254 // Widget type → body class
255 elementorFrontend.hooks.addAction(
256 'frontend/element_ready/global',
257 function ($scope) {
258 addWidgetTypeToBody($scope);
259 }
260 );
261
262 });
263 }
264
265 $(window).on('elementor/frontend/init', function () {
266
267 function checkStickySection() {
268 const stickySection = document.querySelector('.eel-sticky-section');
269 if (stickySection) {
270 document.body.classList.add('sticky-enabled-overlap');
271 } else {
272 document.body.classList.remove('sticky-enabled-overlap');
273 }
274 }
275
276 checkStickySection();
277
278 const observer = new MutationObserver(checkStickySection);
279 observer.observe(document.body, { childList: true, subtree: true });
280 });
281
282
283
284 $(window).on('elementor/frontend/init', function () {
285 function initReveal_ad($scope) {
286 const revealElements = $scope.find(".eel-img-advance-wrap");
287 const revealAnimate = (el) => {
288 const target = el.querySelector(".eel-advance-img-main");
289 if(target && !target.classList.contains("el-reveal-animate")) {
290 target.classList.add("el-reveal-animate");
291 }
292 };
293 const isEditor = window.elementorFrontend && elementorFrontend.isEditMode && elementorFrontend.isEditMode();
294 if(isEditor){
295 revealElements.each(function(){
296 revealAnimate(this);
297 });
298 } else {
299 const observer = new IntersectionObserver((entries, obs) => {
300 entries.forEach(entry => {
301 if(entry.isIntersecting){
302 revealAnimate(entry.target);
303 obs.unobserve(entry.target);
304 }
305 });
306 }, { threshold: 0.3 });
307
308 revealElements.each(function(){
309 observer.observe(this);
310 });
311
312 // Lenis safe
313 if (window.Lenis && typeof window.Lenis.raf === 'function') {
314 window.Lenis.raf(() => {});
315 }
316 }
317 }
318
319 elementorFrontend.hooks.addAction(
320 'frontend/element_ready/eel-advance-image.default',
321 initReveal_ad
322 );
323
324 function initReveal($scope) {
325 const revealElements = $scope.find(".eel-img-reveal-wrap");
326
327 const revealAnimate = (el) => {
328 const target = el.querySelector(".eel-reveal-img-main");
329 if(target && !target.classList.contains("el-reveal-animate")) {
330 target.classList.add("el-reveal-animate");
331 }
332 };
333
334 const isEditor = window.elementorFrontend && elementorFrontend.isEditMode && elementorFrontend.isEditMode();
335
336 if(isEditor){
337 revealElements.each(function(){
338 revealAnimate(this);
339 });
340 } else {
341 const observer = new IntersectionObserver((entries, obs) => {
342 entries.forEach(entry => {
343 if(entry.isIntersecting){
344 revealAnimate(entry.target);
345 obs.unobserve(entry.target);
346 }
347 });
348 }, { threshold: 0.3 });
349
350 revealElements.each(function(){
351 observer.observe(this);
352 });
353
354 // Lenis safe
355 if (window.Lenis && typeof window.Lenis.raf === 'function') {
356 window.Lenis.raf(() => {});
357 }
358 }
359 }
360
361 elementorFrontend.hooks.addAction(
362 'frontend/element_ready/eel-image-reveal.default',
363 initReveal
364 );
365
366 });
367
368
369
370
371 $(window).on("elementor/frontend/init", function ($) {
372 var EasyJarallax = function ($scope, $) {
373 var $wrappers = $scope.hasClass('eele-has-jarallax') ? $scope : $scope.find('.eele-has-jarallax, .eele-inner-jarallax');
374 if (!$wrappers.length) return;
375 $wrappers.each(function () {
376 var $wrapper = $(this);
377 var bg = $wrapper.data('jarallax-bg');
378 var speed = $wrapper.data('jarallax-speed') || 0.5;
379 if (!bg) return;
380 // Check if jarallax div already exists
381 var $jarallaxDiv = $wrapper.find('.jarallax');
382 if ($jarallaxDiv.length === 0) {
383 var jarallaxMarkup = '<div class="jarallax eele-jarallax-bg" data-speed="' + speed + '">\
384 <img class="jarallax-img" src="' + bg + '" alt="">\
385 </div>';
386 $wrapper.append(jarallaxMarkup);
387 $jarallaxDiv = $wrapper.find('.jarallax');
388 }
389
390 // Initialize jarallax
391 $jarallaxDiv.each(function () {
392 jarallax(this, {
393 speed: $(this).data('speed')
394 });
395 });
396 });
397 };
398 elementorFrontend.hooks.addAction("frontend/element_ready/global", EasyJarallax);
399 });
400
401
402
403 jQuery(window).on('elementor/frontend/init', function() {
404
405 elementorFrontend.hooks.addAction('frontend/element_ready/eel-video-popup.default', function($scope, $) {
406
407 const wrapper = $scope.find('.eel-video-popup-wrapper');
408 const overlay = wrapper.find('.eel-video-overlay');
409 // Find video element (try with class first, then without)
410 let videoEl = wrapper.find('video.eel-normal-video');
411 if (!videoEl.length) {
412 videoEl = wrapper.find('video');
413 }
414 // Find iframe element (try with class first, then without)
415 let iframeEl = wrapper.find('iframe.eel-normal-video');
416 if (!iframeEl.length) {
417 iframeEl = wrapper.find('iframe');
418 }
419
420 // Helper function to get YouTube embed URL
421 function getYouTubeEmbedUrl(url) {
422 const regExp = /(?:v=|\/)([0-9A-Za-z_-]{11}).*/;
423 const match = url.match(regExp);
424 if (match && match[1]) {
425 return 'https://www.youtube.com/embed/' + match[1] + '?autoplay=1';
426 }
427 return null;
428 }
429
430 // Helper function to get Vimeo embed URL
431 function getVimeoEmbedUrl(url) {
432 const match = url.match(/(\d+)/);
433 if (match && match[1]) {
434 return 'https://player.vimeo.com/video/' + match[1] + '?autoplay=1';
435 }
436 return null;
437 }
438
439 // Helper function to open lightbox
440 function openLightbox(videoType, videoUrl, popupId, animation) {
441 const lightboxOverlay = $('#' + popupId);
442 const iframeWrapper = lightboxOverlay.find('.eel-video-popup-iframe-wrapper');
443 const lightboxContent = lightboxOverlay.find('.eel-video-popup-content');
444
445 if (!lightboxOverlay.length) return;
446
447 // Prevent duplicate opening if already active
448 if (lightboxOverlay.hasClass('active')) {
449 return;
450 }
451
452 let embedHtml = '';
453
454 if (videoType === 'youtube') {
455 const embedUrl = getYouTubeEmbedUrl(videoUrl);
456 if (embedUrl) {
457 embedHtml = '<iframe src="' + embedUrl + '" frameborder="0" allow="autoplay; fullscreen" allowfullscreen></iframe>';
458 }
459 } else if (videoType === 'vimeo') {
460 const embedUrl = getVimeoEmbedUrl(videoUrl);
461 if (embedUrl) {
462 embedHtml = '<iframe src="' + embedUrl + '" frameborder="0" allow="autoplay; fullscreen" allowfullscreen></iframe>';
463 }
464 } else if (videoType === 'self_hosted') {
465 embedHtml = '<video src="' + videoUrl + '" controls autoplay></video>';
466 }
467
468 if (embedHtml) {
469 iframeWrapper.html(embedHtml);
470
471 // Apply animation if set
472 if (animation && animation.trim() !== '') {
473 // Remove any existing animation classes
474 const currentClasses = lightboxContent.attr('class') || '';
475 const animationClasses = currentClasses.match(/\banimated\s+\S+/g);
476 if (animationClasses) {
477 lightboxContent.removeClass(animationClasses.join(' '));
478 }
479 // Add new animation classes
480 lightboxContent.addClass('animated ' + animation.trim());
481 } else {
482 // Remove animation classes if no animation
483 const currentClasses = lightboxContent.attr('class') || '';
484 const animationClasses = currentClasses.match(/\banimated\s+\S+/g);
485 if (animationClasses) {
486 lightboxContent.removeClass(animationClasses.join(' '));
487 }
488 }
489
490 lightboxOverlay.addClass('active');
491 $('body').css('overflow', 'hidden');
492 }
493 }
494
495 // Close lightbox handler
496 $(document).on('click', '.eel-video-popup-close', function(e) {
497 e.preventDefault();
498 const lightboxOverlay = $(this).closest('.eel-video-popup-overlay');
499 const lightboxContent = lightboxOverlay.find('.eel-video-popup-content');
500 lightboxOverlay.removeClass('active');
501 lightboxContent.removeClass('animated');
502 lightboxOverlay.find('.eel-video-popup-iframe-wrapper').html('');
503 $('body').css('overflow', '');
504 });
505
506 // Close lightbox on overlay click
507 $(document).on('click', '.eel-video-popup-overlay', function(e) {
508 if ($(e.target).hasClass('eel-video-popup-overlay')) {
509 const lightboxContent = $(this).find('.eel-video-popup-content');
510 $(this).removeClass('active');
511 lightboxContent.removeClass('animated');
512 $(this).find('.eel-video-popup-iframe-wrapper').html('');
513 $('body').css('overflow', '');
514 }
515 });
516
517 // Close lightbox on ESC key
518 $(document).on('keydown', function(e) {
519 if (e.keyCode === 27) { // ESC key
520 $('.eel-video-popup-overlay.active').each(function() {
521 const lightboxContent = $(this).find('.eel-video-popup-content');
522 $(this).removeClass('active');
523 lightboxContent.removeClass('animated');
524 $(this).find('.eel-video-popup-iframe-wrapper').html('');
525 $('body').css('overflow', '');
526 });
527 }
528 });
529
530 if (overlay.length) {
531 // Remove existing handler to prevent duplicate
532 overlay.off('click.eelVideoOverlay');
533 overlay.on('click.eelVideoOverlay', function(e) {
534 e.preventDefault();
535 e.stopPropagation();
536
537 const $overlay = $(this);
538 const useLightbox = $overlay.data('lightbox') === 'yes';
539 const videoType = $overlay.data('video-type');
540 const videoUrl = $overlay.data('video-url');
541 const popupId = $overlay.data('popup-id');
542 const animation = $overlay.data('animation') || '';
543
544 // If lightbox is enabled, open lightbox
545 if (useLightbox && videoType && videoUrl && popupId) {
546 // Check if lightbox is already open to prevent duplicate
547 const lightboxOverlay = $('#' + popupId);
548 if (lightboxOverlay.hasClass('active')) {
549 return;
550 }
551 openLightbox(videoType, videoUrl, popupId, animation);
552 return;
553 }
554
555 // Otherwise, play inline (original behavior)
556 // Self-hosted video play
557 if (videoEl.length) {
558 const video = videoEl.get(0);
559
560 // Function to play video
561 const playVideo = function() {
562 const playPromise = video.play();
563 if (playPromise !== undefined) {
564 playPromise.catch(function(error) {
565 console.log('Video play failed:', error);
566 // If play fails due to autoplay policy, try with muted
567 if (error.name === 'NotAllowedError' && !video.muted) {
568 video.muted = true;
569 video.play().then(function() {
570 // Unmute after play starts (if user wants sound)
571 setTimeout(function() {
572 video.muted = false;
573 }, 100);
574 }).catch(function(err) {
575 console.log('Video play with mute also failed:', err);
576 });
577 }
578 });
579 }
580 };
581
582 // Always try to load and play
583 if (video.readyState >= 2) {
584 // Video is already loaded, play immediately
585 playVideo();
586 } else {
587 // Video not loaded, load it first then play
588 video.load();
589 // Wait for video to be ready to play
590 const tryPlay = function() {
591 if (video.readyState >= 2) {
592 playVideo();
593 } else {
594 video.addEventListener('canplay', playVideo, { once: true });
595 video.addEventListener('loadeddata', playVideo, { once: true });
596 }
597 };
598 tryPlay();
599 }
600 }
601
602 // YouTube / Vimeo iframe autoplay
603 if (iframeEl.length) {
604 let src = iframeEl.attr('src');
605 if (src) {
606 // Check if it's YouTube or Vimeo
607 const isYouTube = src.indexOf('youtube.com') !== -1 || src.indexOf('youtu.be') !== -1;
608 const isVimeo = src.indexOf('vimeo.com') !== -1;
609
610 if (isYouTube || isVimeo) {
611 try {
612 // Use URL object for proper parsing
613 const url = new URL(src);
614
615 if (isYouTube) {
616 // YouTube: add autoplay, preserve original mute setting
617 url.searchParams.set('autoplay', '1');
618 // Only set mute=1 if it was already in the URL (preserve original setting)
619 // If mute was not in original URL, don't add it
620 if (!url.searchParams.has('mute')) {
621 // Don't add mute if it wasn't there originally
622 } else {
623 // Keep the original mute value
624 }
625 // Reload iframe with new src
626 iframeEl.attr('src', url.toString());
627 } else if (isVimeo) {
628 // Vimeo: add autoplay (Vimeo doesn't need mute for autoplay)
629 url.searchParams.set('autoplay', '1');
630 // Reload iframe with new src
631 iframeEl.attr('src', url.toString());
632 }
633 } catch (e) {
634 // Fallback for browsers that don't support URL constructor
635 // Remove existing autoplay parameter if present
636 src = src.replace(/[?&]autoplay=[^&]*/g, '');
637
638 if (isYouTube) {
639 // Check if mute parameter exists in original URL
640 const hasMute = /[?&]mute=/.test(src);
641 // Add autoplay=1
642 const separator = src.indexOf('?') === -1 ? '?' : '&';
643 src += separator + 'autoplay=1';
644 // Only add mute if it was already there (preserve original setting)
645 // Don't add mute if it wasn't in original URL
646 } else if (isVimeo) {
647 // Vimeo: just add autoplay=1
648 const separator = src.indexOf('?') === -1 ? '?' : '&';
649 src += separator + 'autoplay=1';
650 }
651
652 // Update src without clearing first
653 iframeEl.attr('src', src);
654 }
655 }
656 }
657 }
658
659 // Remove overlay after a small delay to ensure video starts
660 setTimeout(function() {
661 overlay.fadeOut(300, function() {
662 $(this).remove();
663 });
664 }, 200);
665 });
666 }
667
668 // Handle popup button clicks (for display_type = 'popup')
669 const popupBtn = wrapper.find('.entro-all-video-popup');
670 if (popupBtn.length) {
671 // Remove existing handler to prevent duplicate
672 popupBtn.off('click.eelVideoPopup');
673 popupBtn.on('click.eelVideoPopup', function(e) {
674 e.preventDefault();
675 e.stopPropagation();
676 const $btn = $(this);
677 const videoType = $btn.data('video-type');
678 const videoUrl = $btn.data('video-src');
679 const popupId = $btn.data('popup-id');
680 const lightboxOverlay = $('#' + popupId);
681 const animation = lightboxOverlay.data('animation') || '';
682
683 if (videoType && videoUrl && popupId) {
684 // Check if lightbox is already open to prevent duplicate
685 if (lightboxOverlay.hasClass('active')) {
686 return;
687 }
688 openLightbox(videoType, videoUrl, popupId, animation);
689 }
690 });
691 }
692 });
693
694 });
695
696
697
698
699 // Single Page Navigation
700 document.addEventListener('DOMContentLoaded', function () {
701 const navWrappers = document.querySelectorAll('.eel-single-nav-list');
702 if (!navWrappers.length) return;
703
704 navWrappers.forEach(function (wrapper) {
705
706 const navLinks = wrapper.querySelectorAll('.eel-nav-item a');
707 const navItems = wrapper.querySelectorAll('.eel-nav-item');
708
709 if (!navLinks.length) return;
710
711 /* ========= CLICK → SMOOTH SCROLL ========= */
712 wrapper.addEventListener('click', function (e) {
713
714 const link = e.target.closest('.eel-nav-item a');
715 if (!link || !wrapper.contains(link)) return;
716
717 const href = link.getAttribute('href');
718
719 if (!href || href === '#' || !href.startsWith('#') || href.length === 1) {
720 return;
721 }
722
723 const target = document.querySelector(href);
724 if (!target) return;
725
726 e.preventDefault();
727
728 target.scrollIntoView({
729 behavior: 'smooth',
730 block: 'start'
731 });
732 });
733
734 /* ========= SCROLL → ACTIVE CHANGE ========= */
735 const sectionsMap = new Map();
736
737 navLinks.forEach(link => {
738 const href = link.getAttribute('href');
739
740 if (!href || href === '#' || !href.startsWith('#') || href.length === 1) {
741 return;
742 }
743
744 const section = document.querySelector(href);
745 if (section) {
746 sectionsMap.set(section, href);
747 }
748 });
749
750 function setActive(href) {
751 navItems.forEach(item => item.classList.remove('active'));
752
753 navLinks.forEach(link => {
754 if (link.getAttribute('href') === href) {
755 link.closest('.eel-nav-item')?.classList.add('active');
756 }
757 });
758 }
759
760 const observer = new IntersectionObserver(
761 (entries) => {
762 entries.forEach(entry => {
763 if (entry.isIntersecting) {
764 const id = sectionsMap.get(entry.target);
765 if (id) setActive(id);
766 }
767 });
768 },
769 {
770 root: null,
771 rootMargin: '-40% 0px -40% 0px',
772 threshold: 0.1
773 }
774 );
775
776 sectionsMap.forEach((id, section) => {
777 observer.observe(section);
778 });
779
780 });
781
782 });
783
784 // Sticky Navigation single page
785
786 function applyStickyToContainer($container) {
787 if ($container.find('.elementor-widget-eel-single-nav').length === 0) {
788 return;
789 }
790
791 $container.addClass('eel-single-nav-sticky-enabled');
792 }
793
794 /* ===== Frontend ===== */
795 $(window).on('elementor/frontend/init', function () {
796 elementorFrontend.hooks.addAction(
797 'frontend/element_ready/container',
798 function ($scope) {
799 applyStickyToContainer($scope);
800 }
801 );
802 });
803
804 /* ===== Editor ===== */
805 if (window.elementor && elementor.isEditMode()) {
806 elementor.hooks.addAction('editor:init', function () {
807 elementorFrontend.hooks.addAction(
808 'frontend/element_ready/container',
809 function ($scope) {
810 applyStickyToContainer($scope);
811 }
812 );
813 });
814 }
815
816 // Mobile Menu Sidebar
817 if ($('.sidebar-on-mobile-single-nav').length) {
818 // Sidebar toggle
819 $('.eel-nav-menu-icon').on('click', function(e){
820 e.stopPropagation();
821 $('.sidebar-on-mobile-single-nav').toggleClass('easyel-open');
822 $('body').toggleClass('sidebar-on-mobile-single-nav-active');
823 });
824 // Click outside to close sidebar
825 $(document).on('click', function(){
826 $('.sidebar-on-mobile-single-nav').removeClass('easyel-open');
827 $('body').removeClass('sidebar-on-mobile-single-nav-active');
828 $('.sidebar-on-mobile-single-nav ul.sub-menu').slideUp(300);
829 $('.sidebar-on-mobile-single-nav .sub-arrow').removeClass('active').attr('aria-expanded', false);
830 });
831
832 $('.sidebar-on-mobile-single-nav, .eel-nav-menu').on('click', 'li:not(.menu-item-has-children) a', function(e){
833 $('.sidebar-on-mobile-single-nav li a, .eel-nav-menu li a').removeClass('eel-active');
834 $(this).addClass('eel-active');
835 $('.sidebar-on-mobile-single-nav').removeClass('easyel-open');
836 $('body').removeClass('sidebar-on-mobile-single-nav-active');
837 $('body').removeClass('sidebar-on-mobile-active');
838 });
839
840 // Prevent closing when clicking inside sidebar
841 $('.sidebar-on-mobile-single-nav').on('click', function(e){
842 e.stopPropagation();
843 });
844 }
845
846 jQuery(document).ready(function($){
847 var $sidebar = $('.sidebar-on-mobile-mega-nav');
848 var $body = $('body');
849
850 if(!$sidebar.length) return;
851
852 /* ========== Inject submenu icon ========== */
853 $sidebar.find('li.menu-item-has-children').each(function(){
854 if(!$(this).children('.submenu-parent-icon').length){
855 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>';
856 $(this).prepend(svgIcon);
857 }
858 });
859
860 // Open sidebar
861 $(document).on('click', '.eel-mega-menu-icon-mobile', function(e){
862 e.preventDefault();
863 e.stopPropagation();
864 $sidebar.addClass('easyel-open');
865 $body.addClass('sidebar-on-mobile-mega-nav-active');
866 });
867
868 // Close sidebar (icon)
869 $(document).on('click', '.eel-mega-menu-icon-close', function(e){
870 e.preventDefault();
871 e.stopPropagation();
872 $sidebar.removeClass('easyel-open');
873 $body.removeClass('sidebar-on-mobile-mega-nav-active');
874 });
875
876 // Click outside sidebar (overlay)
877 $(document).on('click', function(){
878 $sidebar.removeClass('easyel-open');
879 $body.removeClass('sidebar-on-mobile-mega-nav-active');
880 });
881
882 // Prevent sidebar clicks from closing
883 $sidebar.on('click', function(e){
884 if (!$(e.target).closest('.eel-mega-menu-icon-close').length) {
885 e.stopPropagation();
886 }
887 });
888
889 /* ========== Submenu toggle ========== */
890 $('.submenu-parent-icon').on('click', function(e){
891 e.preventDefault();
892 e.stopPropagation();
893
894 var $li = $(this).closest('li.menu-item-has-children');
895 var $submenu = $li.children('ul.sub-menu');
896
897 // close other submenus
898 $li.siblings('li.menu-item-has-children')
899 .removeClass('eel-sub-open')
900 .children('ul.sub-menu').slideUp(300);
901
902 $li.siblings('li.menu-item-has-children')
903 .children('.submenu-parent-icon').removeClass('active');
904
905 // toggle current
906 $submenu.slideToggle(300);
907 $li.toggleClass('eel-sub-open');
908 $(this).toggleClass('active');
909 });
910 });
911
912 })(jQuery);
913
914 (function ($) {
915 function eelMegaNavigationInit() {
916 $('.elementor-widget-eel-mega-navigation')
917 .parent()
918 .addClass('parant-eel-mega-static');
919 }
920 // Frontend
921 $(window).on('load', function () {
922 eelMegaNavigationInit();
923 });
924
925 // Elementor Editor
926 $(window).on('elementor/frontend/init', function () {
927 if (elementorFrontend.isEditMode()) {
928 eelMegaNavigationInit();
929 }
930 elementorFrontend.hooks.addAction(
931 'frontend/element_ready/eel-mega-navigation.default',
932 eelMegaNavigationInit
933 );
934 });
935
936 })(jQuery);
937
938
939
940 (function($){
941
942 let eelObserverAttached = false;
943
944 function updateMegaMenuActive() {
945 const $mega = $('.easyel--elementor-template-mega-menu');
946 if (!$mega.length) return;
947
948 const $parent = $mega.parent();
949
950 if ($mega.hasClass('easyel-mega--current')) {
951 $parent.addClass('easyel-mega--current-active menu-item-has-children');
952 } else {
953 $parent.removeClass('easyel-mega--current-active menu-item-has-children');
954 }
955 }
956
957 function updateElementorPositions() {
958 if ($('.easyel-mega--current-active').length) {
959 $('header .elementor-element').css('position', 'static');
960 $('.easyel-mega--current-active .elementor-element').css('position', '');
961 } else {
962 $('header .elementor-element').css('position', '');
963 }
964 }
965
966 function attachObserverOnce() {
967 if (eelObserverAttached) return;
968 eelObserverAttached = true;
969
970 const observer = new MutationObserver(() => {
971 updateMegaMenuActive();
972 updateElementorPositions();
973 });
974
975 observer.observe(document.body, {
976 attributes: true,
977 subtree: true,
978 attributeFilter: ['class']
979 });
980 }
981
982 function initMegaMenuJS(scope) {
983 if (!scope.find('.elementor-widget-eel-navigation-menu').length) return;
984
985 updateMegaMenuActive();
986 updateElementorPositions();
987 attachObserverOnce();
988 }
989
990 // Frontend
991 $(window).on('load', function(){
992 initMegaMenuJS($(document.body));
993 });
994
995 // Elementor Editor
996 $(window).on('elementor/frontend/init', function () {
997 elementorFrontend.hooks.addAction(
998 'frontend/element_ready/eel-navigation-menu.default',
999 function($scope){
1000 initMegaMenuJS($scope);
1001 }
1002 );
1003 });
1004
1005 })(jQuery);
1006