1(function ( $ ) { 2 'use strict'; 3 4 var sober = sober || {}; 5 6 sober.init = function () { 7 this.$window = $( window ); 8 this.$body = $( document.body ); 9 this.$sideMenu = $( '.side-menu' ); 10 11 this.data = soberData || {}; 12 13 this.closeTopbar(); 14 this.menuHover(); 15 this.toggleSideMenu(); 16 this.toggleModal(); 17 this.toggleTabs(); 18 this.focusInputs(); 19 this.productsTabs(); 20 this.sortProducts(); 21 this.productQuantity(); 22 this.productImagesLightbox(); 23 this.stickyProductSummary(); 24 this.sideProductsCarousel(); 25 this.productImageSlider(); 26 this.productImageZoom(); 27 this.stickUpSells(); 28 this.checkBox(); 29 this.formatGallery(); 30 this.backToTop(); 31 this.toggleProductFilter(); 32 this.productQuickView(); 33 this.instanceSearch(); 34 this.fakeScrollbar(); 35 this.setCSSVars(); 36 this.stickyHeader(); 37 this.navAjax(); 38 this.portfolioFilter(); 39 this.productVariationSwatches(); 40 this.productGalleryZoom(); 41 this.productGallerySlider(); 42 this.productFullWidth(); 43 this.productBackground(); 44 this.relatedProductsCarousel(); 45 this.upsellProductsCarousel(); 46 this.singeProductAjaxAddToCart(); 47 this.stickyAddToCart(); 48 this.openCartModalAfterAdd(); 49 this.pageHeaderParallax(); 50 this.responsive(); 51 this.preload(); 52 this.popup(); 53 this.addedToCartNotice(); 54 // this.checkoutTerms(); 55 // this.fixVC(); 56 this.loginModalAuthenticate(); 57 this.lazyLoadImages(); 58 this.closeNotices(); 59 60 this.$body.trigger( 'sober_init', [sober] ); 61 }; 62 63 /** 64 * Check if a node is blocked for processing. 65 * 66 * @param {object} $node 67 * @return {bool} True if the DOM Element is UI Blocked, false if not. 68 */ 69 sober.isBlocked = function ( $node ) { 70 return $node.is( '.processing' ) || $node.parents( '.processing' ).length; 71 }; 72 73 /** 74 * Block a node visually for processing. 75 * 76 * @param {object} $node 77 */ 78 sober.block = function ( $node ) { 79 if ( !sober.isBlocked( $node ) ) { 80 $node.addClass( 'processing' ).block( { 81 message : null, 82 overlayCSS: { 83 background: '#fff', 84 opacity : 0.6 85 } 86 } ); 87 } 88 }; 89 90 /** 91 * Unblock a node after processing is complete. 92 * 93 * @param {object} $node 94 */ 95 sober.unblock = function ( $node ) { 96 $node.removeClass( 'processing' ).unblock(); 97 }; 98 99 /** 100 * Check if an element is in view-port or not 101 * 102 * @param el 103 * @returns {boolean} 104 */ 105 sober.isVisible = function ( el ) { 106 if ( el instanceof jQuery ) { 107 el = el[0]; 108 } 109 110 var rect = el.getBoundingClientRect(); 111 112 return rect.bottom > 0 && 113 rect.right > 0 && 114 rect.left < ( window.innerWidth || document.documentElement.clientWidth ) && 115 rect.top < ( window.innerHeight || document.documentElement.clientHeight ); 116 }; 117 118 /** 119 * Post format gallery with carousel slider 120 */ 121 sober.formatGallery = function () { 122 $( '.entry-gallery' ).addClass( 'owl-carousel' ).owlCarousel( { 123 singleItem: true, 124 items : 1, 125 navSpeed : 800, 126 nav : true, 127 dots : false, 128 autoplay : true, 129 dotsSpeed : 1000, 130 rtl : sober.data.isRTL === "1", 131 navText : ['<svg viewBox="0 0 20 20"><use xlink:href="#left-arrow"></use></svg>', '<svg viewBox="0 0 20 20"><use xlink:href="#right-arrow"></use></svg>'] 132 } ); 133 }; 134 135 /** 136 * Style checkboxes 137 */ 138 sober.checkBox = function () { 139 // Checkbox: Ship to a different address 140 $( '#ship-to-different-address' ).find( '.input-checkbox' ).on( 'change', function () { 141 if ( $( this ).is( ':checked' ) ) { 142 $( this ).parent().find( '.checkbox' ).addClass( 'checked' ); 143 } else { 144 $( this ).parent().find( '.checkbox' ).removeClass( 'checked' ); 145 } 146 } ).trigger( 'change' ); 147 }; 148 149 /** 150 * Load ajax on shop page and blog 151 */ 152 sober.navAjax = function () { 153 // Blog & Portfolio 154 sober.$body.on( 'click', '.ajax-navigation a', function ( e ) { 155 e.preventDefault(); 156 157 var $button = $( this ), 158 $nav = $button.parent(), 159 $main = $( '#main' ), 160 url = $button.attr( 'href' ); 161 162 if ( $nav.hasClass( 'loading' ) ) { 163 return; 164 } 165 166 $nav.addClass( 'loading' ); 167 168 $.get( 169 url, 170 function ( response ) { 171 var $response = $( response ), 172 nextTitle = $response.find( 'title' ).text(); 173 174 if ( $nav.hasClass( 'posts-navigation' ) ) { 175 var $content = $( $response.find( '#main' ).html() ); 176 $content.hide(); 177 178 $main.append( $content ); 179 $content.fadeIn( 1000 ); 180 $nav.remove(); 181 } else if ( $nav.hasClass( 'portfolio-navigation' ) ) { 182 var $items = $response.find( '.portfolio-items .portfolio' ), 183 $link = $response.find( '.ajax-navigation a' ); 184 185 if ( $items.length ) { 186 $items.imagesLoaded( function () { 187 $main.find( '.portfolio-items' ).isotope( 'insert', $items ); 188 } ); 189 $nav.removeClass( 'loading' ); 190 191 if ( $link.length ) { 192 $nav.html( $link ); 193 } else { 194 $nav.fadeOut(); 195 } 196 } 197 } 198 199 window.history.pushState( null, nextTitle, url ); 200 sober.$body.trigger( 'post-load' ); 201 } 202 ); 203 } ); 204 205 // Shop 206 if ( 'ajax' === sober.data.shop_nav_type || 'infinity' === sober.data.shop_nav_type ) { 207 sober.$body.on( 'click', '.woocommerce-pagination .next', function ( e ) { 208 e.preventDefault(); 209 210 var $button = $( this ), 211 $nav = $button.closest( '.woocommerce-pagination' ), 212 url = $button.attr( 'href' ); 213 214 if ( $button.hasClass( 'loading' ) ) { 215 return; 216 } 217 218 $button.addClass( 'loading' ); 219 220 load_products( url, function ( response ) { 221 var $pagination = $( response ).find( '.woocommerce-pagination' ); 222 223 if ( $pagination.length ) { 224 $nav.html( $pagination.html() ); 225 } else { 226 $nav.html( '' ); 227 } 228 } ); 229 } ); 230 } 231 232 // Infinity Shop 233 if ( 'infinity' === sober.data.shop_nav_type ) {
234 var $nav = $( '.woocommerce-pagination' ), 235 $button = $nav.find( '.next' ); 236 237 if ( $button.length ) { 238 // Use this variable to control scroll event handle for better performance 239 var waiting = false, 240 endScrollHandle; 241 242 sober.$window.on( 'scroll', function () { 243 if ( waiting ) { 244 return; 245 } 246 247 $nav = $( '.woocommerce-pagination' ); 248 $button = $( '.next', $nav ); 249 waiting = true; 250 251 // clear previous scheduled endScrollHandle 252 clearTimeout( endScrollHandle ); 253 254 infiniteScoll(); 255 256 setTimeout( function () { 257 waiting = false; 258 }, 100 ); 259 260 // schedule an extra execution of infiniteScoll() after 200ms 261 // in case the scrolling stops in next 100ms 262 endScrollHandle = setTimeout( function () { 263 waiting = false; 264 infiniteScoll(); 265 }, 200 ); 266 } ); 267 } 268 269 var infiniteScoll = function () { 270 // When almost reach to nav and new next button is exists 271 if ( sober.isVisible( $nav ) && $button.length ) { 272 if ( $button.hasClass( 'loading' ) ) { 273 return; 274 } 275 276 $button.addClass( 'loading' ); 277 278 load_products( $button.attr( 'href' ), function ( response ) { 279 var $pagination = $( response ).find( '.woocommerce-pagination' ); 280 281 if ( $pagination.length ) { 282 $nav.html( $pagination.html() ); 283 284 // Re-select because DOM has been changed 285 $button = $nav.find( '.next' ); 286 } else { 287 $nav.html( '' ); 288 $button.length = 0; 289 } 290 } ); 291 } 292 } 293 } 294 295 /** 296 * Private function for ajax loading product 297 * 298 * @param url 299 * @param callback 300 */ 301 function load_products( url, callback ) { 302 // 'ul.products' can be replace by the filter widget, 303 // so we have to query it again every call. 304 var $holder = $( 'ul.products', '#primary' ); 305 306 $.get( 307 url, 308 function ( response ) { 309 var $primary = $( response ).find( '#primary' ), 310 $_products = $primary.find( 'ul.products' ).children(); 311 312 if ( 'isotope' === sober.data.tab_behaviour ) { 313 $_products.imagesLoaded( function() { 314 var i = 0; 315 316 $_products.each( function( index, product ) { 317 setTimeout( function() { 318 $holder.isotope( 'insert', product ); 319 sober.$body.trigger( 'sober_products_loaded', [product] ); 320 }, index * 100 ); 321 322 i++; 323 } ); 324 325 setTimeout( function() { 326 $holder.isotope( 'layout' ); 327 }, i * 100 ); 328 } ); 329 } else { 330 $_products.each( function( index, product ) { 331 $( product ).css( 'animation-delay', index * 100 + 'ms' ); 332 } ); 333 334 $_products.addClass( 'soberFadeInUp soberAnimation' ); 335 $_products.appendTo( $holder ); 336 337 if ( $_products.length ) { 338 sober.$body.trigger( 'sober_products_loaded', [$_products] ); 339 } 340 } 341 342 if ( 'function' === typeof callback ) { 343 callback( response ); 344 } 345 346 window.history.pushState( null, '', url ); 347 } 348 ); 349 } 350 }; 351 352 /** 353 * Close topbar 354 */ 355 sober.closeTopbar = function () { 356 $( '#topbar' ).on( 'click', '.close', function ( e ) { 357 e.preventDefault(); 358 359 var $topbar = $( this ).closest( '#topbar' ); 360 361 if ( sober.data.sticky_header !== 'none' ) { 362 $topbar.css({ position: 'absolute', top: 0, left: 0, width: '100%' }).slideUp(); 363 $( '#masthead' ).animate( {top: 0} ); 364 365 var offsetTop = $topbar.css( 'margin-bottom' ); 366 367 $( '#page' ).css( {paddingTop: offsetTop} ); 368 } else { 369 $topbar.slideUp(); 370 } 371 } ); 372 }; 373 374 /** 375 * Main navigation sub-menu hover 376 */ 377 sober.menuHover = function () { 378 var animations = { 379 none : ['show', 'hide'], 380 fade : ['fadeIn', 'fadeOut'], 381 slide: ['slideDown', 'slideUp'] 382 }; 383 384 var animation = sober.data.menu_animation ? animations[sober.data.menu_animation] : 'fade'; 385 386 $( '.site-navigation li, .topbar-menu li' ).on( 'mouseenter', function () { 387 var $li = $( this ); 388 389 if ( ! $li.hasClass( 'mega-sub-menu' ) ) { 390 $li.addClass( 'active' ).children( '.sub-menu' ).stop( true, true )[animation[0]](); 391 } 392 } ).on( 'mouseleave', function () { 393 var $li = $( this ); 394 395 if ( ! $li.hasClass( 'mega-sub-menu' ) ) { 396 $( this ).removeClass( 'active' ).children( '.sub-menu' ).stop( true, true ).delay( 100 )[animation[1]](); 397 } 398 } ); 399 }; 400 401 /** 402 * Toggle modal 403 */ 404 sober.toggleModal = function () { 405 sober.$body.on( 'click', '[data-toggle="modal"]', function ( e ) { 406 e.preventDefault(); 407 408 var $el = $( this ), 409 $target = $( '#' + $el.data( 'target' ) ), 410 tab = $el.data( 'tab' ); 411 412 if ( !$target.length ) { 413 return; 414 } 415 416 sober.openModal( $target, tab ); 417 } ); 418 419 sober.$body.on( 'click', '.close-modal, .sober-modal-backdrop', function ( e ) { 420 e.preventDefault(); 421 422 sober.closeModal(); 423 } ); 424 425 // Close when press escape button 426 $( document ).on( 'keyup', function ( e ) { 427 if ( e.keyCode === 27 ) { 428 sober.closeModal(); 429 } 430 } ); 431 }; 432 433 /** 434 * Open modal 435 * 436 * @param $modal 437 * @param tab 438 */ 439 sober.openModal = function ( $modal, tab ) { 440 $modal = $modal instanceof jQuery ? $modal : $( $modal ); 441 442 if ( ! $modal.length ) { 443 return; 444 } 445
446 sober.$body.addClass( 'modal-open' ); 447 $modal.fadeIn(); 448 $modal.addClass( 'open' ); 449 450 if ( tab ) { 451 var $tab = $modal.find( '.tab-nav' ).filter( '[data-tab="' + tab + '"]' ); 452 453 $tab.trigger( 'click' ); 454 } 455 }; 456 457 /** 458 * Close modal 459 */ 460 sober.closeModal = function () { 461 sober.$body.removeClass( 'modal-open' ); 462 463 var $opened = $( '.sober-modal.open' ); 464 465 $opened.fadeOut( function () { 466 $( this ).removeClass( 'open' ); 467 } ); 468 469 sober.$body.trigger( 'sober_modal_closed', [$opened] ); 470 }; 471 472 /** 473 * Toggle side menu and its' child items 474 */ 475 sober.toggleSideMenu = function () { 476 // Toggle side menu 477 sober.$body.on( 'click', '.toggle-nav', function () { 478 var target = '#' + $( this ).data( 'target' ); 479 480 $( target ).toggleClass( 'open' ); 481 sober.$body.toggleClass( 'side-menu-opened' ); 482 } ); 483 484 // Close when click to backdrop 485 sober.$body.on( 'click', '.side-menu-backdrop', function () { 486 $( '.side-menu' ).removeClass( 'open' ); 487 sober.$body.removeClass( 'side-menu-opened' ); 488 } ); 489 490 // Add class 'open' to current menu item 491 sober.$sideMenu.find( '.menu > .menu-item-has-children, .menu > ul > .menu-item-has-children' ).filter( function () { 492 return $( this ).hasClass( 'current-menu-item' ) || $( this ).hasClass( 'current-menu-ancestor' ); 493 } ).addClass( 'open' ); 494 495 // Toggle sub-menu 496 sober.$sideMenu.on( 'click', '.menu > .menu-item-has-children > a, .menu > ul > .menu-item-has-children > a', function ( e ) { 497 var $li = $( this ).parent(); 498 499 if ( $li.hasClass( 'open' ) && $li.hasClass( 'clicked' ) ) { 500 return true; 501 } 502 503 e.stopPropagation(); 504 e.preventDefault(); 505 506 $li.addClass( 'clicked' ); 507 508 $li.toggleClass( 'open' ).children( 'ul' ).slideToggle(); 509 $li.siblings( '.open' ).removeClass( 'open' ).children( 'ul' ).slideUp(); 510 } ).on( 'click', '.menu > .menu-item-has-children > .toggle, .menu > ul > .menu-item-has-children > .toggle', function( e ) { 511 e.stopPropagation(); 512 e.preventDefault(); 513 514 var $li = $( this ).parent(); 515 516 $li.toggleClass( 'open' ).children( 'ul' ).slideToggle(); 517 $li.siblings( '.open' ).removeClass( 'open' ).children( 'ul' ).slideUp(); 518 } ); 519 }; 520 521 /** 522 * Focusing inputs and adds 'active' class 523 */ 524 sober.focusInputs = function () { 525 526 $( '.woocommerce-account, .woocommerce-checkout' ).on( 'focus', '.input-text', function () { 527 $( this ).closest( '.form-row' ).addClass( 'active' ); 528 } ).on( 'focusout', '.input-text', function () { 529 if ( $( this ).val() === '' ) { 530 $( this ).closest( '.form-row' ).removeClass( 'active' ); 531 } 532 } ).find( '.input-text' ).each( function () { 533 if ( $( this ).val() !== '' ) { 534 $( this ).closest( '.form-row' ).addClass( 'active' ); 535 } 536 } ); 537 538 539 $( '#commentform' ).on( 'focus', ':input', function () { 540 $( this ).closest( 'p' ).addClass( 'active' ); 541 } ).on( 'focusout', ':input', function () { 542 if ( $( this ).val() === '' ) { 543 $( this ).closest( 'p' ).removeClass( 'active' ); 544 } 545 } ).find( ':input' ).each( function () { 546 if ( $( this ).val() !== '' ) { 547 $( this ).closest( 'p' ).addClass( 'active' ); 548 } 549 } ); 550 551 }; 552 553 /** 554 * Toggle tabs 555 */ 556 sober.toggleTabs = function () { 557 sober.$body.on( 'click', '.tabs-nav .tab-nav', function () { 558 var $tab = $( this ), 559 tab = $tab.data( 'tab' ); 560 561 if ( $tab.hasClass( 'active' ) ) { 562 return; 563 } 564 565 $tab.addClass( 'active' ).siblings( '.active' ).removeClass( 'active' ); 566 $tab.closest( '.tabs-nav' ).next( '.tab-panels' ) 567 .children( '[data-tab="' + tab + '"]' ).addClass( 'active' ) 568 .siblings( '.active' ).removeClass( 'active' ); 569 } ) 570 }; 571 572 /** 573 * Handle product tabs on product catalog pages 574 */ 575 sober.productsTabs = function() { 576 var $tabs = $( '.shop-toolbar .products-filter' ); 577 578 if ( ! $tabs.length ) { 579 return; 580 } 581 582 if ( 'isotope' == sober.data.tab_behaviour ) { 583 sober.isotopeProductTabs(); 584 } else if ( 'ajax' == sober.data.tab_behaviour ) { 585 sober.ajaxProductTabs(); 586 } else { 587 $tabs.on( 'click', 'li', function() { 588 $( this ).addClass( 'active' ).siblings( '.active' ).removeClass( 'active' ); 589 } ); 590 } 591 592 // Reset the active tab when using ajax filter 593 sober.$body.on( 'soo_filter_request_success sober_products_filter_request_success', function() { 594 $( 'li:first', '.products-filter' ).addClass( 'active' ).siblings( '.active' ).removeClass( 'active' ); 595 } ); 596 }; 597 598 /** 599 * Make products filterable with Isotope 600 */ 601 sober.isotopeProductTabs = function() { 602 var $container = $( '.woocommerce.archive .site-content ul.products' ); 603 604 var isotopeOptions = { 605 itemSelector : '.product', 606 transitionDuration: 700, 607 layoutMode : 'fitRows', 608 isOriginLeft : !(sober.data.isRTL === '1'), 609 hiddenStyle: { 610 opacity: 0, 611 transform: 'translate3d(0,50px,0)' 612 }, 613 visibleStyle: { 614 opacity: 1, 615 transform: 'none' 616 } 617 }; 618 619 $container.isotope( isotopeOptions ); 620 621 $container.imagesLoaded().progress( function() { 622 $container.isotope( 'layout' ); 623 } ); 624
625 // Support Jetpack lazy load. 626 $container.on( 'jetpack-lazy-loaded-image', 'img', function() { 627 if ( ! $container.data( 'isotope' ) ) { 628 return; 629 } 630 631 $( this ).imagesLoaded( function() { 632 $container.isotope( 'layout' ); 633 } ); 634 } ); 635 636 // Fix issue of hover style "slider". 637 $( '.product-images__slider', $container ).on( 'initialized.owl.carousel', function() { 638 $container.isotope( 'layout' ); 639 } ); 640 641 // Tabs. 642 $( '.shop-toolbar .products-filter' ).on( 'click', 'li', function( e ) { 643 e.preventDefault(); 644 e.stopPropagation(); 645 646 var $this = $( this ), 647 selector = $this.attr( 'data-filter' ); 648 649 if ( $this.hasClass( 'active' ) ) { 650 return; 651 } 652 653 $this.addClass( 'active' ).siblings( '.active' ).removeClass( 'active' ); 654 $this.closest( '.shop-toolbar' ).next( 'ul.products' ).isotope( { 655 filter: selector 656 } ); 657 } ); 658 659 // Reset the active tab when using ajax filter 660 sober.$body.on( 'soo_filter_request_success sober_products_filter_request_success', function() { 661 setTimeout( function() { 662 $( 'ul.products', '#primary' ).imagesLoaded( function() { 663 $( this.elements ).isotope( isotopeOptions ); 664 } ); 665 }, 100 ); 666 } ); 667 }; 668 669 /** 670 * Ajax product tabs on product catalog pages 671 */ 672 sober.ajaxProductTabs = function() { 673 $( '.shop-toolbar__tabs' ).on( 'click', 'li', function( e ) { 674 e.preventDefault(); 675 e.stopPropagation(); 676 677 var $this = $( this ), 678 $grid = $this.closest( '.shop-toolbar' ).next( 'ul.products' ), 679 $nav = $grid.next( '.woocommerce-pagination' ), 680 url = $this.children( 'a' ).attr( 'href' ); 681 682 if ( $this.hasClass( 'active' ) ) { 683 return; 684 } 685 686 // Reset the filter data 687 $( ':input', '.soo-product-filter-widget' ) 688 .not( ':button, :submit, :reset' ) 689 .val( '' ) 690 .removeAttr( 'checked' ) 691 .removeAttr( 'selected' ); 692 693 $( '.selected', '.soo-product-filter-widget' ).removeClass( 'selected' ); 694 695 $( '.filter-slider', '.soo-product-filter-widget' ).each( function() { 696 var $slider = $( this ), 697 options = $slider.slider( 'option' ); 698 699 $slider.slider( 'values', [options.min, options.max] ); 700 701 sober.$body.trigger( 'soo_price_filter_create', [options.min, options.max, $slider] ); 702 } ); 703 704 // Prepare grid if we are at no-products page 705 if ( ! $grid.length ) { 706 $this.closest( '.shop-toolbar' ).next( '.woocommerce-info' ).replaceWith( '<ul class="products"/>' ); 707 $grid = $this.closest( '.shop-toolbar' ).next( 'ul.products' ); 708 } 709 710 $this.addClass( 'active' ).siblings( '.active' ).removeClass( 'active' ); 711 $grid.addClass( 'loading' ); 712 $grid.append( '<li class="loading-overlay"><span class="loading-icon">\n' + 713 '<span class="bubble"><span class="dot"></span></span>\n' + 714 '<span class="bubble"><span class="dot"></span></span>\n' + 715 '<span class="bubble"><span class="dot"></span></span>\n' + 716 '</span></li>' ); 717 $nav.fadeOut(); 718 719 $.get( url, function( response ) { 720 var $primary = $( response ).find( '#primary' ), 721 $products = $( 'ul.products', $primary ).children(), 722 $info = $( '.woocommerce-info', $primary ), 723 $button = $( '.woocommerce-pagination', $primary ); 724 725 if ( $products.length ) { 726 $( '.woocommerce-result-count' ).html( $( '.woocommerce-result-count', $primary ).html() ); 727 728 $products.addClass( 'soberFadeInUp soberAnimation' ); 729 730 $products.each( function( index ) { 731 $( this ).css( 'animation-delay', index * 100 + 'ms' ); 732 } ); 733 734 $grid.html( $products ).removeClass( 'loading' ); 735 736 // Replace button 737 if ( $button.length ) { 738 if ( $nav.length ) { 739 $nav.html( $button.html() ).fadeIn(); 740 } else { 741 $nav = $button; 742 $nav.insertAfter( $grid ); 743 } 744 } else { 745 $nav.html( '' ).fadeOut(); 746 } 747 748 sober.$body.trigger( 'sober_products_loaded', [$products] ); 749 750 // Update the filter form. 751 $( '.shop-toolbar .filter-widgets .sober-products-filter-widget' ).each( function() { 752 var $widget = $( this ), 753 widgetId = $widget.attr( 'id' ), 754 $newWidget = $( '#' + widgetId, response ); 755
756 if ( ! $newWidget.length ) { 757 return; 758 } 759 760 $( '.filters', $widget ).html( $( '.filters', $newWidget ).html() ); 761 $( '.products-filter__activated', $widget ).html( $( '.products-filter__activated', $newWidget ).html() ); 762 763 $( document.body ).trigger( 'sober_products_filter_widget_updated', [$widget.find( 'form' ).get(0)] ); 764 } ); 765 } else { 766 $( '.woocommerce-result-count' ).html( '' ); 767 $grid.html( '' ).removeClass( 'loading' ).append( $( '<li class="no-result-message" />' ).append( $info ) ); 768 $nav.html( '' ).fadeOut(); 769 } 770 771 window.history.pushState( null, '', url ); 772 } ); 773 } ); 774 }; 775 776 /** 777 * Use select2 to styling the dropdown of the product order select 778 */ 779 sober.sortProducts = function() { 780 if ( ! $.fn.select2 ) { 781 return; 782 } 783 784 $( '.woocommerce-ordering select' ).select2( { 785 minimumResultsForSearch: -1, 786 width: 'auto', 787 dropdownAutoWidth: true, 788 dropdownCssClass: 'woocommerce-ordering-select2-dropdown', 789 } ); 790 }; 791 792 /** 793 * Change product quantity 794 */ 795 sober.productQuantity = function () { 796 sober.$body.on( 'click', '.quantity .increase, .quantity .decrease', function ( e ) { 797 e.preventDefault(); 798 799 var $this = $( this ), 800 $qty = $this.siblings( '.qty' ), 801 current = parseFloat( $qty.val() ), 802 min = parseFloat( $qty.attr( 'min' ) ), 803 max = parseFloat( $qty.attr( 'max' ) ), 804 step = parseFloat( $qty.attr( 'step' ) ); 805 806 if ( true === $qty.prop( 'readonly' ) ) { 807 return; 808 } 809 810 current = current ? current : 0; 811 min = min ? min : 0; 812 max = max ? max : current + 1; 813 step = step ? step : 1; 814 815 if ( $this.hasClass( 'decrease' ) && current > min ) { 816 $qty.val( current - step ); 817 $qty.trigger( 'change' ); 818 } 819 if ( $this.hasClass( 'increase' ) && current < max ) { 820 $qty.val( current + step ); 821 $qty.trigger( 'change' ); 822 } 823 } ); 824 }; 825 826 /** 827 * Make up sells products be sticky 828 */ 829 sober.stickUpSells = function () { 830 if ( !sober.$body.hasClass( 'product-style-3' ) ) { 831 return; 832 } 833 834 var $topbar = $( '#topbar' ), 835 $header = $( '#masthead' ), 836 $upsells = $( '.side-products' ); 837 838 if ( !$upsells.length ) { 839 return; 840 } 841 842 var $primary = $( '#primary' ); 843 844 var offset = $topbar.length ? $topbar.outerHeight() : 0; 845 offset += $header.length ? $header.outerHeight() : 0; 846 847 $( window ).on( 'scroll', function () { 848 if ( $( document ).scrollTop() >= offset ) { 849 $upsells.addClass( 'sticky' ); 850 851 if ( !$upsells.data( 'wrap' ) ) { 852 $upsells.wrap( '<div id="upsells-wrap" class="upsells-wrap"/>' ); 853 $upsells.data( 'wrap', true ); 854 } 855 } else { 856 $upsells.removeClass( 'sticky' ); 857 858 if ( $upsells.data( 'wrap' ) ) { 859 $upsells.unwrap(); 860 $upsells.data( 'wrap', false ); 861 } 862 } 863 864 if ( $( window ).scrollTop() >= $primary.offset().top + $primary.outerHeight() - window.innerHeight ) { 865 var $wrap = $( '#upsells-wrap' ); 866 867 $wrap.addClass( 'sticky-bottom' ).css( 'top', function () { 868 return $( window ).scrollTop() - $primary.offset().top; 869 } ); 870 871 if ( $wrap.outerHeight() <= $upsells.outerHeight() ) { 872 $wrap.addClass( 'reach-bottom' ); 873 } else { 874 $wrap.removeClass( 'reach-bottom' ); 875 } 876 } else { 877 $( '#upsells-wrap' ).removeClass( 'sticky-bottom' ).css( 'top', 0 ); 878 } 879 } ).trigger( 'scroll' ); 880 }; 881 882 /** 883 * Prepare and init carousel for up sell products. 884 * This carousel is placed on product style v3. 885 */ 886 sober.sideProductsCarousel = function () { 887 if ( !sober.$body.hasClass( 'product-style-3' ) ) { 888 return; 889 } 890 891 var $sideProducts = $( '.side-products' ); 892 893 if ( !$sideProducts.length ) { 894 return; 895 } 896 897 if ( $sideProducts.find( 'li.product' ).length > 4 ) { 898 $sideProducts.find( 'ul.products' ).addClass( 'owl-carousel' ).owlCarousel( { 899 rtl : sober.data.isRTL === "1", 900 owl2row : true, 901 owl2rowTarget: 'product', 902 dots : true, 903 nav : true, 904 navText : ['<svg viewBox="0 0 20 20"><use xlink:href="#left-arrow"></use></svg>', '<svg viewBox="0 0 20 20"><use xlink:href="#right-arrow"></use></svg>'], 905 items : 2, 906 responsive : { 907 0 : { 908 items: 1 909 }, 910 992 : { 911 items: 1 912 }, 913 1200: { 914 items: 2 915 } 916 } 917 } ); 918 } 919 }; 920 921 /** 922 * Show photoSwipe lightbox for product images 923 */ 924 sober.productImagesLightbox = function () { 925 if ( '1' !== sober.data.lightbox ) { 926 return; 927 } 928 929 var $product = $( 'div.product' ), 930 $gallery = $( 'div.images', $product ), 931 $slides = $( '.woocommerce-product-gallery__slider .woocommerce-product-gallery__image', $gallery ); 932 933 if ( !$gallery.length ) { 934 return; 935 } 936 937 $gallery.on( 'click', '.woocommerce-product-gallery__slider .woocommerce-product-gallery__image a', function ( e ) { 938 e.preventDefault(); 939 940 var items = getGalleryItems(), 941 $target = $( e.target ), 942 $clicked = $target.closest( '.owl-item' ), 943 options = { 944 index : $clicked.length ? $clicked.index() : $target.closest( '.woocommerce-product-gallery__image' ).index(), 945 history : false, 946 bgOpacity : 0.85, 947 showHideOpacity : true,
948 mainClass : 'pswp--minimal-dark', 949 captionEl : true, 950 fullscreenEl : false, 951 shareEl : false, 952 tapToClose : true, 953 tapToToggleControls: false 954 }; 955 956 var lightBox = new PhotoSwipe( $( '.pswp' ).get(0), window.PhotoSwipeUI_Default, items, options ); 957 958 if ( $gallery.hasClass( 'woocommerce-product-gallery--has-video' ) ) { 959 // Pause the current player. 960 $gallery.find( 'video' ).each( function() { 961 this.player.pause(); 962 } ); 963 964 // Stop videos inside oembed iframes. 965 $gallery.find( '.sober-product-video__content > iframe' ).each( function() { 966 this.src = this.src; 967 } ); 968 969 lightBox.listen( 'afterChange', function() { 970 window.wp.mediaelement.initialize(); 971 972 var $container = $( lightBox.currItem.container ), 973 $video = $container.children( '.wp-video-shortcode' ), 974 videoRatio = $video.width() / $video.height(); 975 976 var viewport = lightBox.viewportSize, 977 videoHeight = viewport.y * 0.6, 978 videoWidth = videoHeight * videoRatio; 979 980 if ( lightBox.currItem.isVideo ) { 981 $container.find( 'video' ).each( function() { 982 this.muted = false; 983 this.controls = false; 984 this.player.play(); 985 } ); 986 987 // Set Video Size 988 sober.$window.on( 'resize.sober_gallery_video', function() { 989 $container 990 .children( '.wp-video-shortcode' ) 991 .css({ 992 'max-width' : videoWidth, 993 'max-height': videoHeight, 994 'top' : ( viewport.y - videoHeight ) / 2, 995 'left' : ( viewport.x - videoWidth ) / 2 996 }); 997 } ).trigger( 'resize' ); 998 } else { 999 $( lightBox.container ).find( 'video' ).each( function() { 1000 this.player.pause(); 1001 } ); 1002 1003 // Handle oembed iframe. 1004 $( lightBox.container ).find( '.pswp__zoom-wrap > iframe' ).each( function() { 1005 this.src = this.src; 1006 } ); 1007 } 1008 } ); 1009 1010 lightBox.listen( 'close', function() { 1011 // Re-play gallery videos. 1012 if ( $product.hasClass( 'layout-style-6' ) || $product.hasClass( 'layout-style-1' ) ) { 1013 $gallery.find( 'video' ).each( function() { 1014 this.player.play(); 1015 } ); 1016 } 1017 1018 // Pause all videos inside the lightbox. 1019 $( lightBox.container ).find( 'video' ).each( function() { 1020 if ( this.player ) { 1021 this.player.pause(); 1022 } 1023 } ); 1024 1025 // Stop videos inside oembed iframes. 1026 $( lightBox.container ).find( '.pswp__zoom-wrap > iframe' ).each( function() { 1027 this.src = this.src; 1028 } ); 1029 1030 // Detach listener resize.sober_gallery_video. 1031 sober.$window.off( 'resize.sober_gallery_video' ); 1032 } ); 1033 } 1034 1035 lightBox.init(); 1036 } ); 1037 1038 /** 1039 * Private function to get gallery items 1040 * @returns {Array} 1041 */ 1042 function getGalleryItems() { 1043 var items = []; 1044 1045 if ( $slides.length > 0 ) { 1046 $slides.each( function( i, el ) { 1047 var $el = $( el ), 1048 img = $el.find( 'img' ), 1049 large_image_src = img.attr( 'data-large_image' ), 1050 large_image_w = img.attr( 'data-large_image_width' ), 1051 large_image_h = img.attr( 'data-large_image_height' ), 1052 item; 1053 1054 if ( $el.hasClass( 'sober-product-video' ) ) { 1055 var $video = $el.find( 'video.wp-video-shortcode' ); 1056 1057 $video = $video.length > 0 ? $video : $el.find( '.sober-product-video__content iframe' ); 1058 1059 if ( $video.length ) { 1060 item = { 1061 html : $video.get(0).outerHTML.replace( 'muted', '' ), 1062 isVideo: true, 1063 }; 1064 } 1065 } else { 1066 item = { 1067 src : large_image_src, 1068 w : large_image_w, 1069 h : large_image_h, 1070 title : img.attr( 'data-caption' ) ? img.attr( 'data-caption' ): '', 1071 isVideo: false 1072 }; 1073 } 1074 items.push( item ); 1075 } ); 1076 } 1077 1078 return items; 1079 } 1080 }; 1081 1082 /** 1083 * Make product summary be sticky when use product layout 1 1084 */ 1085 sober.stickyProductSummary = function () { 1086 if ( ! $.fn.stick_in_parent ) { 1087 return; 1088 } 1089 1090 stickyProductSummary(); 1091 1092 sober.$window.on( 'resize', stickyProductSummary ); 1093 1094 function stickyProductSummary() { 1095 if ( sober.$window.width() > 768 ) { 1096 $( 'div.product .sticky-summary' ).stick_in_parent( { 1097 parent: '.product-summary' 1098 } ); 1099 } else { 1100 $( 'div.product .sticky-summary' ).trigger( 'sticky_kit:detach' ); 1101 } 1102 } 1103 }; 1104 1105 /** 1106 * Back to top icon 1107 */
1108 sober.backToTop = function () { 1109 sober.$body.on( 'click', '#gotop', function ( e ) { 1110 e.preventDefault(); 1111 1112 $( 'html, body' ).animate( {scrollTop: 0}, 'slow' ); 1113 } ); 1114 }; 1115 1116 /** 1117 * Toggle product filter widgets 1118 */ 1119 sober.toggleProductFilter = function () { 1120 sober.$body.on( 'click', '.toggle-filter', function ( e ) { 1121 e.preventDefault(); 1122 1123 $( this ).next( '.filter-widgets' ).fadeIn( function () { 1124 $( this ).addClass( 'active' ); 1125 } ); 1126 } ).on( 'click', '.filter-widgets button.close', function () { 1127 $( this ).closest( '.filter-widgets' ).fadeOut( function () { 1128 $( this ).removeClass( 'active' ); 1129 } ); 1130 } ); 1131 1132 }; 1133 1134 /** 1135 * Toggle product quick view 1136 */ 1137 sober.productQuickView = function () { 1138 if ( !sober.$body.hasClass( 'product-quickview-enable' ) ) { 1139 return; 1140 } 1141 1142 var target = 'ul.products .woocommerce-LoopProduct-link'; 1143 1144 switch ( sober.data.quickview ) { 1145 case 'buy_button': 1146 target = 'ul.products .sober-loop-atc-button'; 1147 break; 1148 1149 case 'view_button': 1150 target = 'ul.products .quick_view_button'; 1151 break; 1152 1153 case 'title': 1154 target = 'ul.products h3 a'; 1155 break; 1156 } 1157 1158 sober.$body.on( 'click', target, function ( e ) { 1159 if ( sober.$window.width() <= 768 ) { 1160 return; 1161 } 1162 1163 e.preventDefault(); 1164 1165 var $a = $( this ), 1166 product_id = $a.data( 'product_id' ) ? $a.data( 'product_id' ) : $a.closest( '.product' ).find( '.sober-loop-atc-button' ).data( 'product_id' ), 1167 url = $a.data( 'url' ) ? $a.data( 'url' ) : $a.attr( 'href' ), 1168 ajax_url = woocommerce_params ? woocommerce_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'product_quick_view' ) : sober.data.ajaxurl, 1169 $modal = $( '#quick-view-modal' ), 1170 $product = $modal.find( '.product' ), 1171 $button = $modal.find( '.modal-header .close-modal' ).first().clone(); 1172 1173 // Incase no product_id found. 1174 if ( ! product_id ) { 1175 var inClass = $a.closest( '.product' ).get( 0 ).classList.value.match( /post-\d*/ ); 1176 1177 if ( inClass ) { 1178 product_id = parseInt( inClass[0].replace( 'post-', '' ) ); 1179 } 1180 } 1181 1182 $product.hide().html( '' ).addClass( 'invisible' ); 1183 $modal.addClass( 'loading' ); 1184 sober.openModal( $modal ); 1185 1186 $.post( ajax_url, { 1187 action : 'sober_get_product_quickview', 1188 product_id: product_id, 1189 }, function ( response ) { 1190 var $_product = $( response.data ); 1191 1192 $_product.find( 'h1.product_title' ).wrapInner( '<a href="' + url + '"></a>' ); 1193 $product.show().html( $_product ).prepend( $button ); 1194 1195 var $gallery = $product.find( '.images' ), 1196 $variations = $product.find( '.variations_form' ), 1197 $carousel = $gallery.find( '.woocommerce-product-gallery__slider' ); 1198 1199 // Force height for images 1200 $carousel.find( 'img' ).css( 'height', $product.outerHeight() ); 1201 1202 // Play the gallery video. 1203 if ( $gallery.hasClass( 'woocommerce-product-gallery--has-video' ) ) { 1204 var $video = $carousel.find( 'video' ); 1205 1206 if ( $video.length ) { 1207 $video.mediaelementplayer(); 1208 1209 var player = $video.get(0).player; 1210 1211 $carousel.on( 'initialized.owl.carousel translated.owl.carousel', function( event ) { 1212 var $currentSlide = $carousel.find( '.owl-item:eq(' + event.item.index + ')' ); 1213 1214 if ( $currentSlide.children( '.woocommerce-product-gallery__image' ).hasClass( 'sober-product-video' ) ) { 1215 player.play(); 1216 } else { 1217 player.pause(); 1218 } 1219 } ); 1220 } 1221 } 1222 1223 if ( $carousel.children().length > 1 ) { 1224 $carousel.addClass( 'owl-carousel' ).owlCarousel( { 1225 rtl : sober.data.isRTL === "1", 1226 items : 1, 1227 smartSpeed: 500, 1228 dots : false, 1229 nav : true, 1230 navText : ['<svg width="20" height="20"><use xlink:href="#left-arrow"></use></svg>', '<svg width="20" height="20"><use xlink:href="#right-arrow"></use></svg>'], 1231 } ); 1232 } 1233 1234 $gallery.css( 'opacity', 1 ); 1235 1236 $carousel.on( 'click', '.woocommerce-product-gallery__image a', function( event ) { 1237 event.preventDefault(); 1238 } ); 1239 1240 $variations.wc_variation_form().find( '.variations select:eq(0)' ).change(); 1241 1242 if ( $.fn.tawcvs_variation_swatches_form ) { 1243 $variations.tawcvs_variation_swatches_form(); 1244 sober.modifyVariationSwatches( $variations ); 1245 } 1246 1247 $variations.on( 'reset_image found_variation', function() { 1248 $carousel.trigger( 'to.owl.carousel', [0] ); 1249 } ); 1250 1251 $modal.removeClass( 'loading' ); 1252 $product.removeClass( 'invisible' ); 1253 1254 if ( $product.find( '.bundle_form .bundle_data' ).length && typeof jQuery.fn.wc_pb_bundle_form === 'function' ) { 1255 $product.find( '.bundle_form .bundle_data' ).wc_pb_bundle_form(); 1256 } 1257 1258 if ( typeof SimpleScrollbar !== 'undefined' ) { 1259 SimpleScrollbar.initEl( $_product.find( '.summary' ).get( 0 ) ); 1260 } 1261 1262 sober.$body.trigger( 'sober_quickview_opened', [$_product, $modal, sober] ); 1263 sober.$body.trigger( 'init_variation_swatches' ); 1264 } ).fail( function() { 1265 window.location.herf = url; 1266 } ); 1267 1268 if ( 'buy_button' === sober.data.quickview ) { 1269 return false; // Prevent ajax add to cart 1270 } 1271 } ); 1272 1273 sober.$body.on( 'sober_modal_closed', function( e, $modal ) { 1274 if ( $modal.hasClass( 'quick-view-modal' ) ) { 1275 $modal.find( '.product' ).html( '' ); 1276 } 1277 } ); 1278 }; 1279 1280 /** 1281 * Product instance search 1282 */ 1283 sober.instanceSearch = function () { 1284 var xhr = null, 1285 term = '', 1286 searchCache = {}, 1287 initialized = {}, 1288 debounceTimeout = null, 1289 $modal = $( '#search-modal' ), 1290 $form = $modal.find( 'form' ),
1291 $search = $form.find( 'input.search-field' ), 1292 $results = $modal.find( '.search-results' ), 1293 $button = $results.find( '.search-results-button' ), 1294 post_type = $modal.find( 'input[name=post_type]' ).val(); 1295 1296 // Focus on the search field when search modal opened 1297 sober.$body.on( 'click', '[data-toggle="modal"][data-target="search-modal"]', function() { 1298 $search.focus(); 1299 } ); 1300 1301 $modal.on( 'keyup', '.search-field', function ( e ) { 1302 var valid = false; 1303 1304 if ( typeof e.which === 'undefined' ) { 1305 valid = true; 1306 } else if ( typeof e.which === 'number' && e.which > 0 ) { 1307 valid = !e.ctrlKey && !e.metaKey && !e.altKey; 1308 } 1309 1310 if ( !valid ) { 1311 return; 1312 } 1313 1314 if ( xhr ) { 1315 xhr.abort(); 1316 } 1317 1318 clearTimeout( debounceTimeout ); 1319 1320 debounceTimeout = setTimeout( function() { search( true ) }, 500 ); 1321 } ).on( 'change', '.product-cats input', function () { 1322 if ( xhr ) { 1323 xhr.abort(); 1324 } 1325 1326 clearTimeout( debounceTimeout ); 1327 1328 debounceTimeout = setTimeout( function() { search( false ) }, 500 ); 1329 } ).on( 'click', '.search-reset', function () { 1330 if ( xhr ) { 1331 xhr.abort(); 1332 } 1333 1334 $modal.addClass( 'reset' ); 1335 $results.find( '.results-container, .view-more-results' ).slideUp( function () { 1336 $modal.removeClass( 'searching searched found-products found-no-product invalid-length reset' ); 1337 } ); 1338 } ).on( 'focusout', '.search-field', function () { 1339 if ( $search.val().length < 2 ) { 1340 $results.find( '.results-container, .view-more-results' ).slideUp( function () { 1341 $modal.removeClass( 'searching searched found-products found-no-product invalid-length' ); 1342 } ); 1343 } 1344 } ); 1345 1346 /** 1347 * Private function for searching products 1348 */ 1349 function search( typing ) { 1350 var keyword = $search.val(), 1351 $category = $form.find( '.product-cats input:checked' ), 1352 category = $category.length ? $category.val() : '', 1353 key = keyword + '[' + category + ']'; 1354 1355 if ( term === keyword && typing ) { 1356 return; 1357 } 1358 1359 term = keyword; 1360 1361 if ( keyword.length < 2 ) { 1362 $modal.removeClass( 'searching found-products found-no-product' ).addClass( 'invalid-length' ); 1363 return; 1364 } 1365 1366 var url = $form.attr( 'action' ) + '?' + $form.serialize(); 1367 1368 $button.removeClass( 'soberFadeInUp' ); 1369 $( '.view-more-results', $results ).slideUp( 10 ); 1370 $modal.removeClass( 'found-products found-no-product' ).addClass( 'searching' ); 1371 1372 if ( key in searchCache ) { 1373 showResult( searchCache[key] ); 1374 } else { 1375 xhr = $.get( url, function ( response ) { 1376 var $content = $( '#primary', response ); 1377 1378 if ( 'product' === post_type ) { 1379 var $products = $( 'ul.products', $content ); 1380 1381 if ( $products.length ) { 1382 $products.children( '.last' ).nextAll().remove(); 1383 1384 // Cache 1385 searchCache[key] = { 1386 found: true, 1387 items: $products, 1388 url : url 1389 }; 1390 } else { 1391 // Cache 1392 searchCache[key] = { 1393 found: false, 1394 text : $( '.woocommerce-info', $content ).text() 1395 }; 1396 } 1397 } else if ( 'post' === post_type ) { 1398 var $posts = $( '#main article:lt(3)', $content ); 1399 1400 if ( $posts.length ) { 1401 $posts.addClass( 'col-md-4' ); 1402 1403 searchCache[key] = { 1404 found: true, 1405 items: $( '<div class="posts row" />' ).append( $posts ), 1406 url : url 1407 }; 1408 } else { 1409 searchCache[key] = { 1410 found: false, 1411 text : $( '.no-results .page-header-none', $content ).text() 1412 }; 1413 } 1414 } 1415 1416 showResult( searchCache[key] ); 1417 1418 $modal.addClass( 'searched' ); 1419 }, 'html' ); 1420 } 1421 } 1422 1423 /** 1424 * Private function for showing the search result 1425 * 1426 * @param result 1427 */ 1428 function showResult( result ) { 1429 var extraClass = 'product' === post_type ? 'woocommerce' : 'sober-post-grid'; 1430 1431 $modal.removeClass( 'searching' ); 1432 1433 if ( result.found ) { 1434 var grid = result.items.clone(), 1435 items = grid.children(); 1436 1437 $modal.addClass( 'found-products' ); 1438 1439 $results.find( '.results-container' ).addClass( extraClass ).html( grid ); 1440 1441 // Add animation class 1442 for ( var index = 0; index < items.length; index++ ) { 1443 $( items[index] ).css( 'animation-delay', index * 100 + 'ms' ); 1444 } 1445 items.addClass( 'soberFadeInUp soberAnimation' ); 1446 1447 $button.attr( 'href', result.url ).css( 'animation-delay', index * 100 + 'ms' ).addClass( 'soberFadeInUp soberAnimation' ); 1448 1449 $results.find( '.results-container, .view-more-results' ).slideDown( 300, function () {
1450 $modal.removeClass( 'invalid-length' ); 1451 } ); 1452 1453 // Init zoom/slider thumbnail 1454 if ( 'product' === post_type ) { 1455 sober.$body.trigger( 'sober_products_loaded', [$results] ); 1456 } else { 1457 sober.$body.trigger( 'post-load' ); 1458 } 1459 } else { 1460 $modal.addClass( 'found-no-product' ); 1461 1462 $results.find( '.results-container' ).removeClass( extraClass ).html( $( '<div class="not-found text-center" />' ).text( result.text ) ); 1463 $button.attr( 'href', '#' ); 1464 1465 $results.find( '.view-more-results' ).slideUp( 300 ); 1466 $results.find( '.results-container' ).slideDown( 300, function () { 1467 $modal.removeClass( 'invalid-length' ); 1468 } ); 1469 } 1470 1471 $modal.addClass( 'searched' ); 1472 } 1473 }; 1474 1475 /** 1476 * Init simple scrollbar 1477 */ 1478 sober.fakeScrollbar = function () { 1479 var el = document.querySelector( '#primary-menu.side-menu' ); 1480 1481 if ( el && typeof SimpleScrollbar !== 'undefined' ) { 1482 SimpleScrollbar.initEl( el ); 1483 } 1484 }; 1485 1486 /** 1487 * Set CSS vars 1488 */ 1489 sober.setCSSVars = function() { 1490 var css = '', 1491 $topbar = $( '#topbar' ), 1492 $header = $( '#masthead' ); 1493 1494 if ( $topbar.length ) { 1495 css += '--topbar-height: ' + $topbar.outerHeight() + 'px;'; 1496 } 1497 1498 if ( $header.length ) { 1499 css += '--header-height: ' + $header.outerHeight() + 'px;'; 1500 } 1501 1502 if ( css ) { 1503 css = ':root{' + css + '}'; 1504 $( document.head ).append( '<style id="sober-custom-css-vars">' + css + '</style>' ); 1505 } 1506 } 1507 1508 /** 1509 * Sticky header 1510 */ 1511 sober.stickyHeader = function () { 1512 if ( !sober.data.sticky_header || 'none' === sober.data.sticky_header ) { 1513 return; 1514 } 1515 1516 var header = document.getElementById( 'masthead' ); 1517 var topbar = document.getElementById( 'topbar' ); 1518 var adminbar = document.getElementById( 'wpadminbar' ); 1519 var offset = 0; 1520 1521 if ( ! header ) { 1522 return; 1523 } 1524 1525 // Prepare for white header 1526 prepareForWhiteHeader(); 1527 calculateOffset(); 1528 1529 sober.$window.on( 'resize', function() { 1530 prepareForWhiteHeader(); 1531 calculateOffset(); 1532 } ); 1533 1534 // Init sticky header. 1535 if ( 'smart' === sober.data.sticky_header && typeof Headroom !== 'undefined' ) { 1536 var stickyHeader = new Headroom( header, { 1537 offset : offset, 1538 onTop : function () { 1539 setTimeout( function () { 1540 header.classList.remove( 'headroom--animation' ); 1541 }, 500 ); 1542 }, 1543 onNotTop: function () { 1544 setTimeout( function () { 1545 header.classList.add( 'headroom--animation' ); 1546 }, 10 ); 1547 } 1548 } ); 1549 1550 stickyHeader.init(); 1551 } else if ( 'normal' === sober.data.sticky_header ) { 1552 sticky(); 1553 1554 sober.$window.on( 'scroll', function () { 1555 sticky(); 1556 } ); 1557 } 1558 1559 /** 1560 * Private function for sticky header 1561 */ 1562 function sticky() { 1563 if ( sober.$window.scrollTop() >= offset ) { 1564 header.classList.add( 'sticky' ); 1565 } else { 1566 header.classList.remove( 'sticky' ); 1567 } 1568 } 1569 1570 function calculateOffset() { 1571 offset = topbar ? topbar.clientHeight : 1; 1572 1573 if ( sober.$window.width() < 783 && adminbar ) { 1574 offset += adminbar.clientHeight; 1575 } 1576 } 1577 1578 /** 1579 * Add empty spacing for white header. 1580 */ 1581 function prepareForWhiteHeader() { 1582 var needPrepare = sober.$body.hasClass( 'header-white' ) || sober.$body.hasClass( 'header-dark' ) || sober.$body.hasClass( 'header-custom' ); 1583 1584 if ( ! needPrepare ) { 1585 needPrepare = ( sober.$body.hasClass( 'no-page-header' ) && !sober.$body.hasClass( 'page-title-hidden' ) && !sober.$body.hasClass( 'page-template-homepage' ) && ! sober.$body.hasClass( 'product-style-5' ) ); 1586 } 1587 1588 if ( sober.$body.hasClass( 'page-template-full-screen' ) ) { 1589 needPrepare = false; 1590 } 1591 1592 if ( needPrepare ) { 1593 if ( topbar ) { 1594 topbar.style.marginBottom = header.clientHeight + 'px'; 1595 } else { 1596 document.getElementById( 'page' ).style.paddingTop = header.clientHeight + 'px'; 1597 } 1598 } 1599 } 1600 }; 1601 1602 /** 1603 * Initialize isotope for portfolio items 1604 */ 1605 sober.portfolioFilter = function () { 1606 var $items = $( '.portfolio-items:not(.sober-portfolio__row)' ); 1607 1608 if ( ! $items.length ) { 1609 return; 1610 } 1611 1612 var options = { 1613 itemSelector : '.portfolio', 1614 transitionDuration: 700, 1615 isOriginLeft : !(sober.data.isRTL === '1') 1616 }; 1617 1618 if ( $items.hasClass( 'portfolio-fullwidth' ) ) { 1619 options.masonry = { 1620 columnWidth: '.col-md-3' 1621 }; 1622 } 1623 1624 if ( $items.hasClass( 'portfolio-classic' ) ) { 1625 options.layoutMode = 'fitRows'; 1626 } 1627 1628 $items.isotope( options ); 1629
1630 $items.imagesLoaded().always( function() { 1631 $items.isotope( 'layout' ); 1632 } ); 1633 1634 // Support Jetpack lazy load. 1635 $items.on( 'jetpack-lazy-loaded-image', 'img', function() { 1636 $items.isotope( 'layout' ); 1637 } ); 1638 1639 var $filter = $( '.portfolio-filter' ); 1640 1641 $filter.on( 'click', 'li', function ( e ) { 1642 e.preventDefault(); 1643 1644 var $this = $( this ), 1645 selector = $this.attr( 'data-filter' ); 1646 1647 if ( $this.hasClass( 'active' ) ) { 1648 return; 1649 } 1650 1651 $this.addClass( 'active' ).siblings( '.active' ).removeClass( 'active' ); 1652 $this.closest( '.portfolio-filter' ).next( '.portfolio-items' ).isotope( { 1653 filter: selector 1654 } ); 1655 } ); 1656 }; 1657 1658 /** 1659 * Add extra script for product variation swatches 1660 * This function will run after plugin swatches did 1661 */ 1662 sober.productVariationSwatches = function () { 1663 sober.$body.on( 'tawcvs_initialized', function () { 1664 var $form = $( '.variations_form' ); 1665 1666 sober.modifyVariationSwatches( $form ); 1667 } ); 1668 }; 1669 1670 /** 1671 * Modify variation swatches 1672 * This function is used in sober.productVariationSwatches and sober.productQuickView 1673 * 1674 * @param $form 1675 */ 1676 sober.modifyVariationSwatches = function ( $form ) { 1677 var $variables = $form.find( '.variations .variable' ); 1678 1679 // Remove class "swatches-support" if there is no swatches in this product 1680 var hasSwatches = false; 1681 $variables.each( function() { 1682 if ( $( this ).hasClass( 'swatches' ) ) { 1683 hasSwatches = true; 1684 } 1685 } ); 1686 1687 if ( ! hasSwatches ) { 1688 $form.removeClass( 'swatches-support' ); 1689 } 1690 1691 // Add class for the last even variation 1692 if ( $variables.length % 2 ) { 1693 $variables.last().addClass( 'wide-variable' ); 1694 } 1695 1696 // Change alert style 1697 $form.off( 'tawcvs_no_matching_variations' ); 1698 $form.on( 'tawcvs_no_matching_variations', function () { 1699 event.preventDefault(); 1700 1701 $form.find( '.woocommerce-variation.single_variation' ).show(); 1702 if ( typeof wc_add_to_cart_variation_params !== 'undefined' ) { 1703 $form.find( '.single_variation' ).stop( true, true ).slideDown( 500 ).html( '<p class="invalid-variation-combination">' + wc_add_to_cart_variation_params.i18n_no_matching_variations_text + '</p>' ); 1704 } 1705 } ); 1706 }; 1707 1708 /** 1709 * Init the zoom function for product gallery 1710 */ 1711 sober.productGalleryZoom = function() { 1712 sober.initZoom( '.woocommerce-product-gallery__slider .woocommerce-product-gallery__image' ); 1713 1714 $( '.woocommerce-product-gallery--with-images' ).on( 'woocommerce_gallery_init_zoom', function() { 1715 var $target = $( '.woocommerce-product-gallery__image', this ).first().trigger( 'zoom.destroy' ); 1716 sober.initZoom( $target ); 1717 } ).on( 'click', '.zoomImg', function() { 1718 $( this ).prev( 'a' ).trigger( 'click' ); 1719 } ); 1720 }; 1721 1722 /** 1723 * Init zoom function on selected image 1724 * 1725 * @param zoomTarget 1726 */ 1727 sober.initZoom = function( zoomTarget ) { 1728 if ( ! sober.data.zoom || ! $.fn.zoom ) { 1729 return; 1730 } 1731 1732 var $zoomTarget = $( zoomTarget ), 1733 galleryWidth = $zoomTarget.width(), 1734 zoomEnabled = false; 1735 1736 $zoomTarget.each( function( index, target ) { 1737 var image = $( target ).find( 'img' ); 1738 1739 if ( image.data( 'large_image_width' ) > galleryWidth ) { 1740 zoomEnabled = true; 1741 return false; 1742 } 1743 } ); 1744 1745 // But only zoom if the img is larger than its container. 1746 if ( zoomEnabled ) { 1747 var zoom_options = { 1748 touch: false 1749 }; 1750 1751 if ( 'ontouchstart' in window ) { 1752 zoom_options.on = 'click'; 1753 } 1754 1755 $zoomTarget.trigger( 'zoom.destroy' ); 1756 $zoomTarget.zoom( zoom_options ); 1757 } 1758 }; 1759 1760 /** 1761 * Init product gallery as a slider 1762 */ 1763 sober.productGallerySlider = function() { 1764 var $product = $( '.woocommerce div.product' ), 1765 $gallery = $( 'div.images', $product ), 1766 $carousel = $( '.woocommerce-product-gallery__slider', $gallery ), 1767 $thumbnails = $( '.thumbnails', $gallery ); 1768 1769 // Play the gallery video. 1770 if ( $gallery.hasClass( 'woocommerce-product-gallery--has-video' ) ) { 1771 var $video = $carousel.find( 'video' ); 1772 1773 if ( $video.length ) { 1774 var player = $video.get(0).player; 1775 1776 if ( $product.hasClass( 'layout-style-6' ) || $product.hasClass( 'layout-style-1' ) ) { 1777 player.play(); 1778 } else { 1779 $carousel.on( 'initialized.owl.carousel translated.owl.carousel', function( event ) { 1780 var $currentSlide = $carousel.find( '.owl-item:eq(' + event.item.index + ')' ); 1781 1782 if ( $currentSlide.children( '.woocommerce-product-gallery__image' ).hasClass( 'sober-product-video' ) ) { 1783 player.play(); 1784 } else { 1785 player.pause(); 1786 } 1787 } ); 1788 } 1789 } 1790 } 1791 1792 // Init the carousel. 1793 if ( $carousel.children().length > 1 ) { 1794 var carouselOptions = { 1795 rtl : sober.data.isRTL === "1", 1796 dots : true, 1797 items : 1, 1798 loop : false, 1799 autoHeight: !! sober.data.product_gallery_autoheight 1800 }; 1801 1802 if ( $product.hasClass( 'layout-style-5' ) ) { 1803 carouselOptions.autoHeight = false;
1804 carouselOptions.animateOut = 'fadeOut'; 1805 } 1806 1807 if ( $product.hasClass( 'layout-style-4' ) ) { 1808 if ( ! $product.hasClass( 'product-type-variable' ) ) { 1809 carouselOptions.loop = true; 1810 carouselOptions.startPosition = 0; 1811 } else { 1812 carouselOptions.startPosition = ( $carousel.children().length > 2 ? 1 : 0 ); 1813 } 1814 1815 carouselOptions.items = 2; 1816 carouselOptions.margin = 30; 1817 carouselOptions.center = true; 1818 carouselOptions.nav = true; 1819 carouselOptions.navText = ['<svg viewBox="0 0 20 20"><use xlink:href="#left-arrow"></use></svg>', '<svg viewBox="0 0 20 20"><use xlink:href="#right-arrow"></use></svg>']; 1820 carouselOptions.responsive = { 1821 0: { 1822 items: 1, 1823 nav: false 1824 }, 1825 768: { 1826 items: 2, 1827 nav: true 1828 } 1829 }; 1830 carouselOptions.onInitialized = function() { 1831 sober.initZoom( '.woocommerce-product-gallery__slider .woocommerce-product-gallery__image' ); 1832 }; 1833 } 1834 1835 // Toggle carousel for mobile 1836 if ( $product.hasClass( 'layout-style-1' ) || $product.hasClass( 'layout-style-6' ) ) { 1837 carouselOptions.margin = 10; 1838 1839 if ( sober.$window.width() < 992 ) { 1840 if ( ! $carousel.hasClass( 'owl-carousel' ) ) { 1841 $carousel.addClass( 'owl-carousel' ).owlCarousel( carouselOptions ); 1842 } 1843 } else { 1844 $carousel.trigger( 'destroy.owl.carousel' ).removeClass( 'owl-carousel' ); 1845 } 1846 } else if ( ! $carousel.hasClass( 'owl-loaded' ) ) { 1847 // Run the carousel. 1848 $carousel.addClass( 'owl-carousel' ).owlCarousel( carouselOptions ); 1849 } 1850 } 1851 1852 // Show it 1853 $gallery.css( 'opacity', 1 ); 1854 1855 // Use thumbnails as pagination 1856 $gallery.on( 'click', '.thumbnails .woocommerce-product-gallery__image a', function( event ) { 1857 event.preventDefault(); 1858 1859 var $image = $( this ).closest( '.woocommerce-product-gallery__image' ); 1860 1861 $carousel.trigger( 'to.owl.carousel', [$image.index()] ); 1862 } ); 1863 1864 // Change the active state of thumbnails. 1865 $carousel.on( 'translated.owl.carousel', function( event ) { 1866 $thumbnails.children().removeClass( 'active' ).eq( event.item.index ).addClass( 'active' ); 1867 } ); 1868 1869 // Variation form changed 1870 $( '.variations_form:not(.yith-wcpb-bundle-form)', $product ).on( 'reset_image found_variation', function() { 1871 $carousel.trigger( 'to.owl.carousel', [0] ); 1872 } ).on( 'reset_image', function() { 1873 $thumbnails.children( ':eq(0)' ).find( 'img' ).wc_reset_variation_attr( 'src' ); 1874 } ).on( 'found_variation', function( event, variation ) { 1875 $thumbnails.children( ':eq(0)' ).find( 'img' ).wc_set_variation_attr( 'src', variation.image.gallery_thumbnail_src ? variation.image.gallery_thumbnail_src : variation.image.thumb_src ); 1876 } ); 1877 }; 1878 1879 /** 1880 * change product width to be full-width 1881 */ 1882 sober.productFullWidth = function() { 1883 var $product = $( '.woocommerce div.product' ); 1884 1885 if ( ! $product.length ) { 1886 return; 1887 } 1888 1889 var $gallerySummary = $product.find( '.product-summary' ), 1890 $toolbar = $product.find( '.product-toolbar' ), 1891 $header = $( '#masthead' ); 1892 1893 // Set width of product 1894 if ( $product.hasClass( 'layout-style-5' ) || $product.hasClass( 'layout-style-6' ) ) { 1895 setProductWidth(); 1896 1897 sober.$window.on( 'resize load', function() { 1898 setProductWidth(); 1899 } ); 1900 } 1901 1902 // Set top spacing 1903 if ( $product.hasClass( 'layout-style-5' ) ) { 1904 setTopSpacing(); 1905 1906 sober.$window.on( 'resize load', function() { 1907 setTopSpacing(); 1908 } ); 1909 } 1910 1911 /** 1912 * Change the product width 1913 */ 1914 function setProductWidth() { 1915 var width = sober.$window.width(); 1916 1917 $gallerySummary.width( width ); 1918 $toolbar.width( width ); 1919 1920 if ( sober.data.isRTL ) { 1921 $gallerySummary.css( 'marginRight', -width/2 ); 1922 $toolbar.css( 'marginRight', -width/2 ); 1923 } else { 1924 $gallerySummary.css( 'marginLeft', -width/2 ); 1925 $toolbar.css( 'marginLeft', -width/2 ); 1926 } 1927 } 1928 1929 function setTopSpacing() { 1930 $gallerySummary.css( 'paddingTop', $header.outerHeight() ); 1931 } 1932 }; 1933 1934 /** 1935 * Set product background 1936 */ 1937 sober.productBackground = function() { 1938 if ( typeof BackgroundColorTheif == 'undefined' ) { 1939 return; 1940 } 1941 1942 if ( ! sober.data.product_gallery_autobg ) { 1943 return; 1944 } 1945 1946 var $product = $( '.woocommerce div.product' ); 1947 1948 if ( ! $product.hasClass( 'layout-style-5' ) ) { 1949 return; 1950 } 1951 1952 var $gallery = $product.find( 'div.images' ), 1953 $carousel = $gallery.find( '.woocommerce-product-gallery__slider' ), 1954 $image = $gallery.find( '.wp-post-image' ),
1955 imageColor = new BackgroundColorTheif(); 1956 1957 // Change background base on main image. 1958 if ( ! $product.hasClass( 'background-set' ) ) { 1959 $image.one( 'load', function() { 1960 setTimeout( function() { 1961 changeProductBackground( $image.get( 0 ) ); 1962 }, 100 ); 1963 } ).each( function() { 1964 if ( this.complete ) { 1965 $( this ).trigger( 'load' ); 1966 } 1967 } ); 1968 1969 // Support Jetpack images lazy loads. 1970 $gallery.on( 'jetpack-lazy-loaded-image', '.wp-post-image', function() { 1971 $( this ).one( 'load', function() { 1972 changeProductBackground( this ); 1973 } ); 1974 } ); 1975 } 1976 1977 // Change background when slider change. 1978 if ( ! $product.hasClass( 'background-set-all' ) ) { 1979 $carousel.on( 'translate.owl.carousel', function( event ) { 1980 setTimeout( function() { 1981 changeProductBackground( $carousel.find( '.owl-item' ).eq( event.item.index ).find( 'img' ).get( 0 ) ); 1982 }, 150 ); 1983 } ); 1984 1985 // Change background when variation changed 1986 $gallery.on( 'woocommerce_gallery_reset_slide_position', function() { 1987 changeProductBackground( $image.get( 0 ) ); 1988 } ); 1989 } 1990 1991 /** 1992 * Change product backgound color 1993 */ 1994 function changeProductBackground( image ) { 1995 // Stop if this image is not loaded. 1996 if ( typeof image === 'undefined' || image.src === '' ) { 1997 return; 1998 } 1999 2000 if ( image.classList.contains( 'jetpack-lazy-image' ) ) { 2001 if ( ! image.dataset['lazyLoaded'] ) { 2002 return; 2003 } 2004 } 2005 2006 var rgb = imageColor.getBackGroundColor( image ); 2007 $product.find( '.product-summary' ).get( 0 ).style.backgroundColor = 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')'; 2008 } 2009 }; 2010 2011 /** 2012 * Display related products as a carousel 2013 */ 2014 sober.relatedProductsCarousel = function() { 2015 if ( ! sober.data.related_products_carousel ) { 2016 return; 2017 } 2018 2019 var $relatedProducts = $( '.related.products ul.products' ); 2020 2021 if ( ! $relatedProducts.length ) { 2022 return; 2023 } 2024 2025 var columns = 4; 2026 2027 if ( sober.$body.hasClass( 'product-style-1' ) || sober.$body.hasClass( 'product-style-2' ) ) { 2028 columns = 5; 2029 } 2030 2031 $relatedProducts.addClass( 'owl-carousel' ).owlCarousel( { 2032 rtl : sober.data.isRTL === "1", 2033 items : columns, 2034 loop : false, 2035 dots: true, 2036 nav: false, 2037 responsive: { 2038 0: { 2039 items: 1 2040 }, 2041 320: { 2042 items: 2 2043 }, 2044 768: { 2045 items: 3 2046 }, 2047 1024: { 2048 items: 4 2049 }, 2050 1366: { 2051 items: columns 2052 } 2053 } 2054 } ); 2055 }; 2056 2057 /** 2058 * Display upsell products as a carousel. 2059 * This is a normal carousel for all product layouts except layout v3. 2060 */ 2061 sober.upsellProductsCarousel = function() { 2062 if ( ! sober.data.upsell_products_carousel ) { 2063 return; 2064 } 2065 2066 var $upsellProducts = $( '.upsells.products ul.products' ); 2067 2068 if ( ! $upsellProducts.length ) { 2069 return; 2070 } 2071 2072 var columns = 4; 2073 2074 if ( sober.$body.hasClass( 'product-style-1' ) || sober.$body.hasClass( 'product-style-2' ) ) { 2075 columns = 5; 2076 } 2077 2078 $upsellProducts.addClass( 'owl-carousel' ).owlCarousel( { 2079 rtl : sober.data.isRTL === "1", 2080 items : columns, 2081 loop : false, 2082 dots: true, 2083 nav: false, 2084 responsive: { 2085 0: { 2086 items: 1 2087 }, 2088 320: { 2089 items: 2 2090 }, 2091 768: { 2092 items: 3 2093 }, 2094 1024: { 2095 items: 4 2096 }, 2097 1366: { 2098 items: columns 2099 } 2100 } 2101 } ); 2102 }; 2103 2104 /** 2105 * Zoom product image when hover 2106 */ 2107 sober.productImageZoom = function() { 2108 $( '.product-thumbnail-zoom' ).each( function () { 2109 var $el = $( this ); 2110 2111 $el.zoom( { 2112 url: $el.attr( 'data-zoom_image' ) 2113 } ); 2114 } ); 2115 2116 sober.$body.on( 'soo_filter_request_success', function() { 2117 $( '.product-thumbnail-zoom', '.archive.woocommerce #primary ul.products' ).each( function () { 2118 var $el = $( this ); 2119 2120 $el.zoom( { 2121 url: $el.attr( 'data-zoom_image' ) 2122 } ); 2123 } ); 2124 } ); 2125 2126 sober.$body.on( 'sober_products_loaded', function( event, products ) { 2127 $( products ).imagesLoaded( function() { 2128 var $thumbnails = $( '.product-thumbnail-zoom', products ); 2129 2130 if ( $thumbnails.length ) { 2131 $thumbnails.each( function() { 2132 var $el = $( this ); 2133 2134 $el.zoom( { 2135 url: $el.attr( 'data-zoom_image' ) 2136 } ); 2137 } ); 2138 } 2139 } ); 2140 } ); 2141 }; 2142 2143 /** 2144 * Display product thumbnail images as slider 2145 */ 2146 sober.productImageSlider = function() { 2147 var $products = $( 'ul.products' ); 2148 2149 $products.each( function() { 2150 if ( ! $( this ).parents().is( '.side-products' ) ) {
2151 sober.initProductThumbnailSlider( this ); 2152 } 2153 } ); 2154 2155 // Fix problem with the product carousel element 2156 $( '.sober-product-carousel' ).on( 'initialized.owl.carousel', function() { 2157 $( '.product-images__slider' ).trigger( 'refresh.owl.carousel' ); 2158 } ); 2159 2160 // Fix problem with Soo Filter 2161 sober.$body.on( 'soo_filter_request_success', function() { 2162 sober.initProductThumbnailSlider( $( '.archive.woocommerce #primary ul.products' ) ); 2163 } ); 2164 2165 sober.$body.on( 'sober_products_loaded', function( event, products ) { 2166 sober.initProductThumbnailSlider( products ); 2167 } ); 2168 }; 2169 2170 /** 2171 * Init product thumbnail slider 2172 */ 2173 sober.initProductThumbnailSlider = function( products ) { 2174 $( products ).imagesLoaded( function() { 2175 $( '.product-images__slider', products ).owlCarousel( { 2176 items: 1, 2177 lazyLoad: true, 2178 dots: false, 2179 nav: true, 2180 loop: !! sober.data.product_card_slider_loop, 2181 rtl: sober.data.isRTL === "1", 2182 navText: ['<svg viewBox="0 0 14 20"><use xlink:href="#left"></use></svg>', '<svg viewBox="0 0 14 20"><use xlink:href="#right"></use></svg>'] 2183 } ).on( 'resize.owl.carousel initialized.owl.carousel', function() { 2184 $( '.woocommerce.archive .site-content ul.products' ).isotope( 'layout' ); 2185 } ); 2186 } ); 2187 }; 2188 2189 /** 2190 * Ajaxify add to cart button on single product page 2191 */ 2192 sober.singeProductAjaxAddToCart = function () { 2193 if ( 'yes' !== sober.data.single_ajax_add_to_cart ) { 2194 return; 2195 } 2196 2197 var alertTimeout = null; 2198 2199 sober.$body.on( 'submit', 'form.cart', function( event ) { 2200 var $form = $( this ); 2201 2202 if ( $form.closest( 'div.product' ).hasClass( 'product-type-external' ) || $form.data( 'with_ajax' ) ) { 2203 return; 2204 } 2205 2206 event.preventDefault(); 2207 2208 var $button = $form.find( '.single_add_to_cart_button' ), 2209 formData = new FormData( this ); 2210 2211 // Prevent double-add by remove the param 'add-to-cart'. 2212 if ( formData.has( 'add-to-cart' ) ) { 2213 formData.delete( 'add-to-cart' ); 2214 } 2215 2216 // Then we will rename it in the ajax handler function. 2217 formData.append( 'sober-add-to-cart', $form.find( '[name="add-to-cart"]' ).val() ); 2218 2219 // Ajax. 2220 $.ajax( { 2221 url: woocommerce_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'sober_ajax_add_to_cart' ), 2222 data: formData, 2223 method: 'POST', 2224 contentType: false, 2225 processData: false, 2226 beforeSend: function() { 2227 $button.removeClass( 'added' ).addClass( 'loading disabled' ).prop( 'disabled', true ); 2228 }, 2229 complete: function( response ) { 2230 $button.removeClass( 'loading disabled' ).addClass( 'added' ).prop( 'disabled', false ); 2231 2232 response = response.responseJSON; 2233 2234 if ( ! response ) { 2235 return; 2236 } 2237 2238 // Reload on error. 2239 if ( response.error && response.product_url ) { 2240 window.location = response.product_url; 2241 return; 2242 } 2243 2244 // Redirect to cart option. 2245 if ( 'undefined' !== typeof wc_add_to_cart_params && wc_add_to_cart_params.cart_redirect_after_add === 'yes' ) { 2246 window.location = wc_add_to_cart_params.cart_url; 2247 return; 2248 } 2249 2250 // Trigger the default event to refresh fragments. 2251 sober.$body.trigger( 'added_to_cart', [ response.fragments, response.cart_hash, $button ] ); 2252 2253 // Trigger the fragment refresh event manually if necessary. 2254 if ( 'undefined' === typeof wc_add_to_cart_params ) { 2255 sober.$body.trigger( 'wc_fragment_refresh' ); 2256 } 2257 2258 // Display new notices (alert bar). 2259 if ( sober.data.open_cart_modal_after_add !== '1' && response.fragments.notices_html ) { 2260 var $alert = $( '.sober-alert-bar' ); 2261 2262 if ( ! $alert.length ) { 2263 $alert = $( '<div class="sober-alert-bar"></div>' ); 2264 sober.$body.append( $alert ); 2265 } 2266 2267 $alert.html( response.fragments.notices_html ); 2268 setTimeout( function() { 2269 $alert.addClass( 'active' ); 2270 }, 100 ); 2271 2272 // Auto hide the alert bar after 5 seconds. Apply for success actions only. 2273 if ( $alert.find( '.woocommerce-message' ).length ) { 2274 clearTimeout( alertTimeout ); 2275 alertTimeout = setTimeout( function() { 2276 $alert.removeClass( 'active' ); 2277 }, 5000 ); 2278 } 2279 } 2280 }, 2281 error: function() { 2282 // Try to submit the form without ajax on errors. 2283 $form.data( 'with_ajax', false ).trigger( 'submit' ); 2284 } 2285 } ); 2286 } ); 2287 2288 // Close alert bar 2289 sober.$body.on( 'click', '.sober-alert-bar .close', function ( e ) { 2290 e.preventDefault(); 2291 2292 $( this ).closest( '.sober-alert-bar' ).removeClass( 'active' ); 2293 } ); 2294 }; 2295 2296 /** 2297 * Init sticky cart form. 2298 * 2299 * @todo Support bundled products. 2300 */ 2301 sober.stickyAddToCart = function() {
2302 var $sticky = $( '.sticky-cart-form' ); 2303 2304 if ( ! $sticky.length ) { 2305 return; 2306 } 2307 2308 // Remove wishlist and share buttons. 2309 $sticky.find( '.wcboost-wishlist-button, .add-to-wishlist-button, .product-share' ).remove(); 2310 2311 var $forms = $( 'form.cart', $sticky.closest( 'div.product' ) ), 2312 $originalForm = $forms.first(), 2313 $image = $sticky.find( '.sticky-cart-form__product-image img' ), 2314 $price = $sticky.find( '.sticky-cart-form__product-summary .price' ), 2315 $stickyHeader = $( '#masthead' ), 2316 offset = 0, 2317 doingSync = false; 2318 2319 if ( $stickyHeader.hasClass( 'header-v10' ) ) { 2320 $stickyHeader = $stickyHeader.find( '.header-main .header-right-items' ); 2321 } 2322 2323 if ( sober.data.sticky_header && 'none' !== sober.data.sticky_header ) { 2324 offset = $stickyHeader.height(); 2325 } 2326 2327 // Handle add-to-cart variations of 2 forms. 2328 $forms 2329 .on( 'reset_data', function() { 2330 $image.attr( 'src', $image.data( 'o_src' ) ); 2331 $price.show().siblings( '.variation-price, .stock' ).remove(); 2332 } ) 2333 .on( 'found_variation', function( event, variation ) { 2334 if ( variation.image && variation.image.gallery_thumbnail_src && variation.image.gallery_thumbnail_src.length > 1 ) { 2335 $image.attr( 'src', variation.image.gallery_thumbnail_src ); 2336 } 2337 2338 if ( variation.availability_html && variation.availability_html.length ) {
2339 $price.hide().siblings( '.stock, .variation-price' ).remove(); 2340 $price.after( variation.availability_html ); 2341 } else { 2342 $price.siblings( '.stock' ).remove(); 2343 2344 if ( variation.price_html && variation.price_html.length ) { 2345 $price.hide().siblings( '.variation-price' ).remove(); 2346 $price.after( $( variation.price_html ).addClass( 'variation-price' ) ); 2347 } 2348 } 2349 } ) 2350 // Sync inputs' value. 2351 .on( 'change', ':input', function() { 2352 // Avoid infinite loop. 2353 if ( doingSync ) { 2354 return; 2355 } 2356 2357 doingSync = true; 2358 2359 var $currentForm = $( this ).closest( 'form.cart' ), 2360 $targetForm = $forms.not( $currentForm ); 2361 2362 $targetForm.find( ':input[name="' + this.name + '"]' ).val( this.value ).trigger( 'change' ); 2363 2364 doingSync = false; 2365 } ); 2366 2367 // Scroll to the grouped products form. 2368 $sticky.on( 'click', '.grouped_form .single_add_to_cart_button, .bundle_form .single_add_to_cart_button', function() { 2369 $( 'html, body' ).animate( { scrollTop: $originalForm.offset().top - offset }, 800 ); 2370 2371 return false; 2372 } ).on( 'click', '.sticky-cart-form__mobile-button', function( event ) { 2373 event.preventDefault(); 2374 2375 if ( this.dataset.product_type === 'simple' || this.dataset.product_type === 'grouped' || this.dataset.product_type === 'external' ) { 2376 $sticky.find( '.single_add_to_cart_button' ).trigger( 'click' ); 2377 } else { 2378 $( 'html, body' ).animate( { scrollTop: $originalForm.offset().top - offset }, 800 ); 2379 } 2380 } ); 2381 2382 // Handle the scroll event. 2383 var lastKnownScrollPosition = window.scrollY, 2384 ticking = false, 2385 rAF = window.requestAnimationFrame || 2386 window.webkitRequestAnimationFrame || 2387 window.mozRequestAnimationFrame || 2388 window.msRequestAnimationFrame || 2389 window.oRequestAnimationFrame; 2390 2391 var checkFormVisibility = function( position ) { 2392 if ( position >= ( $originalForm.offset().top + $originalForm.height() - offset ) ) { 2393 $sticky.attr( 'aria-hidden', false ).removeClass( 'sticky-cart-form--unpin' ).addClass( 'sticky-cart-form--pin' ); 2394 2395 if ( 'top' === $sticky.data( 'position' ) && sober.data.sticky_header && 'none' !== sober.data.sticky_header ) { 2396 $stickyHeader.addClass( 'sticky--ignore' ); 2397 } 2398 } else { 2399 $sticky.attr( 'aria-hidden', true ).removeClass( 'sticky-cart-form--pin' ).addClass( 'sticky-cart-form--unpin' ); 2400 2401 if ( 'top' === $sticky.data( 'position' ) && sober.data.sticky_header && 'none' !== sober.data.sticky_header ) { 2402 $stickyHeader.removeClass( 'sticky--ignore' ); 2403 } 2404 } 2405 }; 2406 2407 // Check and toggle the sticky add-to-cart form on scroll. 2408 document.addEventListener( 'scroll', function() { 2409 lastKnownScrollPosition = window.scrollY; 2410 2411 if ( ! ticking ) { 2412 rAF( function() { 2413 checkFormVisibility( lastKnownScrollPosition ); 2414 ticking = false; 2415 } ); 2416 2417 ticking = true; 2418 } 2419 } ); 2420 2421 rAF( function() { 2422 checkFormVisibility( lastKnownScrollPosition ); 2423 ticking = false; 2424 } ); 2425 } 2426 2427 /** 2428 * Open the cart modal after successful addition 2429 */ 2430 sober.openCartModalAfterAdd = function () { 2431 if ( sober.data.open_cart_modal_after_add !== '1' ) { 2432 return; 2433 } 2434 2435 sober.$body.on( 'added_to_cart', function () { 2436 if ( $( '#cart-modal' ).hasClass( 'open' ) ) { 2437 return; 2438 } 2439 2440 sober.closeModal(); 2441 sober.openModal( '#cart-modal', 'cart' ); 2442 } ); 2443 }; 2444 2445 /** 2446 * Init parallax for page header elements 2447 */ 2448 sober.pageHeaderParallax = function () { 2449 if ( typeof Rellax === 'undefined' ) { 2450 return; 2451 } 2452 2453 var $title = $( '.page-header .page-title' ), 2454 $breadcrumb = $( '.page-header .breadcrumb' ); 2455 2456 if ( 'up' === sober.data.page_header_parallax ) { 2457 if ( $title.length ) { 2458 new Rellax( '.page-header .page-title', {speed: 1} ); 2459 } 2460 if ( $breadcrumb.length ) { 2461 new Rellax( '.page-header .breadcrumb', {speed: 2} ); 2462 } 2463 } else if ( 'down' === sober.data.page_header_parallax ) { 2464 if ( $title.length ) { 2465 new Rellax( '.page-header .page-title', {speed: -2} ); 2466 } 2467 if ( $breadcrumb.length ) { 2468 new Rellax( '.page-header .breadcrumb', {speed: -1} ); 2469 } 2470 } 2471 }; 2472 2473 /** 2474 * Add page preloader 2475 */ 2476 sober.preload = function () { 2477 var $preloader = $( '#preloader' ); 2478 2479 if ( ! $preloader.length ) { 2480 return; 2481 } 2482 2483 var ignorePreloader = false;
2484 2485 sober.$body.on( 'click', 'a[href^=mailto], a[href^=tel]', function() { 2486 ignorePreloader = true; 2487 } ); 2488 2489 sober.$window.on( 'beforeunload', function() { 2490 if ( ! ignorePreloader ) { 2491 $preloader.fadeIn( 'slow' ); 2492 } 2493 2494 ignorePreloader = false; 2495 } ); 2496 2497 $preloader.fadeOut( 800 ); 2498 2499 window.onpageshow = function( event ) { 2500 if ( event.persisted ) { 2501 $preloader.fadeOut( 800 ); 2502 } 2503 }; 2504 }; 2505 2506 /** 2507 * Call functions for responsiveness 2508 */ 2509 sober.responsive = function () { 2510 // Initialize fitvids 2511 sober.responsiveVideos(); 2512 2513 // Init/destroy product image carousel for product style 1 2514 sober.$window.on( 'resize', sober.productGallerySlider ); 2515 2516 // Close the filter panel on success. 2517 sober.$body.on( 'sober_products_filter_request_success', function() { 2518 if ( sober.$window.width() < 768 ) { 2519 sober.closeProductFilter(); 2520 } 2521 } ) 2522 }; 2523 2524 /** 2525 * Responsive videos 2526 */ 2527 sober.responsiveVideos = function () { 2528 sober.$body.fitVids(); 2529 }; 2530 2531 /** 2532 * Close the filter p 2533 */ 2534 sober.closeProductFilter = function() { 2535 $( '.shop-toolbar .filter-widgets.active' ).fadeOut( function () { 2536 $( this ).removeClass( 'active' ); 2537 } ); 2538 } 2539 2540 /** 2541 * Open popup 2542 */ 2543 sober.popup = function() { 2544 var days = parseInt( sober.data.popup_frequency ), 2545 delay = parseInt( sober.data.popup_visible_delay ); 2546 2547 if ( days > 0 && document.cookie.match( /^(.*;)?\s*sober_popup\s*=\s*[^;]+(.*)?$/ ) ) { 2548 return; 2549 } 2550 2551 delay = Math.max( delay, 0 ); 2552 delay = 'delay' === sober.data.popup_visible ? delay : 0; 2553 2554 sober.$window.on( 'load', function() { 2555 setTimeout( function() { 2556 sober.openModal( '#popup' ); 2557 }, delay * 1000 ); 2558 } ); 2559 2560 sober.$body.on( 'sober_modal_closed', function (event, modal) { 2561 if ( ! modal.hasClass( 'sober-popup' ) ) { 2562 return; 2563 } 2564 2565 var date = new Date(), 2566 value = date.getTime(); 2567 2568 date.setTime( date.getTime() + (days * 24 * 60 * 60 * 1000) ); 2569 2570 document.cookie = 'sober_popup=' + value + ';expires=' + date.toGMTString() + ';path=/'; 2571 } ); 2572 2573 // Support third party to close the popup. 2574 sober.$body.on( 'click', '.sober-popup .close-popup-trigger', function() { 2575 sober.closeModal(); 2576 } ); 2577 }; 2578 2579 /** 2580 * Show a notification after added a product to cart successfully 2581 */ 2582 sober.addedToCartNotice = function() { 2583 if ( ! sober.data.added_to_cart_notice || ! $.fn.notify ) { 2584 return; 2585 } 2586 2587 $.notify.addStyle( 'sober', { 2588 html: '<div><svg viewBox="0 0 20 20" class="message-icon"><use xlink:href="#success" href="#success"></use></svg><span data-notify-text/></div>' 2589 }); 2590 2591 $( document ).on( 'added_to_cart', function() { 2592 $.notify( sober.data.l10n.added_to_cart_notice, { 2593 autoHideDelay: 2000, 2594 className: 'success', 2595 style: 'sober', 2596 showAnimation: 'fadeIn', 2597 hideAnimation: 'fadeOut' 2598 } ); 2599 } ); 2600 }; 2601 2602 /** 2603 * Turn off the event handler for terms and conditions link 2604 */
2605 sober.checkoutTerms = function() { 2606 sober.$body.off( 'click', 'a.woocommerce-terms-and-conditions-link' ); 2607 }; 2608 2609 /** 2610 * Fix row full width in RTL 2611 */ 2612 sober.fixVC = function() { 2613 if ( sober.data.isRTL !== '1' ) { 2614 return; 2615 } 2616 2617 $( document ).on( 'vc-full-width-row-single', function( event, data ) { 2618 data.el.css( { 2619 left: 'auto', 2620 right: data.offset 2621 } ); 2622 } ); 2623 }; 2624 2625 /** 2626 * Ajax login before refresh page 2627 */ 2628 sober.loginModalAuthenticate = function() { 2629 $( '#login-modal' ).on( 'submit', '.woocommerce-form-login', function( e ) { 2630 var username = $( 'input[name=username]', this ).val(), 2631 password = $( 'input[name=password]', this ).val(), 2632 remember = $( 'input[name=rememberme]', this ).is( ':checked' ), 2633 nonce = $( 'input[name=woocommerce-login-nonce]', this ).val(), 2634 $button = $( '[type=submit]', this ), 2635 $form = $( this ), 2636 $box = $form.next( '.woocommerce-error' ); 2637 2638 if ( ! username ) { 2639 $( 'input[name=username]', this ).focus(); 2640 2641 return false; 2642 } 2643 2644 if ( ! password ) { 2645 $( 'input[name=password]', this ).focus(); 2646 2647 return false; 2648 } 2649 2650 if ( $form.data( 'validated' ) ) { 2651 return true; 2652 } 2653 2654 $button.addClass( 'loading' ); 2655 2656 if ( $box.length ) { 2657 $box.fadeOut(); 2658 } 2659 2660 $.post( 2661 sober.data.ajaxurl, 2662 { 2663 action: 'sober_login_authenticate', 2664 username: username, 2665 password: password, 2666 remember: remember, 2667 security: nonce, 2668 }, 2669 function( response ) { 2670 if ( ! response.success ) { 2671 if ( ! $box.length ) { 2672 $box = $( '<div class="woocommerce-error sober-message-box danger"/>' ); 2673 2674 $box.append( '<svg viewBox="0 0 20 20" class="message-icon"><use xlink:href="#warning"></use></svg>' ) 2675 .append( '<div class="box-content"></div>' ) 2676 .append( '<a class="close" href="#"><svg viewBox="0 0 14 14"><use xlink:href="#close-delete-small"></use></svg></a>' ); 2677 2678 $box.hide().insertAfter( $form ); 2679 } 2680 2681 $box.find( '.box-content' ).html( response.data ); 2682 $box.fadeIn(); 2683 $button.removeClass( 'loading' ); 2684 } else { 2685 // Need a valid data to pass the login from validation. 2686 $button.before( '<input type="hidden" name="login" value="' + $button.attr( 'value' ) + '" />' ); 2687 $form.data( 'validated', true ).submit(); 2688 } 2689 } 2690 ); 2691 2692 e.preventDefault(); 2693 } ); 2694 }; 2695 2696 /** 2697 * Support lazy load images 2698 */ 2699 sober.lazyLoadImages = function() { 2700 sober.$body.on( 'post-load sober_products_loaded soo_filter_request_success sober_quickview_opened', function() { 2701 // Create a custom event for Jetpack lazy loads 2702 var lazyLoadImagesEvent; 2703 2704 try { 2705 lazyLoadImagesEvent = new Event( 'jetpack-lazy-images-load', { 2706 bubbles: true, 2707 cancelable: true, 2708 } ); 2709 } catch ( e ) { 2710 lazyLoadImagesEvent = document.createEvent( 'Event' ); 2711 lazyLoadImagesEvent.initEvent( 'jetpack-lazy-images-load', true, true ); 2712 } 2713 2714 setTimeout( function() { 2715 document.body.dispatchEvent( lazyLoadImagesEvent ); 2716 }, 100 ); 2717 } ); 2718 }; 2719 2720 /** 2721 * Close WC notices 2722 */ 2723 sober.closeNotices = function() { 2724 sober.$body.on( 'click', '.wc-block-components-notice-banner__close', function( event ) { 2725 event.preventDefault(); 2726 2727 $( this ).closest( '.wc-block-components-notice-banner' ).fadeOut( 'slow' ); 2728 } ); 2729 } 2730 2731 /** 2732 * Document ready 2733 */ 2734 $( function () { 2735 sober.init(); 2736 } ); 2737})( 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.