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

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