1/** 2 * Frontend 3 * 4 * @author YITH <[email protected]> 5 * @package YITH WooCommerce Colors and Labels Variations Premium 6 * @version 1.0.0 7 */ 8 9;(function( $, window, document ){ 10 11 if ( typeof yith_wccl_general === 'undefined' ) 12 return false; 13 14 /** 15 * Matches inline variation objects to chosen attributes and return variation 16 * @type {Object} 17 */ 18 var variations_match = function( form, value, current_attribute_name ) { 19 var match = false, 20 product_variations = form.data( 'product_variations' ), 21 all_select = form.find( '.variations select' ), 22 show_specific_attr = true; 23 settings = []; 24 25 // current selected values 26 $.each( all_select, function(){ 27 var attribute_name = $( this ).data( 'attribute_name' ) || $( this ).attr( 'name' ); 28 if( current_attribute_name == attribute_name ) { 29 settings[attribute_name] = value; 30 } 31 else { 32 if( $(this).val() !== '' ) { 33 settings[attribute_name] = $(this).val(); 34 } 35 } 36 }); 37 38 for ( var i = 0; i < product_variations.length; i++ ) { 39 var variation = product_variations[i]; 40 41 // if found matching variation exit 42 if( match ) { 43 break; 44 } 45 var attr = ( show_specific_attr ) ? settings : variation.attributes; 46 if( Object.keys(attr).length >= 1 ) { 47 match = variation; 48 for (var attr_name in attr) { 49 if (variation.attributes.hasOwnProperty(attr_name) ) { 50 var val1 = variation.attributes[attr_name], 51 val2 = settings[attr_name]; 52 if (val1 != val2 && attr[attr_name] != '' && val1 !='') { 53 match = false; 54 } 55 } 56 } 57 } 58 } 59 return match; 60 }; 61 62 /** 63 * Add to cart variation loop 64 * @param event 65 */ 66 var yith_wccl_add_cart = function( event ){ 67 68 event.preventDefault(); 69 70 var b = $( this ), 71 product_id = b.data( 'product_id' ), 72 quantity = b.data( 'quantity' ), 73 attr = [], 74 data = {}; 75 76 $.each( b.data(), function( key, value ) { 77 data[ key ] = value; 78 }); 79 80 // get select value 81 event.data.select.each( function(index){ 82 attr[ index ] = this.name + '=' + this.value; 83 }); 84 85 // Trigger event. 86 $( document.body ).trigger( 'adding_to_cart', [ b, data ] ); 87 88 $.ajax({ 89 url: yith_wccl_general.ajaxurl.toString().replace( '%%endpoint%%', yith_wccl_general.actionAddCart ), 90 type: 'POST', 91 data: { 92 action: yith_wccl_general.actionAddCart, 93 product_id : product_id, 94 variation_id : event.data.variation, 95 attr: attr.join('&'), 96 quantity: quantity, 97 context: 'frontend' 98 }, 99 beforeSend: function(){ 100 b.addClass( 'loading').removeClass( 'added' ); 101 }, 102 success: function( res ){ 103 104 // redirect to product page if some error occurred 105 if ( res.error && res.product_url ) { 106 window.location = res.product_url; 107 return; 108 } 109 // redirect to cart 110 if ( yith_wccl_general.cart_redirect ) { 111 window.location = yith_wccl_general.cart_url; 112 return; 113 } 114 115 // change button 116 b.removeClass('loading').addClass('added'); 117 118 if( ! b.next('.added_to_cart').length ) { 119 b.after(' <a href="' + yith_wccl_general.cart_url + '" class="added_to_cart wc-forward" title="' + yith_wccl_general.view_cart + '">' + yith_wccl_general.view_cart + '</a>'); 120 } 121 122 $( document.body ).trigger( 'wc_fragment_refresh' ); 123 // trigger refresh also cart page 124 $( document ).trigger( 'wc_update_cart' ); 125 126 // added to cart 127 $( document.body ).trigger( 'added_to_cart', [ res.fragments, res.cart_hash, b ] ); 128 } 129 }); 130 } 131 132 var yith_wccl_change_label = function (label, t) { 133 134 t.closest('tr').find('th label').text(label); 135 }; 136 137 /** 138 * 139 * @param $form 140 * @param attr 141 * @constructor 142 */ 143 var WCCL = function( $form, attr ) { 144 145 this.$form = $form; 146 this.$attr = ( typeof yith_wccl != 'undefined' ) ? JSON.parse( yith_wccl.attributes ) : attr; 147 this.$select = this.$form.find( '.variations select' ); 148 this.$use_ajax = this.$form.data( 'product_variations' ) === false; 149 this.$attr_number = this.$form.data('number-variation-attributes'); 150 // variables for loop 151 this.$is_loop = this.$form.hasClass('in_loop'); 152 this.$wrapper = this.$form.closest( yith_wccl_general.wrapper_container_shop ).length ? this.$form.closest( yith_wccl_general.wrapper_container_shop ) : this.$form.closest('.product-add-to-cart' ); 153 this.$image = this.$wrapper.find( yith_wccl_general.image_selector ).first(); 154 this.$def_image_src = ( this.$image.data('lazy-src') ) ? this.$image.data('lazy-src') : this.$image.attr( 'src' ); 155 this.$def_image_srcset = ( this.$image.data('lazy-srcset') ) ? this.$image.data('lazy-srcset') : this.$image.attr( 'srcset' ); 156 this.$price_html = this.$wrapper.find( yith_wccl_general.wrapper_price_shop ).clone().wrap('<p>').parent().html(); 157 this.$button = this.$wrapper.find( yith_wccl_general.add_to_cart_selector_loop ); 158 this.$button_html = this.$button.html(); 159 this.$input_qty = this.$wrapper.find('input.thumbnail-quantity'); 160 this.$xhr = null; 161 this.variations_gallery = []; // store variations gallery to improve performace 162 163 // prevent undefined attr error 164 if( typeof this.$attr == 'undefined' ) { 165 this.$attr = []; 166 } 167 168 $form.on( 'yith_wccl_form_initialized', { obj: this }, this.init ); 169 170 // get default value 171 this.$select.each( function() { 172 this.setAttribute( 'data-default_value', this.value ); 173 }); 174 175 // reset form and select 176 this.resetForm( this ); 177 178 if( this.$is_loop ) { 179 $form.parent().on( 'change', function(e) { e.stopPropagation(); }); 180 } 181 182 // hide input qty if present 183 if( this.$input_qty.length ) 184 this.$input_qty.hide(); 185 186 if( ! this.$form.hasClass( 'initialized' ) ) { 187 this.$form.addClass('initialized').fadeIn().trigger( 'yith_wccl_form_initialized' ); 188 } 189 }; 190 191 WCCL.prototype.styleOption = function( obj, option, type, value ) { 192 193 const regExp = /^(ftp|http|https):\/\/[^ "]+$/; 194 195 if( regExp.test(value ) && 'image' !== type ) { 196 type = 'image'; 197 } 198 199 switch ( type ) { 200 201 case 'colorpicker': 202 value = value.split(','); 203 204 if( value.length == 1 ) { 205 /*option.append($('<span/>', { 206 'class': 'yith_wccl_value', 207 'css': { 208 'background': value 209 } 210 }));*/ 211 option.append($('<div class="yith_wccl_value_wrapper"><span class="yith_wccl_value"></span></div>')); 212 option.find('.yith_wccl_value').css({ 213 'background': value, 214 }); 215 } else { 216 option.append($('<div class="yith_wccl_value_wrapper"><span class="yith_wccl_value"><span class="yith-wccl-bicolor"/></span></div>')); 217 option.find('.yith-wccl-bicolor').css({ 218 'background-color': value[0], 219 'border-color': value[1] 220 }); 221 222 } 223 break; 224 case 'image' : 225 /*option.append($('<img/>', { 226 'class': 'yith_wccl_value', 227 'src': value, 228 'alt':type 229 }));*/ 230 option.append($('<div class="yith_wccl_value_wrapper"><img class="yith_wccl_value"/></div>')); 231 option.find('.yith_wccl_value').attr('src',value).attr('alt',type); 232 break; 233 234 case 'label' : 235 /*option.append($('<span/>', { 236 'class': 'yith_wccl_value', 237 'text': value 238 }));*/ 239 option.append($('<div class="yith_wccl_value_wrapper"><span class="yith_wccl_value"></span></div>')); 240 option.find('.yith_wccl_value').text(value); 241 break; 242 243 } 244 }; 245 246 WCCL.prototype.addTooltip = function( obj, tooltip, option, type, value, tooltip_image = '' ) { 247 248 var tooltip_wrapper = $('<span class="yith_wccl_tooltip"></span>'), 249 classes = yith_wccl_general.tooltip_pos + ' ' + yith_wccl_general.tooltip_ani; 250 251 if( ! yith_wccl_general.tooltip || typeof tooltip == 'undefined' || ( ! tooltip && tooltip_image === '' ) || option.find( '.yith_wccl_tooltip' ).length ) { 252 return; 253 } 254 255 if( type == 'image' ) { 256 tooltip = tooltip.toString().replace('{show_image}', '<img src="' + value + '" />'); 257 } 258 if( tooltip_image !== '' ) {
259 color_image = '<img src="' + tooltip_image + '" />'; 260 tooltip = color_image + tooltip.toString(); 261 } 262 263 tooltip_wrapper.addClass( classes ); 264 option.append( tooltip_wrapper.html( '<span class="yith-wccl-tooltip-image">' + tooltip + '</span>' ) ); 265 }; 266 267 WCCL.prototype.handleSelect = function( event ) { 268 269 var obj = event.data.obj; 270 var chosenAttributes= {}; 271 272 // First loop selectors for know the current chosen attributes. 273 obj.$select.each( function() { 274 var p = $(this); 275 chosenAttributes[this.name] = p.val(); 276 }); 277 //Display C&L attributes. 278 obj.$select.each( function() { 279 var t = $(this), 280 current_attr = obj.$attr[ this.name ], 281 decoded_name = decodeURIComponent( this.name ), 282 select_box = t.parent().find( '.select_box' ), 283 current_option = [], 284 default_label = t.closest('tr').find('th label').data('default-label'), 285 selectParent; 286 287 // Set select parent. 288 selectParent = t.closest('.select-wrapper'); 289 if ( ! selectParent.length ) { 290 selectParent = t.closest('td'); 291 if ( ! selectParent.length ) { 292 selectParent = t.parent(); 293 } 294 } 295 296 if( typeof current_attr == 'undefined' ) { 297 current_attr = obj.$attr[ decoded_name ]; 298 } 299 // Add description 300 if ( yith_wccl_general.description && ! obj.$is_loop && ! obj.$wrapper.length && ! obj.$form.find( '.description_' + decoded_name ).length 301 && typeof current_attr != 'undefined' && current_attr.descr ) { 302 if( t.closest('tr').length ) { 303 t.closest('tr').after( '<tr class="description_' + decoded_name + '"><td colspan="2">' + current_attr.descr + '</td></tr>' ); 304 } else { 305 selectParent.append( '<p class="description_' + decoded_name + '">' + current_attr.descr + '</p>' ); 306 } 307 } 308 var type = ( typeof current_attr != 'undefined' ) ? current_attr.type : t.data('type'), 309 opt = ( typeof current_attr != 'undefined' ) ? current_attr.terms : false; 310 311 selectParent.addClass('yith_wccl_layout_'+yith_wccl_general.variation_layout); 312 selectParent.siblings( ".label" ).addClass('yith_wccl_layout_'+yith_wccl_general.variation_layout); 313 314 // exit if is not a custom attr 315 if ( ( ! obj.$is_loop && ( typeof current_attr == 'undefined' || ! current_attr.terms ) ) || typeof type == 'undefined' || ! type ) { 316 317 // Show option selected on th label. 318 t.on('change', function (e) { 319 var label = $(this).closest('tr').find('th label').data('default-label'); 320 var actual_val = $(this).val(); 321 322 if( actual_val ) { 323 label = label + yith_wccl_general.attribute_separator + actual_val; 324 } 325 if( yith_wccl_general.change_label_on_selected_attribute ) { 326 yith_wccl_change_label(label, $(this)); 327 } 328 329 }); 330 return; 331 } 332 333 t.addClass('yith_wccl_custom').hide(); 334 selectParent.addClass('yith_wccl_is_custom'); 335 336 if( ! select_box.length || ! yith_wccl_general.grey_out ) { 337 select_box.remove(); 338 select_box = $('<div />', { 339 'class': 'select_box_' + type + ' select_box ' + t.attr('name') 340 }).insertAfter(t); 341 } 342 t.find('option').each(function () { 343 var option_val = $(this).val(); 344 if( ( opt && typeof opt[option_val] != 'undefined') || ( typeof $(this).data('value') !== 'undefined' && $(this).data('value') !== '' ) ) { 345 current_option.push( option_val ); 346 347 var o = $(this), 348 classes = 'select_option_' + type + ' select_option', 349 value = opt && typeof opt[option_val] != 'undefined' ? opt[option_val].value : $(this).data('value'), 350 tooltip = opt && typeof opt[option_val] != 'undefined' ? opt[option_val].tooltip : $(this).data('tooltip'), 351 tooltip_image = opt && typeof opt[option_val] != 'undefined' ? opt[option_val].tooltip_image : $(this).data('tooltip_image'), 352 option = select_box.find('[data-value="' + option_val + '"]'), 353 attr_type = opt && typeof opt[option_val] != 'undefined' ? opt[option_val].type : $(this).data('type'); 354 355 type = attr_type ? attr_type : type; 356 // add options if missing 357 if( ! option.length ) { 358 // add selected class if is default 359 if( option_val == t.val() || option_val == t.attr( 'data-default_value' ) ) { 360 classes += ' selected'; 361 if( yith_wccl_general.change_label_on_selected_attribute && default_label !== undefined ) { 362 yith_wccl_change_label( default_label + yith_wccl_general.attribute_separator+o[0].label, t ); 363 } 364 } 365 366 option = $('<div/>', { 367 'class': classes, 368 'data-value': option_val, 369 'data-label': o[0].label, 370 'data-tooltip_image': tooltip_image, 371 'data-attr_name': decoded_name, 372 }).appendTo(select_box); 373 374 // event 375 option.off('click').on('click', function (e) { 376 377 var inactive = $(this).hasClass('inactive'), 378 selected = $(this).hasClass('selected'); 379 380 if( inactive ) { 381 382 var current_attribute_name = t.data('attribute_name') || t.attr('name'); 383 384 if( variations_match( obj.$form, $(this).data('value'), current_attribute_name ) ) { 385 t.val('').change(); 386 } 387 } 388 389 if( selected ) { 390 t.val('').change(); 391 if( yith_wccl_general.change_label_on_selected_attribute ) { 392 yith_wccl_change_label(default_label, t); 393 } 394 } else { 395 t.val( o.val() ).change(); 396 var label = $(this).data('label'); 397 if( yith_wccl_general.change_label_on_selected_attribute ) { 398 yith_wccl_change_label(default_label + yith_wccl_general.attribute_separator + label, t); 399 } 400 } 401 402 $(this).toggleClass( 'selected' ); 403 $(this).siblings().removeClass( 'selected' ); 404 }); 405 406 // style option 407 obj.styleOption( obj, option, type, value ); 408 // add tooltip if any 409 obj.addTooltip( obj, tooltip, option, type, value, tooltip_image ); 410 } 411 } 412 }); 413 414 var variationData = obj.$form.data('product_variations'); 415 416 //TODO Try to save the actual variations in order to know if they are available or not 417 select_box.children().each(function () { 418 var val = $(this).data('value') + ''; 419 var opt_attr_name = $(this).closest( '.yith_wccl_is_custom' ).find( 'select.yith_wccl_custom' ).attr( 'name' ); 420 let chosenAttr = Object.assign({}, chosenAttributes); //Copy original selected attributes. 421 var in_stock = true; 422
423 if ( $.inArray( val, current_option ) == '-1' ) { 424 $(this).addClass('yith-wccl-remove'); 425 } else { 426 $(this).removeClass('yith-wccl-remove'); 427 } 428 429 if( !obj.$use_ajax ) { 430 // Add inactive cross on out-of-stock attributes. 431 chosenAttr[opt_attr_name] = val; 432 var variations = obj.findMatchingVariations(variationData, chosenAttr); 433 in_stock = variations.find(variation => variation.is_in_stock === true) ? true : false; 434 if (!in_stock) { 435 $(this).addClass('inactive'); 436 switch (yith_wccl_general.attribute_style) { 437 case 'blur_cross' : 438 $(this).addClass('inactive_cross'); 439 break; 440 case 'hide': 441 $(this).addClass('yith-wccl-remove'); 442 break; 443 } 444 } else { 445 $(this).removeClass('inactive'); 446 switch (yith_wccl_general.attribute_style) { 447 case 'blur_cross' : 448 $(this).removeClass('inactive_cross'); 449 break; 450 case 'hide': 451 $(this).removeClass('yith-wccl-remove'); 452 break; 453 } 454 } 455 } 456 }); 457 458 obj.$form.trigger( 'yith_wccl_select_initialized', [ t, current_attr ] ); 459 }); 460 }; 461 462 WCCL.prototype.setDefaultValue = function( event ) { 463 var obj = event.data.obj; 464 465 obj.$select.each( function () { 466 $(this).val( $(this).attr( 'data-default_value' ) ); 467 }); 468 469 obj.$select.first().change(); 470 } 471 472 WCCL.prototype.changeLoopImage = function( obj, variation ){ 473 if( ! variation ) { 474 obj.$image.attr( 'src', obj.$def_image_src ); 475 if( obj.$def_image_srcset ) { 476 obj.$image.attr( 'srcset', obj.$def_image_srcset ); // restore srcset if any 477 } 478 } else { 479 480 var var_image = ( typeof variation.image != 'undefined' && variation.image.thumb_src ) ? variation.image.thumb_src : '', 481 var_image_srcset = ( typeof variation.image != 'undefined' && variation.image.srcset ) ? variation.image.srcset : ''; 482 483 if( var_image && var_image.length ) { 484 obj.$image.attr('src', var_image ); 485 obj.$image.attr('data-lazy-src', var_image ); 486 } 487 if( yith_wccl_general.set_srcset_on_loop_image && var_image_srcset && var_image_srcset.length && obj.$def_image_srcset ) { 488 obj.$image.attr( 'srcset', var_image_srcset ); 489 obj.$image.attr( 'data-lazy-srcset', var_image_srcset ); 490 } 491 } 492 }; 493 494 WCCL.prototype.changeSingleImage = function( obj, variation ) { 495 var $product_gallery = obj.$form.closest( '.product' ).find( '.images' ), 496 $product_img_wrap = $product_gallery.find( '.woocommerce-product-gallery__image, .woocommerce-product-gallery__image--placeholder' ).eq( 0 ), 497 $product_img = $product_img_wrap.find( '.wp-post-image' ), 498 $product_link = $product_img_wrap.find( 'a' ).eq( 0 ); 499 500 $product_img.wc_set_variation_attr( 'src', variation.image.src ); 501 $product_img.wc_set_variation_attr( 'height', variation.image.src_h ); 502 $product_img.wc_set_variation_attr( 'width', variation.image.src_w ); 503 $product_img.wc_set_variation_attr( 'srcset', variation.image.srcset ); 504 $product_img.wc_set_variation_attr( 'sizes', variation.image.sizes ); 505 $product_img.wc_set_variation_attr( 'title', variation.image.title ); 506 $product_img.wc_set_variation_attr( 'alt', variation.image.alt ); 507 $product_img.wc_set_variation_attr( 'data-src', variation.image.full_src ); 508 $product_img.wc_set_variation_attr( 'data-large_image', variation.image.full_src ); 509 $product_img.wc_set_variation_attr( 'data-large_image_width', variation.image.full_src_w ); 510 $product_img.wc_set_variation_attr( 'data-large_image_height', variation.image.full_src_h ); 511 $product_img_wrap.wc_set_variation_attr( 'data-thumb', variation.image.src ); 512 $product_link.wc_set_variation_attr( 'href', variation.image.full_src ); 513 }; 514 515 WCCL.prototype.changeImageOnHover = function( event ) { 516 517 var obj = event.data.obj; 518 519 if( /*obj.$select.length != 1 ||*/ ! yith_wccl_general.image_hover ) { 520 return; 521 } 522 523 obj.$form.on('mouseenter', '.select_option', function() { 524 var value = $(this).attr("data-value"), 525 //attr_name = //obj.$select.attr('name'), 526 attr_name = $(this).attr('data-attr_name'), 527 variation = variations_match( obj.$form, value, attr_name ); // find variation 528 529 if( ! yith_wccl_general.image_hover_even_selected && ( $(this).hasClass('selected') || $(this).siblings().hasClass('selected') ) ){ 530 return; 531 } 532 533 if( variation && ( ( variation.image && variation.image.src ) || variation.image_src ) ) { 534 if( obj.$form.hasClass('in_loop') ) { 535 obj.changeLoopImage( obj, variation ); 536 } else { 537 obj.changeSingleImage( obj, variation ); 538 } 539 } 540 }).on('mouseleave', '.select_option', function() { 541 542 var variation = variations_match( obj.$form, '', false ); // find variation 543 544 if( variation && ( ( variation.image && variation.image.src ) || variation.image_src ) ) { 545 if( obj.$form.hasClass('in_loop') ) { 546 obj.changeLoopImage( obj, variation ); 547 } else { 548 obj.changeSingleImage( obj, variation ); 549 } 550 } else { 551 if( obj.$form.hasClass('in_loop') ) { 552 obj.changeLoopImage( obj, false ); 553 } else { 554 obj.$form.wc_variations_image_update( false ); 555 } 556 } 557 }); 558 559 }; 560 561 WCCL.prototype.handleCheckVariations = function( event, data, focus ) { 562 var obj = event.data.obj; 563 if ( ! focus ) { 564 if( obj.$found ) { 565 event.data.obj.$found = false;
566 if( ! obj.$use_ajax ) return; 567 } 568 if( obj.$changed ) { 569 event.data.obj.$changed = false; 570 // reset 571 obj.resetLoopForm( obj ); 572 } 573 } 574 } 575 576 WCCL.prototype.handleFoundVariation = function( event, variation ) { 577 var obj = event.data.obj; 578 579 if( obj.$use_ajax ) { 580 obj.handleSelect( event ); 581 } else { 582 obj.$select.last().trigger('focusin'); 583 } 584 585 if ( obj.$is_loop ) { 586 587 if( obj.$changed ) { 588 obj.resetLoopForm( obj ); 589 } 590 591 // found it! 592 event.data.obj.$changed = true; 593 event.data.obj.$found = true; 594 595 // change image 596 obj.changeLoopImage( obj, variation ); 597 598 let selectLength = obj.$select.length; 599 600 if( selectLength === obj.$attr_number ) { //Prevent to change if the attribute number showed are less than the registered. 601 602 if (variation.is_purchasable) { 603 // change price 604 if (variation.price_html) { 605 obj.$wrapper.find(yith_wccl_general.wrapper_price_shop).replaceWith(variation.price_html); 606 } 607 608 // show qty input 609 if (obj.$input_qty.length) { 610 obj.$input_qty.show(); 611 } 612 // change button and add event add to cart 613 614 if (variation.is_in_stock) { 615 616 obj.$button.html(yith_wccl_general.add_cart); 617 618 obj.$button.off('click').on('click', { 619 variation: variation.variation_id, 620 select: obj.$select 621 }, yith_wccl_add_cart); 622 } 623 } 624 625 // add availability 626 //span.price 627 obj.$wrapper.find('.variations_form').after($(variation.availability_html).addClass('ywccl_stock')); 628 629 // set active variation 630 obj.$form.data('active_variation', variation.variation_id); 631 632 $(document).trigger('ywccl_found_variation_in_loop', [variation, obj.$button, yith_wccl_general.add_cart]); 633 } else { 634 //No variation is selected, we'll update only the button link. 635 let all_select = obj.$form.find( '.variations select' ), 636 settings = []; 637 638 $.each( all_select, function(){ 639 var attribute_name = $( this ).data( 'attribute_name' ) || $( this ).attr( 'name' ); 640 if( $(this).val() !== '' ) { 641 settings[attribute_name] = $(this).val(); 642 } 643 }); 644 var newURL = new URL(obj.$button.attr('href')); 645 646 for (var key in settings) { 647 var value = settings[key]; 648 if (value) { 649 newURL.searchParams.set(key, value); //newURL.searchParams.append(key, value); 650 } 651 } 652 obj.$button.attr('href',newURL); 653 } 654 } 655 $(document).trigger('ywccl_after_found_variation', [variation, obj.$button, yith_wccl_general.add_cart]); 656 }; 657 658 WCCL.prototype.handleVariationGallery = function( event, variation ) { 659 660 var obj = event.data.obj, 661 gallery_wrap = $( yith_wccl_general.single_gallery_selector ), 662 id; 663 664 if( obj.$is_loop || ! gallery_wrap.length ) { 665 return; 666 } 667 668 if( obj.$xhr !== null ) { 669 obj.$xhr.abort(); 670 } 671 672 id = typeof variation != 'undefined' ? variation.variation_id : obj.$form.find( '
672input[name="product_id"]' ).val(); 673 if( ! id || typeof id == 'undefined' ) { 674 return; 675 } 676 677 if ( undefined !== obj.variations_gallery[ id ] ) { 678 obj.loadVariationGallery( obj.variations_gallery[ id ], gallery_wrap, variation ); 679 } 680 else { 681 obj.$xhr = $.ajax({ 682 url: yith_wccl_general.ajaxurl.toString().replace( '%%endpoint%%', yith_wccl_general.actionVariationGallery ), 683 data: { 684 action: yith_wccl_general.actionVariationGallery, 685 id : id, 686 context: 'frontend' 687 }, 688 type: 'POST', 689 dataType: 'html', 690 beforeSend: function(){ 691 gallery_wrap.addClass( 'loading-gallery' ); 692 }, 693 success: function( html ){ 694 gallery_wrap.removeClass( 'loading-gallery' ); 695 obj.$xhr = null; 696 if( html ) { 697 // store variation gallery to improve performance 698 obj.variations_gallery[ id ] = html; 699 obj.loadVariationGallery( html, gallery_wrap, variation ); 700 } 701 702 // Support for Avda custom gallery 703 if( typeof initAvadaWoocommerProductGallery === "function" ){ 704 initAvadaWoocommerProductGallery(); 705 } 706 } 707 }); 708 } 709 } 710 711 WCCL.prototype.loadVariationGallery = function( html, gallery_wrap, variation ) { 712 gallery_wrap.replaceWith( html ).fadeIn(); 713 if ( typeof wc_single_product_params !== 'undefined' ) { 714 // reload gallery 715 $( yith_wccl_general.single_gallery_selector ).wc_product_gallery( wc_single_product_params ); 716 } 717 718 this.$form.wc_variations_image_update( variation ); 719 720 $(document).trigger( 'yith_wccl_product_gallery_loaded' ); 721 // uncode theme compatibility 722 $(document).trigger( 'uncode-quick-view-loaded' ); 723 // woodmart theme compatibility 724 $(document).trigger('wdReplaceMainGallery'); 725 $(document).trigger('wdResetVariation'); 726 if( typeof woodmartThemeModule !== 'undefined' ){ 727 woodmartThemeModule.productImages(); 728 } 729 } 730 731 WCCL.prototype.resetLoopForm = function( obj ){ 732 // reset image 733 obj.changeLoopImage( obj, false ); 734 obj.$wrapper.find( yith_wccl_general.wrapper_price_shop ).replaceWith( obj.$price_html ); 735 obj.$wrapper.find('.ywccl_stock').remove(); 736 737 if( obj.$input_qty.length ){ 738 obj.$input_qty.hide(); 739 } 740 741 obj.$button.html( obj.$button_html ) 742 .off( 'click', yith_wccl_add_cart ) 743 .removeClass( 'added' ) 744 .next('.added_to_cart').remove(); 745 746 // set active variation 747 obj.$form.data('active_variation', '' ); 748 749 $(document).trigger( 'yith_wccl_reset_loop_form',[obj.$button] ); 750 } 751 752 WCCL.prototype.resetForm = function( obj ) { 753 obj.$form.find( 'div.select_option' ).removeClass( 'selected inactive' ); 754 obj.$select.val('').change(); 755 obj.$form.trigger( 'reset_data' ); 756 }; 757 758 WCCL.prototype.onReset = function( event ) { 759 event.data.obj.$form.find('.select_option.selected').removeClass('selected inactive'); 760 }; 761 762 WCCL.prototype.onAddToCart = function ( event ) { 763 764 if ( $( this ).is('.wc-variation-selection-needed') ) { 765 766 event.data.obj.$select.each(function (){ 767 $(this).closest('tr').find('th').removeClass('yith-wccl-no-selected'); 768 if( !$(this).val() ) { 769 $(this).closest('tr').find('th').addClass('yith-wccl-no-selected'); 770 } 771 }); 772 } 773 }; 774 775 WCCL.prototype.findMatchingVariations = function( variations, attributes ) { 776 var matching = []; 777 for (var i = 0; i < variations.length; i++) { 778 var variation = variations[i]; 779 if (this.isMatch(variation.attributes, attributes)) { 780 matching.push(variation); 781 } 782 } 783 return matching; 784 }; 785 786 /** 787 * See if attributes match. 788 * @return {Boolean} 789 */ 790 WCCL.prototype.isMatch = function( variation_attributes, attributes ) { 791 var match = true; 792 for ( var attr_name in variation_attributes ) { 793 if ( variation_attributes.hasOwnProperty( attr_name ) ) { 794 var val1 = variation_attributes[ attr_name ]; 795 var val2 = attributes[ attr_name ]; 796 if ( val1 !== undefined && val2 !== undefined && typeof val2 === "string" && val1.length !== 0 && val2.length !== 0 && val1 !== val2 ) { 797 match = false;
798 } 799 } 800 } 801 return match; 802 }; 803 804 WCCL.prototype.getChosenAttributes = function(form) { 805 var data = {}; 806 var count = 0; 807 var chosen = 0; 808 809 if ( form.$use_ajax ) { 810 return; 811 } 812 813 this.$select.each( function() { 814 var attribute_name = $( this ).data( 'attribute_name' ) || $( this ).attr( 'name' ); 815 var value = $( this ).val() || ''; 816 817 if ( value.length > 0 ) { 818 chosen ++; 819 } 820 821 count ++; 822 data[ attribute_name ] = value; 823 }); 824 825 return { 826 'count' : count, 827 'chosenCount': chosen, 828 'data' : data 829 }; 830 }; 831 832 WCCL.prototype.onUpdateAttributes = function(event) { 833 var form = event.data.obj; 834 835 if ( form.$use_ajax ) { 836 return; 837 } 838 839 var attributes = form.getChosenAttributes(form), 840 currentAttributes = attributes.data; 841 842 843 844 // Loop through selects and disable/enable options based on selections. 845 form.$select.each( function( index, el ) { 846 var current_attr_select = $( el ), 847 current_attr_name = current_attr_select.data( 'attribute_name' ) || current_attr_select.attr( 'name' ), 848 show_option_none = $( el ).data( 'show_option_none' ), 849 option_gt_filter = ':gt(0)', 850 attached_options_count = 0, 851 new_attr_select = $( '<select/>' ), 852 selected_attr_val = current_attr_select.val() || '', 853 selected_attr_val_valid = true; 854 855 // Reference options set at first. 856 if ( ! current_attr_select.data( 'attribute_html' ) ) { 857 var refSelect = current_attr_select.clone(); 858 859 refSelect.find( 'option' ).removeAttr( 'attached' ).prop( 'disabled', false ).prop( 'selected', false ); 860 861 // Legacy data attribute. 862 current_attr_select.data( 863 'attribute_options', 864 refSelect.find( 'option' + option_gt_filter ).get() 865 ); 866 current_attr_select.data( 'attribute_html', refSelect.html() ); 867 } 868 869 new_attr_select.html( current_attr_select.data( 'attribute_html' ) ); 870 871 // The attribute of this select field should not be taken into account when calculating its matching variations: 872 // The constraints of this attribute are shaped by the values of the other attributes. 873 var checkAttributes = $.extend( true, {}, currentAttributes ); 874 875 checkAttributes[ current_attr_name ] = ''; 876 var variations = form.findMatchingVariations( event.data.obj.$form.data('product_variations'), checkAttributes ); 877 878 // Loop through variations. 879 for ( var num in variations ) { 880 if ( typeof( variations[ num ] ) !== 'undefined' ) { 881 var variationAttributes = variations[ num ].attributes; 882 883 for ( var attr_name in variationAttributes ) { 884 if ( variationAttributes.hasOwnProperty( attr_name ) ) { 885 var attr_val = variationAttributes[ attr_name ], 886 variation_active = ''; 887 888 if ( attr_name === current_attr_name ) { 889 if ( variations[ num ].variation_is_active ) { 890 variation_active = 'enabled'; 891 } 892 893 if ( attr_val ) { 894 // Decode entities. 895 attr_val = $( '<div/>' ).html( attr_val ).text(); 896 897 // Attach to matching options by value. This is done to compare 898 // TEXT values rather than any HTML entities. 899 var $option_elements = new_attr_select.find( 'option' ); 900 if ( $option_elements.length ) { 901 for (var i = 0, len = $option_elements.length; i < len; i++) { 902 var $option_element = $( $option_elements[i] ), 903 option_value = $option_element.val(); 904 905 if ( attr_val === option_value ) { 906 $option_element.addClass( 'attached ' + variation_active ); 907 break; 908 } 909 } 910 } 911 } else { 912 // Attach all apart from placeholder. 913 new_attr_select.find( 'option:gt(0)' ).addClass( 'attached ' + variation_active ); 914 } 915 } 916 } 917 } 918 } 919 } 920 921 // Count available options. 922 attached_options_count = new_attr_select.find( 'option.attached' ).length; 923 924 // Check if current selection is in attached options. 925 if ( selected_attr_val ) { 926 selected_attr_val_valid = false;
927 928 if ( 0 !== attached_options_count ) { 929 new_attr_select.find( 'option.attached.enabled' ).each( function() { 930 var option_value = $( this ).val(); 931 932 if ( selected_attr_val === option_value ) { 933 selected_attr_val_valid = true; 934 return false; // break. 935 } 936 }); 937 } 938 } 939 940 // Detach the placeholder if: 941 // - Valid options exist. 942 // - The current selection is non-empty. 943 // - The current selection is valid. 944 // - Placeholders are not set to be permanently visible. 945 if ( attached_options_count > 0 && selected_attr_val && selected_attr_val_valid && ( 'no' === show_option_none ) ) { 946 new_attr_select.find( 'option:first' ).remove(); 947 option_gt_filter = ''; 948 } 949 950 // Detach unattached. 951 new_attr_select.find( 'option' + option_gt_filter + ':not(.attached)' ).remove(); 952 953 // Finally, copy to DOM and set value. 954 current_attr_select.html( new_attr_select.html() ); 955 current_attr_select.find( 'option' + option_gt_filter + ':not(.enabled)' ).prop( 'disabled', true ); 956 957 // Choose selected value. 958 if ( selected_attr_val ) { 959 // If the previously selected value is no longer available, fall back to the placeholder (it's going to be there). 960 if ( selected_attr_val_valid ) { 961 current_attr_select.val( selected_attr_val ); 962 } else { 963 current_attr_select.val( '' ).trigger( 'change' ); 964 } 965 } else { 966 current_attr_select.val( '' ); // No change event to prevent infinite loop. 967 } 968 }); 969 970 // Custom event for when variations have been updated. 971 form.$form.trigger( 'woocommerce_update_variation_values' ); 972 }; 973 974 WCCL.prototype.init = function( event ) { 975 976 var obj = event.data.obj; 977 978 obj.$form.on( 'click.wc-variation-form', '.reset_variations', { obj: obj }, obj.onReset ); 979 obj.$form.on( 'woocommerce_update_variation_values', { obj: obj }, obj.handleSelect ); 980 obj.$form.one( 'yith_wccl_select_initialized', { obj: obj }, obj.changeImageOnHover ); 981 obj.$form.on( 'check_variations', { obj: obj }, obj.handleCheckVariations ); 982 obj.$form.on( 'found_variation', { obj: obj }, obj.handleFoundVariation ); 983 if( yith_wccl_general.enable_handle_variation_gallery ){ 984 obj.$form.on( 'found_variation', {obj: obj}, obj.handleVariationGallery ); 985 obj.$form.on( 'reset_image', {obj: obj}, obj.handleVariationGallery ); 986 } 987 obj.$form.on( 'click', '.single_add_to_cart_button', { obj: obj }, obj.onAddToCart ); 988 989 obj.$select.each( function(){ 990 991 // Fix compatibility to avoid conflict with Product Bundles - Variation Bundles 992 if( $(this).parents( '.bundled_product' ).length ){ 993 return; 994 } 995 996 var val = $(this).attr( 'data-default_value' ); 997 $(this).removeAttr( 'data-default_value' ); 998 $(this).val( val ); 999 1000 //set label. 1001 $(this).closest('tr').find('th label').attr('data-default-label', $(this).closest('tr').find('th label').text() ); 1002 1003 }); 1004 obj.$form.on( 'update_variation_values.wc-variation-form', { obj: obj }, obj.onUpdateAttributes ); 1005 1006 // force start select 1007 obj.handleSelect( event ); 1008 1009 } 1010 1011 // retrocompatibility 1012 $.yith_wccl = function( attr ) { 1013 forms = $( '.variations_form.cart:not(.initialized), .owl-item.cloned .variations_form, form.cart.ywcp_form_loaded' ); 1014 // prevent undefined attr error 1015 if( typeof attr == 'undefined' ) { 1016 attr = []; 1017 } 1018 1019 forms.each(function (){ 1020 new WCCL( $(this), attr ); 1021 }); 1022 }; 1023 1024 // plugin compatibility 1025 $(document).on( yith_wccl_general.plugin_compatibility_selectors, function() { 1026 1027 if( typeof $.yith_wccl != 'undefined' && typeof $.fn.wc_variation_form != 'undefined' ) { 1028 // not initialized 1029 $(document).find( '.variations_form:not(.initialized), .owl-item.cloned .variations_form' ).each( function() { 1030 $(this).wc_variation_form(); 1031 }); 1032 1033 // prevent undefined attr error 1034 if( typeof attr == 'undefined' ) { 1035 attr = []; 1036 } 1037 1038 $.yith_wccl(attr); 1039 } 1040 }); 1041 1042 // Astra infinite scrolling. Facetwp load more 1043 $(window).on('astWooCommerceAjaxPostsAdded facetwp-loaded', function() { 1044 if( typeof $.yith_wccl != 'undefined' && typeof $.fn.wc_variation_form != 'undefined' ) { 1045 // not initialized 1046 $(document).find( '.variations_form:not(.initialized)' ).each( function() { 1047 $(this).wc_variation_form(); 1048 }); 1049 1050 $.yith_wccl([]); 1051 } 1052 }) 1053 1054 // reinit for woocommerce quick view 1055 $( 'body' ).on( 'quick-view-displayed', function() { 1056 var attr_qv = $('.pp_woocommerce_quick_view').find('.yith-wccl-data').data('attr'); 1057 if( attr_qv ) { 1058 $.yith_wccl(attr_qv); 1059 } 1060 }); 1061 1062 // Fix for Flatsome Infinite Scrolling 1063 $('.shop-container > .products').on('append.infiniteScroll',function(){ 1064 $(document).find( '.variations_form:not(.initialized), .owl-item.cloned .variations_form' ).each( function() { 1065 $(this).wc_variation_form(); 1066 }); 1067 1068 // prevent undefined attr error 1069 if( typeof attr == 'undefined' ) { 1070 attr = []; 1071 } 1072 1073 $.yith_wccl(attr); 1074 1075 }); 1076 1077 // Re-init scripts on gallery loaded 1078 $(document).on( 'yith_wccl_product_gallery_loaded', function(){ 1079 if( typeof mkdf != 'undefined' && typeof mkdf.modules.common.mkdfPrettyPhoto === "function" ) { 1080 var item = $('.mkdf-woo-single-page.mkdf-woo-single-has-pretty-photo .images .woocommerce-product-gallery__image'); 1081 if( item.length ) { 1082 item.children('a').attr('data-rel', 'prettyPhoto[woo_single_pretty_photo]'); 1083 mkdf.modules.common.mkdfPrettyPhoto(); 1084 } 1085 } 1086 1087 if( typeof Flatsome != 'undefined' ){ 1088 Flatsome.attach( $( '.product-gallery' ) ); 1089 // foce zoom button to work 1090 $( '.zoom-button' ).click( function ( ev ) { 1091 ev.preventDefault(); 1092 $( '.product-gallery-slider' ) .find( '.is-selected a' ).click(); 1093 }); 1094 } 1095 }); 1096 1097 //Prevent windows.
1098 $('.single_add_to_cart_button').on('click',function(e){ 1099 if ( $( this ).is('.disabled') ) { 1100 e.preventDefault(); 1101 if ( $( this ).is('.wc-variation-selection-needed') ) { 1102 let form = $('.variations_form.cart'); 1103 let selects = form.find( '.variations select' ); 1104 1105 selects.each(function (){ 1106 $(this).closest('tr').find('th').removeClass('yith-wccl-no-selected'); 1107 $(this).closest('tr').find('.yith-wccl-no-selected-msg').remove(); 1108 if( !$(this).val() ) { 1109 $(this).closest('tr').find('th').addClass('yith-wccl-no-selected'); 1110 $(this).closest('td').append('<div class="yith-wccl-no-selected-msg"><small>'+yith_wccl_general.error_no_selected+'</small></div>'); 1111 1112 } 1113 }); 1114 1115 return false; 1116 } 1117 } 1118 }); 1119 1120 // START 1121 $(document).ready( function(){ 1122 $.yith_wccl(); 1123 }); 1124 1125 1126})( jQuery, window, document );
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.