1/** 2 * Often used vanilla js functions, so that we don't need 3 * to use all of underscore/jQuery 4 */ 5(function( undefined ) { 6 "use strict"; 7 8 var v = ( window.VAMTAM = window.VAMTAM || {} ); // Namespace 9 10 // Returns a function, that, as long as it continues to be invoked, will not 11 // be triggered. The function will be called after it stops being called for 12 // N milliseconds. If `immediate` is passed, trigger the function on the 13 // leading edge, instead of the trailing. 14 v.debounce = function( func, wait = 300, immediate = false ) { 15 var timeout; 16 return function() { 17 var context = this, args = arguments; 18 var later = function() { 19 timeout = null; 20 if ( ! immediate ) func.apply( context, args ); 21 }; 22 var callNow = immediate && ! timeout; 23 clearTimeout( timeout ); 24 timeout = setTimeout( later, wait ); 25 if ( callNow ) func.apply( context, args ); 26 }; 27 }; 28 29 // vanilla jQuery.fn.offset() replacement 30 // @see https://plainjs.com/javascript/styles/get-the-position-of-an-element-relative-to-the-document-24/ 31 32 v.offset = function( el ) { 33 var rect = el.getBoundingClientRect(), 34 scrollLeft = window.pageXOffset || document.documentElement.scrollLeft, 35 scrollTop = window.pageYOffset || document.documentElement.scrollTop; 36 return { top: rect.top + scrollTop, left: rect.left + scrollLeft }; 37 }; 38 39 // Faster scroll-based animations 40 41 v.scroll_handlers = []; 42 v.latestKnownScrollY = 0; 43 44 var ticking = false; 45 46 v.addScrollHandler = function( handler ) { 47 requestAnimationFrame( function() { 48 handler.init(); 49 v.scroll_handlers.push( handler ); 50 51 handler.measure( v.latestKnownScrollY ); 52 handler.mutate( v.latestKnownScrollY ); 53 } ); 54 }; 55 56 v.onScroll = function() { 57 v.latestKnownScrollY = window.pageYOffset; 58 59 if ( ! ticking ) { 60 ticking = true; 61 62 requestAnimationFrame( function() { 63 var i; 64 65 for ( i = 0; i < v.scroll_handlers.length; i++ ) { 66 v.scroll_handlers[i].measure( v.latestKnownScrollY ); 67 } 68 69 for ( i = 0; i < v.scroll_handlers.length; i++ ) { 70 v.scroll_handlers[i].mutate( v.latestKnownScrollY ); 71 } 72 73 ticking = false; 74 } ); 75 } 76 }; 77 78 window.addEventListener( 'scroll', v.onScroll, { passive: true } ); 79 80 // Load an async script 81 v.load_script = function( src, callback ) { 82 var s = document.createElement('script'); 83 s.type = 'text/javascript'; 84 s.async = true; 85 s.src = src; 86 87 if ( callback ) { 88 s.onload = callback; 89 } 90 91 document.getElementsByTagName('script')[0].before( s ); 92 }; 93 94 v.load_style = function( href, media, callback, after ) { 95 var l = document.createElement('link'); 96 l.rel = 'stylesheet'; 97 l.type = 'text/css'; 98 l.media = media; 99 l.href = href; 100 101 if ( callback ) { 102 l.onload = callback; 103 } 104 105 if ( after ) { 106 after.after( l ); 107 } else { 108 document.getElementsByTagName('link')[0].before( l ); 109 } 110 }; 111 112 // Checks if current window size is inside the below-max breakpoint range. 113 v.isBelowMaxDeviceWidth = function () { 114 return ! window.matchMedia( '(min-width: ' + VAMTAM_FRONT.max_breakpoint + 'px)' ).matches; 115 }; 116 117 // Checks if current window size is inside the max breakpoint range. 118 v.isMaxDeviceWidth = function () { 119 return window.matchMedia( '(min-width: ' + VAMTAM_FRONT.max_breakpoint + 'px)' ).matches; 120 }; 121 122 v.isMobileBrowser = /Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/.test( navigator.userAgent ) || ( /Macintosh/.test( navigator.userAgent ) && navigator.maxTouchPoints && navigator.maxTouchPoints > 2 ); 123 124 v.getScrollbarWidth = () => window.innerWidth - document.documentElement.clientWidth; 125})(); 126 127(function($, v, undefined) { 128 "use strict"; 129 130 var mainHeader = $('header.main-header'); 131 var header_contents = mainHeader.find( '.header-contents' ); 132 var menu_toggle = document.getElementById( 'vamtam-fallback-main-menu-toggle' ); 133 var original_toggle = document.querySelector( '#main-menu > .mega-menu-wrap > .mega-menu-toggle' ); 134 135 // scrolling below 136 137 var smoothScrollTimer, smoothScrollCallback; 138 139 var smoothScrollListener = function() { 140 clearTimeout( smoothScrollTimer ); 141 142 smoothScrollTimer = setTimeout( scrollToElComplete, 200 ); 143 }; 144 145 var scrollToElComplete = function() { 146 window.removeEventListener( 'scroll', smoothScrollListener, { passive: true } ); 147 v.blockStickyHeaderAnimation = false;
148 149 if ( smoothScrollCallback ) { 150 smoothScrollCallback(); 151 } 152 }; 153 154 var scrollToEl = function( el, duration, callback ) { 155 requestAnimationFrame( function() { 156 var el_offset = el.offset().top; 157 158 v.blockStickyHeaderAnimation = true; 159 160 // measure header height 161 var header_height = 0; 162 header_height = header_contents.height() || 0; 163 164 165 var scroll_position = el_offset - v.adminBarHeight - header_height; 166 167 smoothScrollCallback = callback; 168 169 window.addEventListener( 'scroll', smoothScrollListener, { passive: true } ); 170 171 window.scroll( { left: 0, top: scroll_position, behavior: 'smooth' } ); 172 173 if ( el.attr( 'id' ) ) { 174 if ( history.pushState ) { 175 history.pushState( null, null, '#' + el.attr( 'id' ) ); 176 } else { 177 window.location.hash = el.attr( 'id' ); 178 } 179 } 180 181 menu_toggle && menu_toggle.classList.remove( 'mega-menu-open' ); 182 original_toggle && original_toggle.classList.remove( 'mega-menu-open' ); 183 } ); 184 }; 185 186 $( document.body ).on('click', '.vamtam-animated-page-scroll[href], .vamtam-animated-page-scroll [href], .vamtam-animated-page-scroll [data-href]', function(e) { 187 var href = $( this ).prop( 'href' ) || $( this ).data( 'href' ); 188 var el = $( '#' + ( href ).split( "#" )[1] ); 189 190 var l = document.createElement('a'); 191 l.href = href; 192 193 if(el.length && l.pathname === window.location.pathname) { 194 menu_toggle && menu_toggle.classList.remove( 'mega-menu-open' ); 195 original_toggle && original_toggle.classList.remove( 'mega-menu-open' ); 196 197 scrollToEl( el ); 198 e.preventDefault(); 199 } 200 }); 201 202 if ( window.location.hash !== "" && 203 ( 204 $( '.vamtam-animated-page-scroll[href*="' + window.location.hash + '"]' ).length || 205 $( '.vamtam-animated-page-scroll [href*="' + window.location.hash + '"]').length || 206 $( '.vamtam-animated-page-scroll [data-href*="'+window.location.hash+'"]' ).length 207 ) 208 ) { 209 var el = $( window.location.hash ); 210 211 if ( el.length > 0 ) { 212 $( window ).add( 'html, body, #page' ).scrollTop( 0 ); 213 } 214 215 setTimeout( function() { 216 scrollToEl( el ); 217 }, 400 ); 218 } 219})( jQuery, window.VAMTAM ); 220 221/* jshint multistr:true */ 222(function( $, undefined ) { 223 "use strict"; 224 225 window.VAMTAM = window.VAMTAM || {}; // Namespace 226 227 $(function () { 228 window.VAMTAM.adminBarHeight = document.body.classList.contains( 'admin-bar' ) ? 32 : 0; 229 230 if ( /iPad|iPhone|iPod/.test( navigator.userAgent ) && ! window.MSStream) { 231 requestAnimationFrame( function() { 232 document.documentElement.classList.add( 'ios-safari' ); 233 } ); 234 } 235 236 if ( navigator.userAgent.includes( 'Safari' ) && ! navigator.userAgent.includes( 'Chrome' ) ) { 237 requestAnimationFrame( function() { 238 document.documentElement.classList.add( 'safari' ); 239 } ); 240 } 241 242 if ( navigator.userAgent.includes( 'Safari' ) && ! navigator.userAgent.includes( 'Chrome' ) ) { 243 requestAnimationFrame( function() { 244 document.documentElement.classList.add( 'safari' ); 245 } ); 246 } 247 248 // prevent hover when scrolling 249 (function() { 250 var wrapper = document.body, 251 timer; 252 253 window.addEventListener( 'scroll', function() { 254 clearTimeout(timer); 255 256 requestAnimationFrame( function() { 257 wrapper.classList.add( 'disable-hover' ); 258 259 timer = setTimeout( function() { 260 wrapper.classList.remove( 'disable-hover' ); 261 }, 300 ); 262 } ); 263 }, { passive:true } ); 264 })(); 265 266 // print trigger 267 268 document.addEventListener( 'click', function( e ) { 269 if ( e.target.closest( '.vamtam-trigger-print' ) ) { 270 window.print(); 271 e.preventDefault(); 272 } 273 } ); 274 275 // Code which depends on the window width 276 // ===================================================================== 277 278 window.VAMTAM.resizeElements = function() { 279 // video size 280 $('#page .media-inner,\ 281 .wp-block-embed-vimeo:not(.wp-has-aspect-ratio),\ 282 :not(.wp-block-embed__wrapper) > .vamtam-video-frame').find('iframe, object, embed, video').each(function() { 283 284 setTimeout( function() { 285 requestAnimationFrame( function() { 286 var v_width = this.offsetWidth; 287 288 this.style.width = '100%'; 289 290 if ( this.width === '0' && this.height === '0' ) { 291 this.style.height = ( v_width * 9/16 ) + 'px'; 292 } else { 293 this.style.height = ( this.height * v_width / this.width ) + 'px'; 294 } 295 296 $( this ).trigger('vamtam-video-resized'); 297 }.bind( this ) ); 298 }.bind( this ), 50 ); 299 }); 300 301 setTimeout( function() { 302 requestAnimationFrame( function() { 303 $('.mejs-time-rail').css('width', '-=1px'); 304 } ); 305 }, 100 ); 306 }; 307 308 window.addEventListener( 'resize', window.VAMTAM.debounce( window.VAMTAM.resizeElements, 100 ), false ); 309 window.VAMTAM.resizeElements(); 310 } ); 311 312 // Handles various overlay types. 313 /* 314 How it works: 315 -keep track of all overlays (vamtam-overlay-trigger class). 316 -keep track of all overlays state (active or not). 317 -bind the relevant click handlers (custom for each overlay type). 318 -attach a doc click handler which figures out which overlays to close or not. 319 -if we switch breakpoint (below-max / max) we close all overlays to avoid conflicts. 320 321 *Every overlay type will be a bit different so it will prob need 322 some custom coding (defining the overlay's targets, close triggers). 323 */ 324 var vamtamOverlaysHandler = function () { 325 var elsThatCauseOverlay = document.querySelectorAll( '.vamtam-overlay-trigger' ); 326 var elsThatCauseOverlayArr = []; 327 var prevIsBelowMax = window.VAMTAM.isBelowMaxDeviceWidth(); 328 329 var triggerCloseHandlers = function () { 330 elsThatCauseOverlayArr.forEach( function( el ) { 331 if ( el.isActive ) { 332 el.closeTrigger.click(); 333 } 334 } ); 335 }; 336 337 var overlaysResizeHandler = function () { 338 var isBelowMax = window.VAMTAM.isBelowMaxDeviceWidth(); 339 if ( prevIsBelowMax !== isBelowMax) { 340 // We changed breakpoint (max/below-max). 341 // Close all overlays. 342 triggerCloseHandlers(); 343 prevIsBelowMax = isBelowMax; 344 } 345 }; 346 347 var overlayCloseHandler = function ( target ) { 348 // Is this an elementor menu overlay? 349 if ( $( target ).hasClass( 'elementor-menu-toggle' ) ) { 350 // Button is a toggle (on/off). 351 target.removeEventListener( 'click', onOverlayCloseClick ); 352 target.addEventListener( 'click', onOverlayCloseClick ); 353 return; 354 } 355 // Add other type of overlays here. 356 }; 357 358 var onOverlayCloseClick = function ( e ) { 359 // Is this an elementor menu overlay? 360 var target = e.currentTarget; 361 if ( $( target ).hasClass( 'elementor-menu-toggle' ) ) { 362 var elRow = $( target ).closest( '.elementor-row' ); 363 // Elementor >= v3.0. 364 if ( ! elRow.length ) { 365 // v3.0 removed the .elementor-row element. 366 elRow = $( target ).closest( '.elementor-container' ); 367 } 368 369 if ( elRow.hasClass( 'vamtam-overlay-trigger--overlay' ) ) { 370 // We need to remove the overlay 371 elRow.removeClass( 'vamtam-overlay-trigger--overlay' ); 372 target.removeEventListener( 'click', onOverlayCloseClick ); // cause it's a toggle. 373 elsThatCauseOverlayArr.forEach( function( e ) { 374 if ( e.overlayTarget === target || e.closeTrigger === target ) { 375 e.isActive = false;
376 } 377 }); 378 } 379 } 380 // Add other type of overlays here. 381 382 var activeOverlays = $( '.vamtam-overlay-trigger--overlay .vamtam-overlay-element:visible' ); 383 if ( activeOverlays.length < 2 ) { // If there are other active overlays, don't activate scrollers/stt. 384 // Enable page scroll. 385 $( 'html, body' ).removeClass( 'vamtam-disable-scroll' ); 386 // Show stt. 387 $( '#scroll-to-top' ).removeClass( 'hidden' ); 388 } 389 }; 390 391 var onOverlayTriggerClick = function ( e ) { 392 var target = e.currentTarget; 393 // Is this an elementor menu overlay? 394 if ( $( target ).hasClass( 'elementor-menu-toggle' ) ) { 395 var elRow = $( target ).closest( '.elementor-row' ); 396 // Elementor >= v3.0. 397 if ( ! elRow.length ) { 398 // v3.0 removed the .elementor-row element. 399 elRow = $( target ).closest( '.elementor-container' ); 400 } 401 402 // This is for moving the overlay underneath the main-menu. 403 if ( ! elRow.hasClass( 'vamtam-menu-nav-overlay-inside' ) ) { 404 elRow.addClass( 'vamtam-menu-nav-overlay-inside' ); 405 $( elRow ).find( '.vamtam-overlay-element' ).css( 'top', ( $( elRow )[ 0 ].getBoundingClientRect().top + $( elRow ).height() ) + 'px' ); 406 } 407 408 if ( ! elRow.hasClass( 'vamtam-overlay-trigger--overlay' ) ) { 409 // We need to add the overlay class 410 elRow.addClass( 'vamtam-overlay-trigger--overlay' ); 411 elsThatCauseOverlayArr.forEach( function( e ) { 412 if ( e.overlayTarget === target || e.closeTrigger === target ) { 413 e.isActive = true; 414 } 415 }); 416 } else { 417 // This is a close instruction, let onOverlayCloseClick() handle it. 418 return; 419 } 420 } 421 // Add other type of overlays here. 422 423 // Disable page scroll. 424 $( 'html, body' ).addClass( 'vamtam-disable-scroll' ); 425 // Hide stt 426 $( '#scroll-to-top' ).addClass( 'hidden' ); 427 428 // Register the overlay close handler 429 overlayCloseHandler( target ); 430 }; 431 432 elsThatCauseOverlay.forEach( function ( el ) { 433 // Is this an elementor menu overlay? 434 if ( $( el ).hasClass( 'elementor-widget-nav-menu' ) ) { 435 // Get menu toggle. 436 var menuToggle = $( el ).find( '.elementor-menu-toggle' )[ 0 ]; 437 438 // The click listener should be on menu toggle for nav menus. 439 menuToggle.removeEventListener( 'click', onOverlayTriggerClick ); 440 menuToggle.addEventListener( 'click', onOverlayTriggerClick ); 441 442 elsThatCauseOverlayArr.push( { 443 overlayTarget: el, // The el that holds the vamtam-overlay-trigger class. 444 closeTrigger: menuToggle, // The el that closes the overlay. 445 isActive: false // If the overlay is active or not. 446 } ); 447 448 // Add the overlay el. 449 var elRow = $( el ).closest( '.elementor-row' ); 450 // Elementor >= v3.0. 451 if ( ! elRow.length ) { 452 // v3.0 removed the .elementor-row element. 453 elRow = $( el ).closest( '.elementor-container' ); 454 } 455 $( '<span class="vamtam-overlay-element"></span>' ).appendTo( elRow ); 456 457 return; 458 } 459 // Add other type of overlays here. 460 } ); 461 462 if ( elsThatCauseOverlay.length ) { 463 var docClickHandler = function ( e ) { 464 elsThatCauseOverlayArr.forEach( function( el ) { 465 if ( ! el.isActive ) { 466 return; 467 } 468 // If a click happened, 469 // and there is an active overlay, 470 // and the click target isn't the overlay target or an element inside it, 471 // then call the overlay close handler. 472 if ( e.target !== el.overlayTarget && ! el.overlayTarget.contains( e.target ) ) { 473 el.closeTrigger.click(); 474 } 475 } ); 476 }; 477 478 document.addEventListener( 'click', docClickHandler, true ); // we need capture phase here. 479 window.addEventListener( 'resize', window.VAMTAM.debounce( overlaysResizeHandler, 200 ), false ); 480 } 481 }; 482 483 const addScrollbarWidthCSSProp = () => { 484 jQuery( 'html' ).css( '--vamtam-scrollbar-width', window.VAMTAM.getScrollbarWidth() + 'px' ); 485 }; 486 487 // Low priority scripts are loaded later 488 document.addEventListener('DOMContentLoaded', function () { 489 window.VAMTAM.load_script( VAMTAM_FRONT.jspath + 'low-priority.js' ); 490 491 vamtamOverlaysHandler(); 492 493 addScrollbarWidthCSSProp(); 494 }, { passive: true } ); 495 496})(jQuery); 497 498( function( $, v, undefined ) { 499 'use strict'; 500 501 window.Cookies = window.Cookies || { 502 get: function( name ) { 503 var value = '; ' + document.cookie; 504 var parts = value.split( '; ' + name + '=' ); 505 506 if ( parts.length === 2 ) { 507 return parts.pop().split( ';' ).shift(); 508 } 509 } 510 }; 511 512 var bijouxCustomNumericInputs = function () { 513 var numInputs = document.querySelectorAll( 'input[type="number"]:not([max="1"])' );
514 numInputs.forEach( function( input ) { 515 var $parent = $( input ).parent(); 516 517 $parent.append( '<div class="vamtam-count-wrap"></div>' ); 518 $parent.append( '<span class="vamtam-increment" onclick="this.parentNode.querySelector(\'input[type=number]\').stepUp();this.parentNode.querySelector(\'input[type=number]\').dispatchEvent(new Event(\'change\', {bubbles: true}))">+</span>' ); 519 $parent.append( '<span class="vamtam-decrement" onclick="this.parentNode.querySelector(\'input[type=number]\').stepDown();this.parentNode.querySelector(\'input[type=number]\').dispatchEvent(new Event(\'change\', {bubbles: true}))">-</span>' ); 520 521 var $wrap = $parent.find( '.vamtam-count-wrap' ), 522 $inc = $parent.find( '.vamtam-increment' ), 523 $dec = $parent.find( '.vamtam-decrement' ); 524 525 $( $dec ).appendTo( $wrap ); 526 $( input ).appendTo( $wrap ); 527 $( $inc ).appendTo( $wrap ); 528 } ); 529 }; 530 531 $( function() { 532 var dropdown = $( '.fixed-header-box .cart-dropdown' ), 533 link = $( '.vamtam-cart-dropdown-link' ), 534 count = $( '.products', link ), 535 $elementorCart = $( '.elementor-widget-woocommerce-menu-cart' ), 536 isElementorCart = $elementorCart.length, 537 $itemsCount = isElementorCart && $( $elementorCart ).find( '.vamtam-elementor-menu-cart__header .item-count' ), 538 isCartPage = 'wc_add_to_cart_params' in window && window.wc_add_to_cart_params.is_cart; 539 540 $( document.body ).on( 'applied_coupon_in_checkout', function() { 541 let error = document.querySelector( '.woocommerce-error' ); 542 error && error.scrollIntoView( { block: 'center', 'behavior': 'smooth' } ); 543 } ); 544 545 function fixElementorWcCartConflicts() { 546 if ( isCartPage ) { 547 // Cart page 548 var targets = document.querySelectorAll( '.woocommerce-cart-form__contents' ); 549 targets.forEach( function( target ) { 550 var shouldRemoveClass = ! $( target ).hasClass( 'shop_table' ) && ! $( target ).parent().hasClass( 'vamtam-cart-main' ); 551 if ( shouldRemoveClass ) { 552 // This class is used by WC. https://github.com/woocommerce/woocommerce/blob/master/assets/js/frontend/cart.js#L92 553 // Elementor uses on their menu cart which causes problems. 554 // So, if we are on the cart page and the class is not added by WC or us, remove it. 555 $( target ).removeClass( 'woocommerce-cart-form__contents' ); 556 } 557 }); 558 } 559 } 560 561 window.VAMTAM.bijouxCustomNumericInputs = bijouxCustomNumericInputs; 562 563 var isFrontend = ! ( 'elementorFrontend' in window ) || ! window.elementorFrontend.isEditMode(); 564 565 if ( isFrontend ) { 566 $( document ).ready( function () { 567 if ( document.body.classList.contains( 'elementor-active' ) ) { 568 bijouxCustomNumericInputs(); 569 } 570 } ); 571 } else { 572 $(window).on('elementor/frontend/init', function(){ 573 window.elementor.on('document:loaded', function(){ 574 // The timeout is for ensuring that when the function is run, the frontend DOM has 575 // been created (when on editor). Can't seem to find a proper event for this from Elementor. 576 setTimeout( function() { 577 bijouxCustomNumericInputs(); 578 }, 1000 ); 579 } ); 580 } ); 581 } 582 583 function triggerSideCart() { 584 const toggleCartOpenBtns = $( '#elementor-menu-cart__toggle_button:visible' ); 585 $.each( toggleCartOpenBtns, function ( i, el ) { 586 el.click(); 587 } ); 588 } 589 590 var openCartHandle = function ( e ) { 591 const toggleCartOpenBtns = $( '.elementor-widget-woocommerce-menu-cart .elementor-menu-cart__toggle_button' ); 592 let toggleClicked = false; 593 594 $.each( toggleCartOpenBtns, function ( i, el ) { 595 if ( $( el ).is( e.target ) || $( el ).has( e.target ).length ) { 596 toggleClicked = true; 597 return false; // break. 598 } 599 } ); 600 601 if ( ! toggleClicked ) { 602 /* 603 We only care about clicks on the toggle button. 604 605 We do manual target detection because we cant use the on() method to attach 606 the event cause we need capture phase. 607 */ 608 return; 609 } 610 611 e.preventDefault(); 612 if ( isCartPage ) { 613 // Don't do anything on cart page. 614 e.stopImmediatePropagation(); 615 return false; 616 } else { 617 if ( window.VAMTAM.isMobileBrowser ) { 618 // Redirect to cart page. 619 e.stopImmediatePropagation(); 620 window.location = window.wc_add_to_cart_params.cart_url; 621 return false;
622 } 623 624 // Disable page scroll. 625 $( 'body' ).addClass( 'vamtam-disable-scroll' ); 626 // Hide stt 627 $( '#scroll-to-top' ).addClass( 'hidden' ); 628 // Furthest section from target inside header. 629 var sectionWrapSection = $( e.target ).closest( '.elementor-section-wrap' ).find( 'section.elementor-element' ).first(); 630 // This is so the element tree gets it's own layer. We need this for top to work correctly. 631 sectionWrapSection.css( 'will-change', 'transform' ); 632 // Raise z-index cause sometimes card gets hidden by other elements. 633 sectionWrapSection.css( 'z-index', '1000' ); 634 } 635 }; 636 637 var closeCartHandle = function ( e, cartIsEmpty ) { 638 var targetIsWrapperOrCloseBtn = $( e.target ).is( '.elementor-menu-cart__container, .vamtam-close-cart, .e-close-cart-custom-icon' ); 639 var lastItemRemoved = e === 'no-target' && cartIsEmpty; 640 if ( targetIsWrapperOrCloseBtn || lastItemRemoved ) { 641 // Enable page scroll. 642 $( 'body' ).removeClass( 'vamtam-disable-scroll' ); 643 // Show stt. 644 $( '#scroll-to-top' ).removeClass( 'hidden' ); 645 // Unset z-index 646 $( e.target ).closest( 'section.elementor-element' ).css( 'z-index', '' ); 647 } 648 }; 649 650 // Registers the handlers that toggle the scroll to top. 651 function bindOpenCloseMenuCartHandlers() { 652 // Menu cart open btn (Elementor) 653 const toggleCartOpenBtns = document.querySelectorAll( '.elementor-widget-woocommerce-menu-cart .elementor-menu-cart__toggle_wrapper' ); 654 toggleCartOpenBtns.forEach( function( el ) { 655 el.removeEventListener( 'click', openCartHandle ); 656 el.addEventListener( 'click', openCartHandle, true ); 657 }); 658 659 // Menu cart wrap/close btn (Elementor) 660 const toggleCartCloseBtns = document.querySelectorAll( '.elementor-widget-woocommerce-menu-cart .elementor-menu-cart__container .elementor-menu-cart__close-button, .elementor-widget-woocommerce-menu-cart .elementor-menu-cart__container' ); 661 toggleCartCloseBtns.forEach( function( el ) { 662 el.removeEventListener( 'click', closeCartHandle ); 663 el.addEventListener( 'click', closeCartHandle ); 664 } ); 665 } 666 bindOpenCloseMenuCartHandlers(); 667 668 function moveScrollToTop( reset ) { 669 const $stt = $('#scroll-to-top.vamtam-scroll-to-top'); 670 const $wcMsg = $('.woocommerce-notices-wrapper .woocommerce-message'); 671 if ( $stt.length ) { 672 if ( reset ) { 673 $stt.css( 'bottom', '10px' ); 674 } else { 675 if ( $wcMsg.length ) { 676 $stt.css('bottom', $wcMsg.outerHeight() + 10 + 'px'); 677 } else { 678 $stt.css( 'bottom', '95px' ); 679 } 680 681 } 682 } 683 } 684 685 // "vamtam_elementor_menu_cart_fragments" == "added_to_cart" + "removed_from_cart" + "wc_fragments_refreshed" + "wc_fragments_loaded" 686 // We also add "wc_fragments_loaded" on "vamtam_elementor_menu_cart_fragments" for caching reasons. 687 const menuCartEvents = isElementorCart ? 'vamtam_elementor_menu_cart_fragments wc_fragments_loaded' : 'added_to_cart removed_from_cart wc_fragments_refreshed wc_fragments_loaded'; 688 $( document.body ).on( menuCartEvents, function() { 689 var count_val = parseInt( Cookies.get( 'woocommerce_items_in_cart' ) || 0, 10 ); 690 if ( count_val > 0 ) { 691 if ( isElementorCart ) { 692 $elementorCart.removeClass( 'hidden' ); 693 var itemsInCart = $elementorCart[ 0 ].querySelectorAll( '.cart_item .quantity select, .cart_item .quantity .vamtam-quantity-input' ), 694 getFromTextContent = false, 695 bubbleIconEls = $elementorCart.find( '#elementor-menu-cart__toggle_button .elementor-button-icon-qty' ); 696 697 if ( ! itemsInCart.length ) { 698 itemsInCart = $elementorCart[ 0 ].querySelectorAll( '.cart_item .product-quantity' ); 699 getFromTextContent = true; 700 } 701 702 var total = 0; 703 itemsInCart.forEach( function( item ) { 704 const val = getFromTextContent ? item.textContent : item.value; 705 total += parseInt( val, 10 ); 706 } ); 707 708 $itemsCount.text( '(' + total + ')' ); 709 710 $.each( bubbleIconEls, function( index, el ) { 711 const bubbleIconVal = parseInt( $( el ).attr( 'data-counter' ), 10 ); 712 // Mismatch between products total and cart bubble icon - Can happen due to caching. 713 if ( total !== bubbleIconVal ) { 714 $( el ).attr( 'data-counter', total ); 715 } 716 } ); 717 } else { 718 var count_real = 0; 719 720 var widgetShoppingCart = document.querySelector( '.widget_shopping_cart' ), 721 spans = widgetShoppingCart ? widgetShoppingCart.querySelectorAll( 'li .quantity' ) : []; 722 723 if ( widgetShoppingCart ) { 724 for ( var i = 0; i < spans.length; i++ ) { 725 count_real += parseInt( spans[i].innerHTML.split( '<span' )[0].replace( /[^\d]/g, '' ), 10 ); 726 } 727 728 // sanitize count_real - if it's not a number, then don't show the counter at all 729 count_real = count_real >= 0 ? count_real : ''; 730 731 count.text( count_real );
732 count.removeClass( 'cart-empty' ); 733 dropdown.removeClass( 'hidden' ); 734 } 735 } 736 } else { 737 if ( isElementorCart ) { 738 var hideEmpty = $elementorCart.hasClass( 'elementor-menu-cart--empty-indicator-hide' ); 739 $elementorCart.toggleClass( 'hidden', hideEmpty ); 740 $itemsCount.text( '(0)' ); 741 closeCartHandle( 'no-target', true ); 742 } else { 743 var show_if_empty = dropdown.hasClass( 'show-if-empty' ); 744 745 count.addClass( 'cart-empty' ); 746 count.text( '0' ); 747 748 dropdown.toggleClass( 'hidden', ! show_if_empty ); 749 } 750 } 751 752 document.body.classList.toggle( 'vamtam-wc-cart-empty', count_val === 0 ); 753 754 // Move the scroll to top so it's not on top of checkout message (single product). 755 var isSingleProduct = $('body').hasClass('single-product'); 756 var checkoutMessageExists = isSingleProduct ? $('.woocommerce-notices-wrapper .woocommerce-message').length : false; 757 if ( checkoutMessageExists ) { 758 moveScrollToTop(); 759 } 760 761 bijouxCustomNumericInputs(); 762 bindOpenCloseMenuCartHandlers(); 763 fixElementorWcCartConflicts(); 764 } ); 765 766 function injectWCNotice( notice ) { 767 if ( notice ) { 768 // Append notice. 769 $( '.woocommerce-notices-wrapper').empty().append( notice ); 770 // Remove notice btn handler. 771 var $closeNoticeBtn = $( '.woocommerce-notices-wrapper' ).find( '.vamtam-close-notice-btn' ); 772 773 if ( ! $closeNoticeBtn.length ) { 774 return; 775 } 776 777 $closeNoticeBtn[ 0 ].addEventListener( 'click', function () { 778 var $msg = $( this ).closest( '.woocommerce-message' ); 779 $msg.fadeOut( 'fast' ); 780 moveScrollToTop( true ); 781 setTimeout( function() { 782 $msg.remove(); 783 }, 2000 ); 784 } ); 785 // Remove notice after 10s. 786 setTimeout( function() { 787 var $msg = $closeNoticeBtn.closest( '.woocommerce-message' ); 788 $msg.fadeOut( 'fast' ); 789 setTimeout( function() { 790 $msg.remove(); 791 moveScrollToTop( true ); 792 }, 2000 ); 793 }, 1000 * 10 ); 794 } 795 } 796 797 function moveCheckoutErrorNotices() { 798 const isCheckout = $( document.body ).hasClass( 'woocommerce-checkout' ), 799 $checkout_form = isCheckout && $( 'form.checkout' ), 800 $dest = isCheckout && $( '.woocommerce > .woocommerce-notices-wrapper' ).first(); 801 802 if ( ! isCheckout || ! $checkout_form.length || ! $dest.length ) { 803 return; 804 } 805 806 const onCheckoutError = function () { 807 const noticeGroup = $checkout_form.find( '.woocommerce-NoticeGroup.woocommerce-NoticeGroup-checkout' ); 808 $dest.append( noticeGroup ); 809 }; 810 811 $( document.body ).on( 'checkout_error', onCheckoutError ); 812 } 813 814 // Products with stock management that have a single last unit available. 815 function lastStockQtyFix() { 816 if ( $( 'body' ).hasClass( 'single-product' ) && ! $( '.sold-individually' ).length ) { 817 const $stock = $( '.stock.in-stock' ); 818 const qty = $( 'input[name="quantity"][max="1"][min="1"]' ); 819 if ( $stock.length && qty.length) { 820 $stock.addClass( 'last-one' ); 821 } 822 } 823 } 824 lastStockQtyFix(); 825 826 // Apply coupon (standard cart). 827 $( document ).on( 'click', '.woocommerce-cart button[name="apply_coupon"]:not(".e-apply-coupon"):visible', function( e ) { 828 e.preventDefault(); 829 // This is a proxy btn, trigger the sumbit which is inside the wc-cart-form. 830 const $applyCouponSubmit = $( 'input[type="submit"][name="apply_coupon"]' ); 831 $applyCouponSubmit.trigger( 'click' ); 832 }); 833 834 if ( window.VAMTAM_FRONT.enable_ajax_add_to_cart === 'yes' ) { 835 // Add to cart ajax 836 $( document ).on( 'click', '.single_add_to_cart_button, .products.vamtam-wc.table-layout .add_to_cart_button:not(.product_type_variable)', function( e ) { 837 // Collect product data. 838 var $thisbutton = $( this ), 839 $form = $thisbutton.closest( 'form.cart' ), 840 id = $thisbutton.val(),
841 product_qty = $form.find( 'input[name=quantity]' ).val() || 1, 842 product_id = $form.find( 'input[name=product_id]' ).val() || id, 843 variation_id = $form.find( 'input[name=variation_id]' ).val() || 0, 844 isVariable = variation_id, 845 isBookable = $form.find( 'input[name=add-to-cart].wc-booking-product-id' ).val(), 846 isGrouped = $form.hasClass( 'grouped_form' ), 847 isExternal = $form.parent( '.elementor-product-external').length && $form.attr('method') === 'get', 848 isTableLayout = $thisbutton.closest( '.products.vamtam-wc.table-layout' ).length, 849 products = {}; 850 851 // TODO: Until we find a proper ajax solution for variable products. 852 if ( isVariable ) { 853 const disableThemeHandler = $thisbutton.parents( '.elementor-widget-woocommerce-product-add-to-cart.vamtam-has-disable-theme-ajax-vars' ).length; 854 if ( disableThemeHandler ) { 855 return; 856 } 857 } 858 859 // Don't submit the form. 860 e.preventDefault(); 861 862 // External product. 863 if ( isExternal ) { 864 // Open the external link in a new tab instead. 865 window.open( $form.attr( 'action' ),'_blank' ); 866 return; 867 } 868 869 if ( ! window.wc_add_to_cart_params ) { 870 return; // No ajax_url 871 } 872 873 // Grouped products 874 if ( isGrouped ) {
875 product_id = parseInt( $form.find( 'input[name=add-to-cart]' ).val() ); 876 var $products = $form.find( '[id^="product-"]' ); 877 878 $.each( $products, function( index, product ) { 879 var addToCartBtn = $( product ).find( '.add_to_cart_button' ); 880 var p_id = $( product ).attr( 'id' ).substr( 8 ), // the "product-" part. 881 p_qty; 882 883 if ( addToCartBtn.length ) { 884 p_qty = parseInt( addToCartBtn.attr( 'data-quantity' ) ) || 0; 885 } else { 886 p_qty = parseInt( $( product ).find( 'input.qty' ).val() ) || 0; 887 } 888 889 products[ p_id ] = p_qty; 890 } ); 891 } 892 893 // Table Layout. 894 if ( isTableLayout ) { 895 const $row = $thisbutton.closest( 'tr.vamtam-product' ); 896 if ( $row.length ) { 897 product_qty = $row.find( 'input[name=quantity]' ).val() || 1; 898 product_id = $thisbutton.attr( 'data-product_id' ) || id; 899 } 900 } 901 902 // Format post data. 903 var data = {}; 904 if ( isBookable ) { 905 // Channel bookables through our woocommerce_ajax_add_to_cart so there's 906 // a single endpoint for all "add to cart" actions. 907 const fData = new FormData( $form[ 0 ] ); 908 fData.forEach( function( value, key ){ 909 // We need to generate those fields to pass woocommerce_add_to_cart_validation 910 // since we are not posting the form directly to wc_bookings. 911 if ( key === 'add-to-cart' ) { 912 data.product_id = value; 913 } else { 914 data[ key.replace( 'wc_bookings_field', '' ) ] = value; 915 } 916 917 // if key contains "[]" it should be treated as an array. 918 if ( key.includes( '[]') ) { 919 data[ key ] = fData.getAll( key ); 920 } else { 921 data[ key ] = value; 922 } 923 }); 924 data.is_wc_booking = true; 925 } else if ( isGrouped ) { 926 // Grouped product 927 data = { 928 product_id: product_id, 929 products: products, 930 is_grouped: true, 931 }; 932 } else if ( isVariable ) { 933 // Variable product 934 data = { 935 product_id: product_id, 936 is_variable: true, 937 }; 938 // Send all fields. 939 const fData = new FormData( $form[ 0 ] ); 940 fData.forEach( function( value, key ) { 941 if ( key === 'add-to-cart' ) { 942 // the "add-to-cart: id" pair triggers WC's WC_Form_Handler::add_to_cart_action() 943 // and the product ends up being added twice to the cart. 944 data.product_id = value; 945 } else { 946 // if key contains "[]" it should be treated as an array. 947 if ( key.includes( '[]') ) { 948 data[ key ] = fData.getAll( key ); 949 } else { 950 data[ key ] = value; 951 } 952 } 953 } ); 954 } else { 955 // Simple product 956 data = { 957 product_id: product_id, 958 }; 959 960 // Send all fields. 961 const fData = new FormData( $form[ 0 ] ); 962 fData.forEach( function( value, key ) { 963 if ( key === 'add-to-cart' ) { 964 // the "add-to-cart: id" pair triggers WC's WC_Form_Handler::add_to_cart_action() 965 // and the product ends up being added twice to the cart. 966 if ( ! data.product_id ) { 967 data.product_id = value; 968 } 969 } else { 970 // if key contains "[]" it should be treated as an array. 971 if ( key.includes( '[]') ) { 972 data[ key ] = fData.getAll( key ); 973 } else { 974 data[ key ] = value; 975 } 976 } 977 } ); 978 } 979 980 // Common fields. 981 data.product_sku = ''; 982 data.quantity = product_qty; 983 data.variation_id = variation_id; 984 data.action = 'woocommerce_ajax_add_to_cart'; 985 986 // Triger adding_to_cart event (theme/plugins might wanna use it). 987 $( document.body ).trigger( 'adding_to_cart', [$thisbutton, data] ); 988 989 // Perform Ajax. 990 $.ajax({ 991 type: 'post', 992 url: window.wc_add_to_cart_params.ajax_url, 993 data: data, 994 beforeSend: function () { 995 $thisbutton.removeClass( 'added' ).addClass( 'loading' ); 996 }, 997 complete: function ( response ) { 998 if ( response.error ) { 999 $thisbutton.removeClass( 'loading' ); 1000 } else { 1001 $thisbutton.addClass( 'added' ).removeClass( 'loading' ); 1002 } 1003 }, 1004 success: function ( response ) { 1005 if ( response.error ) { 1006 // Inject wc notice if there's one. 1007 injectWCNotice( response.notice ); 1008 $( document.body ).trigger( 'wc_fragments_refreshed' ); 1009 } else { 1010 // Successful addition 1011 if ( response.redirect_to_cart ) { 1012 // User has enabled redirect to cart on successful addition. 1013 // Redirect to cart page, don't do anything else. 1014 window.location = window.wc_add_to_cart_params.cart_url; 1015 return; 1016 } 1017 1018 if ( isElementorCart ) { 1019 if ( isTableLayout ) { 1020 const shouldTriggerSideCart = ! window.VAMTAM.isMobileBrowser && $thisbutton.parents( '.vamtam-has-adc-triggers-menu-cart[data-widget_type="woocommerce-products.products_table_layout"]' ).length; 1021 if ( shouldTriggerSideCart ) { 1022 setTimeout(() => { 1023 triggerSideCart(); 1024 }, 500); 1025 } 1026 } else { 1027 const shouldTriggerSideCart = ! window.VAMTAM.isMobileBrowser; 1028 if ( shouldTriggerSideCart ) { 1029 setTimeout(() => { 1030 triggerSideCart(); 1031 }, 500); 1032 } 1033 } 1034 } else { 1035 // Inject wc notice if there's one. 1036 injectWCNotice( response.fragments.notice ); 1037 } 1038 $( document.body ).trigger( 'added_to_cart', [response.fragments, response.cart_hash, $thisbutton] ); 1039 } 1040 }, 1041 }); 1042 1043 return false;
1044 }); 1045 } 1046 1047 if ( window.wc_add_to_cart_params ) { 1048 // Ajax delete product in the menu cart. 1049 $( document ).on( 'click', '.mini_cart_item a.remove, .woocommerce-mini-cart .woocommerce-cart-form__cart-item .product-remove > a:not([class])', function ( e ) { 1050 // Don't refresh. 1051 e.preventDefault(); 1052 1053 // Collect product data. 1054 var $thisbutton = $( this ), 1055 product_id = $( this ).attr( 'data-product_id' ), 1056 cart_item_key = $( this ).attr( 'data-cart_item_key' ), 1057 product_container = $( this ).parents('.mini_cart_item, .woocommerce-cart-form__cart-item' ); 1058 1059 // Perform Ajax. 1060 $.ajax({ 1061 type: 'post', 1062 dataType: 'json', 1063 url: window.wc_add_to_cart_params.ajax_url, 1064 data: { 1065 action: 'product_remove', 1066 product_id: product_id, 1067 cart_item_key: cart_item_key 1068 }, 1069 beforeSend: function () { 1070 product_container.css( 'pointer-events', 'none' ).css( 'opacity', '0.5' ); 1071 $( 'body' ).css( 'cursor', 'wait' ); 1072 }, 1073 complete: function () { 1074 $( 'body' ).css( 'cursor', 'default' ); 1075 }, 1076 success: function( response ) { 1077 if ( ! response || ! response.fragments ) { 1078 window.location = $thisbutton.attr( 'href' ); 1079 return; 1080 } 1081 $( document.body ).trigger( 'removed_from_cart', [ response.fragments, response.cart_hash, $thisbutton ] ); 1082 }, 1083 error: function() { 1084 window.location = $thisbutton.attr( 'href' ); 1085 return; 1086 }, 1087 }); 1088 }); 1089 } 1090 1091 // Ajax update product quantity from cart (menu/standard). 1092 const cartQtySelector = '.woocommerce-cart-form__cart-item .vamtam-quantity'; 1093 $( document ).on( 'change', `${cartQtySelector} select, ${cartQtySelector} input`, v.debounce( function ( e ) { 1094 e.preventDefault(); 1095 1096 // Collect data. 1097 var isStandardCard = $( '.woocommerce-cart' ).length, 1098 product_quantity = $( this ).val(), 1099 product_id = $( this ).attr( 'data-product_id' ), 1100 cart_item_key = $( this ).attr( 'data-cart_item_key' ), 1101 product_container = $( this ).parents('.mini_cart_item, .woocommerce-cart-form__cart-item' ); 1102 1103 if ( isStandardCard ) { 1104 var $updateCardBtn = $( 'input[type="submit"][name="update_cart"]' ); 1105 $updateCardBtn.prop( 'disabled', false ); 1106 $updateCardBtn.trigger( 'click' ); 1107 return; 1108 } 1109 1110 if ( ! window.wc_add_to_cart_params ) { 1111 return; // No ajax_url 1112 } 1113 1114 // Perform Ajax. 1115 $.ajax({ 1116 type: 'post', 1117 dataType: 'json', 1118 url: window.wc_add_to_cart_params.ajax_url, 1119 data: { 1120 action : 'update_item_from_cart', 1121 'product_id' : product_id, 1122 'cart_item_key' : cart_item_key, 1123 'product_quantity' : product_quantity, 1124 }, 1125 beforeSend: function () { 1126 product_container.css( 'pointer-events', 'none' ).css( 'opacity', '0.5' ); 1127 $( 'body' ).css( 'cursor', 'wait' ); 1128 }, 1129 complete: function () { 1130 product_container.css( 'pointer-events', 'auto' ).css( 'opacity', '1' ); 1131 $( 'body' ).css( 'cursor', 'default' ); 1132 }, 1133 success: function( response ) { 1134 if ( ! response || ! response.fragments ) { 1135 return; 1136 } 1137 $( document.body ).trigger( 'wc_fragment_refresh' ); 1138 }, 1139 error: function() { 1140 return; 1141 } 1142 }); 1143 } ) ); 1144 1145 window.addEventListener('load',function(){ 1146 moveCheckoutErrorNotices(); 1147 if ( isElementorCart ) { 1148 bindOpenCloseMenuCartHandlers(); 1149 fixElementorWcCartConflicts(); 1150 } 1151 } ); 1152 1153 function onElementorMenuCartFragmentsUpdated() { 1154 /* 1155 They have replaced WC's cart-fragments ajax handling but have not provided any way of reacting to their changes. 1156 We do this ourselves here. 1157 */ 1158 $( document ).ajaxComplete( ( event, xhr, settings ) => { 1159 const response = xhr.responseJSON, 1160 responseOk = response && response.fragments, 1161 actionOK = ( settings.data && settings.data.indexOf( 'action=elementor_menu_cart_fragments' ) !== -1 ); 1162 1163 if ( responseOk && actionOK ) { 1164 $( document.body ).trigger( 'vamtam_elementor_menu_cart_fragments' ); 1165 } 1166 } ); 1167 } 1168 1169 if ( isElementorCart ) { 1170 onElementorMenuCartFragmentsUpdated(); 1171 } 1172 } ); 1173} )( jQuery, window.VAMTAM ); 1174 1175( function( $, undefined ) { 1176 "use strict"; 1177 1178 window.VAMTAM = window.VAMTAM || {}; // Namespace 1179 window.VAMTAM.CUSTOM_ANIMATIONS = {}; 1180 1181 $( function() { 1182 window.VAMTAM.CUSTOM_ANIMATIONS = { 1183 init: function () { 1184 this.VamtamCustomAnimations.init(); 1185 }, 1186 onDomReady: function () { 1187 this.VamtamCustomAnimations.scrollBasedAnims(); 1188 }, 1189 // Handles custom animations. 1190 VamtamCustomAnimations: { 1191 init: function() { 1192 this.registerAnimations(); 1193 this.utils.watchScrollDirection(); 1194 // this.observedAnims(); // Disabled in favor of elementorFrontend.waypoint(). 1195 }, 1196 registerAnimations: function () { 1197 var self = this; 1198 1199 // Register animations here. 1200 var animations = [ 1201 'stickyHeader', // Same name as function. 1202 // Caridad-only. 1203 'cursorAnimation', // Same name as function. 1204 ]; 1205 1206 animations.forEach( function( animation ) { 1207 self[ animation ].apply( self ); 1208 } ); 1209 }, 1210 // A sticky header animation. 1211 stickyHeader: function () { 1212 var $target = $( '.vamtam-sticky-header' ), 1213 topScrollOffsetTrigger = 300, 1214 _self = this; 1215 1216 if ( ! $target.length ) { 1217 return; 1218 } 1219 1220 if ( $target.length > 1 ) { 1221 // There should only be one sticky header. 1222 $target = $target[ 0 ]; 1223 } 1224 1225 ( function () { // IIFE for closure so $target is available in rAF. 1226 var prevAnimState, 1227 isTransparentHeader = $( $target ).hasClass( 'vamtam-sticky-header--trans
1227parent-header' ), 1228 isFrontend = ! window.elementorFrontend.isEditMode(); 1229 1230 // state: fixed, scrolled up (not visible). 1231 var fixedHiddenState = function () { 1232 $( $target ).removeClass( 'vamtam-sticky-header--fixed-shown' ); 1233 if ( ! $( $target ).hasClass( 'vamtam-sticky-header--fixed-hidden' ) ) { 1234 $( $target ).addClass( 'vamtam-sticky-header--fixed-hidden' ); 1235 } 1236 prevAnimState = 'fixedHiddenState'; 1237 }; 1238 1239 // state: fixed, scrolled down (visible). 1240 var fixedShownState = function () { 1241 $( $target ).removeClass( 'vamtam-sticky-header--fixed-hidden' ); 1242 if ( ! $( $target ).hasClass( 'vamtam-sticky-header--fixed-shown' ) ) { 1243 $( $target ).addClass( 'vamtam-sticky-header--fixed-shown' ); 1244 } 1245 prevAnimState = 'fixedShownState'; 1246 }; 1247 1248 // state: no animation. 1249 var noAnimState = function () { 1250 $( $target ).removeClass( 'vamtam-sticky-header--fixed-shown' ); 1251 $( $target ).removeClass( 'vamtam-sticky-header--fixed-hidden' ); 1252 prevAnimState = 'noAnimState'; 1253 }; 1254 1255 const headerShouldAnimate = () => { 1256 // If a link inside the header is being hovered, we don't want to trigger the sticky header. 1257 if ( $( $target ).find( 'a:hover' ).length ) { 1258 return false; 1259 } 1260 // If a mega-menu belonging to the header is open, we don't want to trigger the sticky header. 1261 if ( $( '.vamtam-header-mega-menu:visible' ).length ) { 1262 return false; 1263 } 1264 1265 return true; 1266 }; 1267 1268 // Initial phase 1269 1270 if ( isFrontend ) { 1271 $( $target ).after( '<div class="vamtam-prevent-scroll-jumps"></div>' ); 1272 } 1273 1274 function preventScrollJumps() { 1275 if ( ! isFrontend ) { 1276 return; 1277 } 1278 // Apply a negative margin to prevent content jumps. 1279 var stickyHeight = $( $target ).innerHeight(); 1280 $( $target ).css( 'margin-bottom', '-' + stickyHeight + 'px' ); 1281 $( $target ).next( '.vamtam-prevent-scroll-jumps' ).css( 'padding-top', stickyHeight + 'px' ); 1282 } 1283 1284 /* 1285 Fixes an issue where on webkit the parent container of an icon with no specialized height (parent) 1286 has a bit more space allocated to it creating bigger than necesseary height requirements for the whole block 1287 tha leads to problems like in https://github.com/veniko/estudiar/issues/37 1288 */ 1289 function reduceRedundantParentHeightOfIconForWebkit() { 1290 if ( ! $( 'body' ).hasClass( 'e--ua-webkit' ) ) { 1291 return; 1292 } 1293 1294 // Get all icons in header. 1295 const $icons = $( '.vamtam-sticky-header' ).find( '.elementor-icon-wrapper > .elementor-icon' ); 1296 $.each( $icons, function ( i, icon ) { 1297 const $icon = $( icon ), 1298 iconHeight = $icon.height(), // Pixels. 1299 $iconParent = $icon.parent(), // .elementor-icon-wrapper 1300 iconParentHeight = $icon.parent().height(), // Pixels. 1301 heightDiffWithParent = Math.abs( iconHeight - iconParentHeight ); 1302 1303 if ( heightDiffWithParent > 5 ) { 1304 $iconParent.css( 'height', ( iconParentHeight - 1 ) + 'px' ); // remove 1px from parent. 1305 } 1306 } ); 1307 } 1308 1309 if ( window.VAMTAM.isMaxDeviceWidth() ) { 1310 preventScrollJumps(); 1311 1312 setTimeout(() => { 1313 reduceRedundantParentHeightOfIconForWebkit(); 1314 preventScrollJumps(); 1315 }, 50); 1316 } 1317 1318 // If passed the trigger point it should always be at fixed hidden state. 1319 const initialScrollPosCheck = ( pageLoad = false ) => { 1320 if ( window.pageYOffset >= topScrollOffsetTrigger ) { 1321 fixedHiddenState(); 1322 } else if ( ! pageLoad ) { 1323 // Sometimes the browser's onload scroll comes after the initialScrollPosCheck() so we check on page load jic. Happens mostly when initial scroll pos is after middle of page. 1324 window.addEventListener( 'load', function() { 1325 if ( ! prevAnimState ) { 1326 setTimeout( () => { 1327 initialScrollPosCheck( true ); 1328 }, 10 ); 1329 } 1330 } ); 1331 } 1332 }; 1333 initialScrollPosCheck(); 1334 1335 var scrollTimer = null, lastScrollYPause = window.scrollY, lastDirection; // Used to check if the user has scrolled up far enough to trigger the sticky header. 1336 window.addEventListener( 'scroll', function( e ) { 1337 if ( scrollTimer !== null ) { 1338 clearTimeout( scrollTimer ); 1339 } 1340 1341 // If the user hasn't scrolled for 500ms we use that as the new Y point. 1342 scrollTimer = setTimeout( function() { 1343 lastScrollYPause = window.scrollY; 1344 }, 500 ); 1345 1346 var anim = window.VAMTAM.debounce( function() { 1347 if ( e.target.nodeName === '#document' ) { 1348 1349 if ( ! headerShouldAnimate() ) { 1350 1351 // Don't animate, but go to fixedShown state for the transparent header. 1352 if ( isTransparentHeader ) { 1353 if ( prevAnimState !== 'fixedShownState' ) { 1354 fixedShownState(); 1355 } 1356 } 1357 1358 return; 1359 } 1360 1361 var direction = _self.utils.getScrollDirection(); 1362 1363 if ( lastDirection !== direction ) { 1364 lastScrollYPause = window.scrollY; 1365 } 1366 lastDirection = direction; 1367 1368 const scrollDifference = Math.abs( window.scrollY - lastScrollYPause ); // Pixels. 1369 if ( scrollDifference < 80 && window.scrollY > 80 ) { 1370 return; 1371 } 1372 1373 if ( direction === 'up' ) { 1374 if ( window.pageYOffset >= topScrollOffsetTrigger ) { 1375 if ( prevAnimState !== 'fixedShownState' ) { 1376 fixedShownState(); 1377 } 1378 } else { 1379 if ( prevAnimState !== 'noAnimState' ) { 1380 noAnimState(); 1381 } 1382 } 1383 return; 1384 } 1385 1386 if ( direction === 'down' ) { 1387 if ( window.pageYOffset >= topScrollOffsetTrigger || isTransparentHeader ) { // Transparent header gets hidden right away. 1388 // Safe-guard for times when the opening of the cart can cause a scroll down and hide the menu (also sliding the cart upwards). 1389 var menuCardNotVisible = ! $( $target ).find( '.elementor-menu-cart--shown' ).length; 1390 if ( prevAnimState !== 'fixedHiddenState' && menuCardNotVisible ) { 1391 fixedHiddenState(); 1392 } 1393 } 1394 } 1395 } 1396 }, 200 ); 1397 1398 if ( window.VAMTAM.isMaxDeviceWidth() ) { 1399 preventScrollJumps(); 1400 requestAnimationFrame( anim ); 1401 } else if ( prevAnimState !== 'noAnimState' ) { 1402 noAnimState(); 1403 } 1404 }, { passive: true } ); 1405 } )(); 1406 }, 1407 // Attaches observers to required anims and fires (adds vamtam-animate class & triggers vamtam:animate event) 1408 // them only when they are visible. 1409 observedAnims: function() { 1410 var observeClass = 'vamtam-observe', 1411 animClass = 'vamtam-animate', 1412 loopedAnimClass = 'vamtam-looped', // Whenever the el toggles to visible the anim will fire. 1413 animEls = document.querySelectorAll( '.' + observeClass ); 1414 1415 if ( ! animEls.length ) { 1416 return; 1417 } 1418 1419 var observer; 1420 1421 var cb = function( iOEntries, observer ) {
1422 iOEntries.forEach( function( entry ) { 1423 var isVisible = false, 1424 el = entry.target, 1425 $el = el && $( el ); 1426 1427 if ( entry.isIntersecting ) { 1428 isVisible = true; 1429 } 1430 1431 if ( isVisible ) { 1432 if ( ! $el.hasClass( animClass ) ) { 1433 $el.addClass( animClass ); 1434 $el.trigger('vamtam:animate'); 1435 } 1436 1437 if ( ! $el.hasClass( loopedAnimClass ) ) { 1438 // If not looped, stop observing (anim fires only once). 1439 observer && observer.unobserve && observer.unobserve( el ); 1440 } 1441 } else { 1442 if ( $el.hasClass( animClass ) ) { 1443 $el.removeClass( animClass ); 1444 } 1445 } 1446 } ); 1447 }; 1448 1449 animEls.forEach( function( el ) { 1450 var $el = $( el ); 1451 $el.removeClass( animClass ); 1452 1453 if ( ! observer ) { 1454 observer = new IntersectionObserver( cb ); 1455 } 1456 1457 observer.observe( el ); 1458 } ); 1459 }, 1460 // Scroll-based anims. 1461 scrollBasedAnims: function() { 1462 var scrollAnims = [ 1463 '[data-settings*="growFromLeftScroll"]', 1464 '[data-settings*="growFromRightScroll"]', 1465 ]; 1466 1467 var animEls = document.querySelectorAll( scrollAnims.join( ', ' ) ); 1468 1469 if ( ! animEls.length ) { 1470 return; 1471 } 1472 1473 var observer, entries = {}, _this = this; 1474 1475 var cb = function( iOEntries ) { 1476 iOEntries.forEach( function( entry ) { 1477 var currentScrollY = entry.boundingClientRect.y, 1478 isInViewport = entry.isIntersecting, 1479 observedEl = entry.target, 1480 scrollPercentage = Math.abs( parseFloat( ( entry.intersectionRatio * 100 ).toFixed( 2 ) ) ), 1481 prevScrollPercentage = entries[ observedEl.dataset.vamtam_anim_id ].lastScrollPercentage, 1482 lastScrollY = entries[ observedEl.dataset.vamtam_anim_id ].lastScrollY, 1483 animateEl = entries[ observedEl.dataset.vamtam_anim_id ].animateEl; 1484 1485 var animate = function () { 1486 window.requestAnimationFrame( function() { 1487 animateEl.style.setProperty( '--vamtam-scroll-ratio', scrollPercentage + '%' ); 1488 } ); 1489 }; 1490 1491 if ( isInViewport && lastScrollY !== currentScrollY ) { 1492 if( _this.utils.getScrollDirection() === 'down') { 1493 if ( prevScrollPercentage < scrollPercentage ) { 1494 animate(); 1495 } 1496 } else { 1497 animate(); 1498 } 1499 } 1500 1501 entries[ observedEl.dataset.vamtam_anim_id ].lastScrollY = currentScrollY; 1502 entries[ observedEl.dataset.vamtam_anim_id ].lastScrollPercentage = scrollPercentage; 1503 } ); 1504 }; 1505 1506 var buildThresholdList = function() { 1507 var thresholds = [], 1508 numSteps = 50, 1509 i; 1510 1511 for ( i = 1.0; i <= numSteps; i++ ) { 1512 var ratio = i / numSteps; 1513 thresholds.push( ratio ); 1514 } 1515 1516 thresholds.push( 0 ); 1517 return thresholds; 1518 }; 1519 1520 const thresholds = buildThresholdList(); 1521 1522 animEls.forEach( function( el ) { 1523 if ( ! observer ) { 1524 var options = { 1525 root: null, 1526 rootMargin: "20% 0% 20% 0%", 1527 threshold: thresholds, 1528 }; 1529 observer = new IntersectionObserver( cb, options ); 1530 } 1531 1532 // Init. 1533 el.style.setProperty( '--vamtam-scroll-ratio', '1%' ); 1534 1535 var observeEl; 1536 if ( el.classList.contains( 'elementor-widget' ) || el.classList.contains( 'elementor-column' ) ) { 1537 // For widgets we observe .elementor-widget-wrap 1538 // For columns we observe .elementor-row 1539 observeEl = el.parentElement; 1540 observeEl.setAttribute('data-vamtam_anim_id', el.dataset.id ); 1541 } else { 1542 // Sections. 1543 // Add scroll anim wrapper. 1544 $( el ).before( '<div class="vamtam-scroll-anim-wrap" data-vamtam_anim_id="' + el.dataset.id + '"></div>' ); 1545 var $wrap = $( el ).prev( '.vamtam-scroll-anim-wrap' ); 1546 $( $wrap ).append( el ); 1547 observeEl = $wrap[ 0 ]; 1548 } 1549 1550 entries[el.dataset.id] = { 1551 lastScrollY: '', 1552 lastScrollPercentage: '', 1553 observeEl: observeEl, 1554 animateEl: el, 1555 }; 1556 1557 observer.observe( observeEl ); 1558 } ); 1559 }, 1560 // Caridad-only. 1561 cursorAnimation: function() { 1562 const isEditor = window.elementorFrontend.isEditMode(); 1563 1564 if ( isEditor || 1565 this.utils.isTouchDevice() || 1566 $( 'body' ).filter( '.no-theme-cursor, .no-estudiar-cursor' ).length || 1567 ! ( 'elementorFrontendConfig' in window ) || 1568 ! window.elementorFrontendConfig.kit.vamtam_theme_has_theme_cursor ) 1569 { 1570 return; 1571 } 1572 1573 $( 'body' ) 1574 .append( '<span id="mouseDot" class="mouse-dot"></span>' ) 1575 .addClass( 'has-mouse-dot' ); 1576 1577 var $mouseDot = $( '#mouseDot' ), 1578 mouseX = 0, mouseY = 0, 1579 xp = 0, yp = 0, 1580 timer; 1581 1582 $( document ).on( 'mousemove mouseenter', function( e ) { 1583 clearTimeout(timer); 1584 1585 if ( window.VAMTAM.isBelowMaxDeviceWidth() ) { 1586 return; 1587 } 1588 1589 if ( ! $mouseDot.hasClass( 'moving' ) ) { 1590 $mouseDot.addClass( 'moving' ); 1591 } 1592 1593 mouseX = e.clientX; 1594 mouseY = e.clientY; 1595 1596 timer = setTimeout(function() { 1597 $mouseDot.removeClass( 'moving' ); 1598 }, 500 ); 1599 } ); 1600 1601 // Position circle. 1602 window.requestAnimationFrame( function () { 1603 setInterval( function() { 1604 xp += ( ( mouseX - xp ) / 6 ); 1605 yp += ( ( mouseY - yp ) / 6 ); 1606 $mouseDot.css( { 1607 transform: 'translate(' + xp + 'px, ' + yp + 'px)', 1608 } ); 1609 }, 20 ); 1610 } ); 1611 1612 // Handler for links. 1613 var onMouseOver = function( e ) { 1614 let hoveringOverLink, hoveringOverIframe; 1615 1616 if ( e.target.href ) { 1617 hoveringOverLink = true; 1618 } else if ( e.target.tagName === 'IFRAME' ) { 1619 hoveringOverIframe = true; 1620 } else { 1621 // Check if it's wrapped by a link (common-case). 1622 if ( $( e.target ).parent( 'a[href]' ).length ) { 1623 hoveringOverLink = true; 1624 } 1625 1626 // < -- Custom Cases -- > 1627 // Button. 1628 if ( $( e.target ).closest( 'a.elementor-button-link[href]' ).length ) { 1629 hoveringOverLink = true; 1630 } 1631 } 1632 1633 if ( hoveringOverLink ) { 1634 window.requestAnimationFrame( function() { 1635 $mouseDot.addClass( 'over-link' ); 1636 } ); 1637 } else { 1638 window.requestAnimationFrame( function() { 1639 $mouseDot.removeClass( 'over-link' ); 1640 } ); 1641 } 1642 1643 if ( hoveringOverIframe ) { 1644 window.requestAnimationFrame( function() { 1645 $mouseDot.addClass( 'over-iframe' ); 1646 } ); 1647 } else { 1648 window.requestAnimationFrame( function() { 1649 $mouseDot.removeClass( 'over-iframe' ); 1650 } ); 1651 } 1652 1653 }; 1654 $( document ).on( 'mouseover', window.VAMTAM.debounce( onMouseOver, 100 ) ); 1655 }, 1656 // Common funcs used in custom animations. 1657 utils: { 1658 getAdminBarHeight: function () { 1659 return window.VAMTAM.adminBarHeight; 1660 }, 1661 watchScrollDirection: function () { 1662 var watcher = function () { 1663 this.lastScrollTop = 0; 1664 this.utils = this; 1665 return { 1666 init: function () { 1667 }, 1668 measure: function ( cpos ) { 1669 this.direction = cpos > this.lastScrollTop ? 'down' : 'up'; 1670 }.bind( this ), 1671 mutate: function ( cpos ) { 1672 this.utils.getScrollDirection = function () { 1673 return this.direction; 1674 }; 1675 this.lastScrollTop = cpos <= 0 ? 0 : cpos; // For Mobile or negative scrolling 1676 }.bind( this ), 1677 }; 1678 }.bind( this ); 1679 1680 window.VAMTAM.addScrollHandler( watcher() ); 1681 }, 1682 isTouchDevice: function() { 1683 const prefixes = ' -webkit- -moz- -o- -ms- '.split( ' ' ); 1684 1685 const mq = function( query ) { 1686 return window.matchMedia( query ).matches; 1687 }; 1688 1689 if ( ( 'ontouchstart' in window ) || window.DocumentTouch && document instanceof DocumentTouch ) { // jshint ignore:line 1690 return true; 1691 } 1692 1693 // include the 'heartz' as a way to have a non matching MQ to help terminate the join 1694 // https://git.io/vznFH 1695 var query = [ '(', prefixes.join( 'touch-enabled),(' ), 'heartz', ')' ].join( '' ); 1696
1697 return mq( query ); 1698 }, 1699 } 1700 }, 1701 }; 1702 1703 window.VAMTAM.CUSTOM_ANIMATIONS.init(); 1704 1705 $( window ).ready( function () { 1706 window.VAMTAM.CUSTOM_ANIMATIONS.onDomReady(); 1707 } ); 1708 }); 1709})( jQuery );
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.