1/*global wc_add_to_cart_variation_params */ 2;(function ( $, window, document, undefined ) { 3 /** 4 * VariationForm class which handles variation forms and attributes. 5 */ 6 var VariationForm = function( $form ) { 7 var self = this; 8 9 self.$form = $form; 10 self.$attributeFields = $form.find( '.variations select' ); 11 self.$singleVariation = $form.find( '.single_variation' ); 12 self.$singleVariationWrap = $form.find( '.single_variation_wrap' ); 13 self.$resetVariations = $form.find( '.reset_variations' ); 14 self.$resetAlert = $form.find( '.reset_variations_alert' ); 15 self.$product = $form.closest( '.product' ); 16 self.variationData = $form.data( 'product_variations' ); 17 self.useAjax = false === self.variationData; 18 self.xhr = false; 19 self.loading = true; 20 21 // Initial state. 22 self.$singleVariationWrap.show(); 23 self.$form.off( '.wc-variation-form' ); 24 25 // Methods. 26 self.getChosenAttributes = self.getChosenAttributes.bind( self ); 27 self.findMatchingVariations = self.findMatchingVariations.bind( self ); 28 self.isMatch = self.isMatch.bind( self ); 29 self.toggleResetLink = self.toggleResetLink.bind( self ); 30 self.showNoMatchingVariationsMsg = self.showNoMatchingVariationsMsg.bind( self ); 31 32 // Events. 33 $form.on( 'click.wc-variation-form', '.reset_variations', { variationForm: self }, self.onReset ); 34 $form.on( 'reload_product_variations', { variationForm: self }, self.onReload ); 35 $form.on( 'hide_variation', { variationForm: self }, self.onHide ); 36 $form.on( 'show_variation', { variationForm: self }, self.onShow ); 37 $form.on( 'click', '.single_add_to_cart_button', { variationForm: self }, self.onAddToCart ); 38 $form.on( 'reset_data', { variationForm: self }, self.onResetDisplayedVariation ); 39 $form.on( 'reset_focus', { variationForm: self }, self.onResetVariationFocus ); 40 $form.on( 'announce_reset', { variationForm: self }, self.onAnnounceReset ); 41 $form.on( 'clear_reset_announcement', { variationForm: self }, self.onClearResetAnnouncement ); 42 $form.on( 'reset_image', { variationForm: self }, self.onResetImage ); 43 $form.on( 'change.wc-variation-form', '.variations select', { variationForm: self }, self.onChange ); 44 $form.on( 'found_variation.wc-variation-form', { variationForm: self }, self.onFoundVariation ); 45 $form.on( 'check_variations.wc-variation-form', { variationForm: self }, self.onFindVariation ); 46 $form.on( 'update_variation_values.wc-variation-form', { variationForm: self }, self.onUpdateAttributes ); 47 $form.on( 48 'keydown.wc-variation-form', 49 '.reset_variations', 50 { variationForm: self }, 51 self.onResetKeyDown 52 ); 53 54 // Init after gallery. 55 setTimeout( function() { 56 $form.trigger( 'check_variations' ); 57 $form.trigger( 'wc_variation_form', self ); 58 self.loading = false; 59 }, 100 ); 60 }; 61 62 /** 63 * Reset all fields. 64 */ 65 VariationForm.prototype.onReset = function( event ) { 66 event.preventDefault(); 67 event.data.variationForm.$attributeFields.val( '' ).trigger( 'change' ); 68 event.data.variationForm.$form.trigger( 'announce_reset' ); 69 event.data.variationForm.$form.trigger( 'reset_data' ); 70 event.data.variationForm.$form.trigger( 'reset_focus' ); 71 }; 72 73 /** 74 * Reload variation data from the DOM. 75 */ 76 VariationForm.prototype.onReload = function( event ) { 77 var form = event.data.variationForm; 78 form.variationData = form.$form.data( 'product_variations' ); 79 form.useAjax = false === form.variationData; 80 form.$form.trigger( 'check_variations' ); 81 }; 82 83 /** 84 * When a variation is hidden. 85 */ 86 VariationForm.prototype.onHide = function( event ) { 87 event.preventDefault(); 88 event.data.variationForm.$form 89 .find( '.single_add_to_cart_button' ) 90 .removeClass( 'wc-variation-is-unavailable' ) 91 .addClass( 'disabled wc-variation-selection-needed' ); 92 event.data.variationForm.$form 93 .find( '.woocommerce-variation-add-to-cart' ) 94 .removeClass( 'woocommerce-variation-add-to-cart-enabled' ) 95 .addClass( 'woocommerce-variation-add-to-cart-disabled' ); 96 }; 97 98 /** 99 * When a variation is shown.
100 */ 101 VariationForm.prototype.onShow = function( event, variation, purchasable ) { 102 event.preventDefault(); 103 if ( purchasable ) { 104 event.data.variationForm.$form 105 .find( '.single_add_to_cart_button' ) 106 .removeClass( 'disabled wc-variation-selection-needed wc-variation-is-unavailable' ); 107 event.data.variationForm.$form 108 .find( '.woocommerce-variation-add-to-cart' ) 109 .removeClass( 'woocommerce-variation-add-to-cart-disabled' ) 110 .addClass( 'woocommerce-variation-add-to-cart-enabled' ); 111 } else { 112 event.data.variationForm.$form 113 .find( '.single_add_to_cart_button' ) 114 .removeClass( 'wc-variation-selection-needed' ) 115 .addClass( 'disabled wc-variation-is-unavailable' ); 116 event.data.variationForm.$form 117 .find( '.woocommerce-variation-add-to-cart' ) 118 .removeClass( 'woocommerce-variation-add-to-cart-enabled' ) 119 .addClass( 'woocommerce-variation-add-to-cart-disabled' ); 120 } 121 122 // If present, the media element library needs initialized on the variation description. 123 if ( wp.mediaelement ) { 124 event.data.variationForm.$form.find( '.wp-audio-shortcode, .wp-video-shortcode' ) 125 .not( '.mejs-container' ) 126 .filter( 127 function () { 128 return ! $( this ).parent().hasClass( 'mejs-mediaelement' ); 129 } 130 ) 131 .mediaelementplayer( wp.mediaelement.settings ); 132 } 133 }; 134 135 /** 136 * When the cart button is pressed. 137 */ 138 VariationForm.prototype.onAddToCart = function( event ) { 139 if ( $( this ).is('.disabled') ) { 140 event.preventDefault(); 141 142 if ( $( this ).is('.wc-variation-is-unavailable') ) { 143 window.alert( wc_add_to_cart_variation_params.i18n_unavailable_text ); 144 } else if ( $( this ).is('.wc-variation-selection-needed') ) { 145 window.alert( wc_add_to_cart_variation_params.i18n_make_a_selection_text ); 146 } 147 } 148 }; 149 150 /** 151 * When displayed variation data is reset. 152 */ 153 VariationForm.prototype.onResetDisplayedVariation = function( event ) { 154 var form = event.data.variationForm; 155 form.$product.find( '.product_meta' ).find( '.sku' ).wc_reset_content(); 156 form.$product 157 .find( '.product_weight, .woocommerce-product-attributes-item--weight .woocommerce-product-attributes-item__value' ) 158 .wc_reset_content(); 159 form.$product 160 .find( '.product_dimensions, .woocommerce-product-attributes-item--dimensions .woocommerce-product-attributes-item__value' ) 161 .wc_reset_content(); 162 form.$form.trigger( 'reset_image' ); 163 form.$singleVariation.slideUp( 200 ).trigger( 'hide_variation' ); 164 }; 165 166 /** 167 * Announce reset to screen readers. 168 */ 169 VariationForm.prototype.onAnnounceReset = function( event ) { 170 event.data.variationForm.$resetAlert.text( wc_add_to_cart_variation_params.i18n_reset_alert_text ); 171 } 172 173 /** 174 * Focus variation reset 175 */ 176 VariationForm.prototype.onResetVariationFocus = function( event ) { 177 event.data.variationForm.$attributeFields[0].focus(); 178 } 179 180 /** Clear reset announcement */ 181 VariationForm.prototype.onClearResetAnnouncement = function( event ) { 182 event.data.variationForm.$resetAlert.text( '' ); 183 } 184 185 /** 186 * When the product image is reset. 187 */ 188 VariationForm.prototype.onResetImage = function( event ) { 189 event.data.variationForm.$form.wc_variations_image_update( false ); 190 }; 191 192 /** 193 * Looks for matching variations for current selected attributes. 194 */ 195 VariationForm.prototype.onFindVariation = function( event, chosenAttributes ) { 196 var form = event.data.variationForm, 197 attributes = 'undefined' !== typeof chosenAttributes ? chosenAttributes : form.getChosenAttributes(), 198 currentAttributes = attributes.data; 199 200 if ( attributes.count && attributes.count === attributes.chosenCount ) { 201 if ( form.useAjax ) { 202 if ( form.xhr ) { 203 form.xhr.abort(); 204 } 205 form.$form.block( { message: null, overlayCSS: { background: '#fff', opacity: 0.6 } } ); 206 currentAttributes.product_id = parseInt( form.$form.data( 'product_id' ), 10 ); 207 currentAttributes.custom_data = form.$form.data( 'custom_data' ); 208 form.xhr = $.ajax( { 209 url: wc_add_to_cart_variation_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'get_variation' ), 210 type: 'POST', 211 data: currentAttributes, 212 success: function( variation ) { 213 if ( variation ) {
214 form.$form.trigger( 'found_variation', [ variation ] ); 215 } else { 216 form.$form.trigger( 'reset_data' ); 217 attributes.chosenCount = 0; 218 219 if ( ! form.loading ) { 220 form.showNoMatchingVariationsMsg(); 221 } 222 } 223 }, 224 complete: function() { 225 form.$form.unblock(); 226 } 227 } ); 228 } else { 229 form.$form.trigger( 'update_variation_values' ); 230 231 var matching_variations = form.findMatchingVariations( form.variationData, currentAttributes ), 232 variation = matching_variations.shift(); 233 234 if ( variation ) { 235 form.$form.trigger( 'found_variation', [ variation ] ); 236 } else { 237 form.$form.trigger( 'reset_data' ); 238 attributes.chosenCount = 0; 239 240 if ( ! form.loading ) { 241 form.showNoMatchingVariationsMsg(); 242 } 243 } 244 } 245 } else { 246 form.$form.trigger( 'update_variation_values' ); 247 form.$form.trigger( 'reset_data' ); 248 } 249 250 // Show reset link. 251 form.toggleResetLink( attributes.chosenCount > 0 ); 252 }; 253 254 /** 255 * Triggered when a variation has been found which matches all attributes. 256 */ 257 VariationForm.prototype.onFoundVariation = function( event, variation ) { 258 var form = event.data.variationForm, 259 $sku = form.$product.find( '.product_meta' ).find( '.sku' ), 260 $weight = form.$product.find( 261 '.product_weight, .woocommerce-product-attributes-item--weight .woocommerce-product-attributes-item__value' 262 ), 263 $dimensions = form.$product.find( 264 '.product_dimensions, .woocommerce-product-attributes-item--dimensions .woocommerce-product-attributes-item__value' 265 ), 266 $qty_input = form.$singleVariationWrap.find( '.quantity input.qty[name="quantity"]' ), 267 $qty = $qty_input.closest( '.quantity' ), 268 purchasable = true, 269 variation_id = '', 270 template = false, 271 $template_html = ''; 272 273 if ( variation.sku ) { 274 $sku.wc_set_content( variation.sku ); 275 } else { 276 $sku.wc_reset_content(); 277 } 278 279 if ( variation.weight ) { 280 $weight.wc_set_content( variation.weight_html ); 281 } else { 282 $weight.wc_reset_content(); 283 } 284 285 if ( variation.dimensions ) { 286 // Decode HTML entities. 287 $dimensions.wc_set_content( $.parseHTML( variation.dimensions_html )[0].data ); 288 } else { 289 $dimensions.wc_reset_content(); 290 } 291 292 form.$form.wc_variations_image_update( variation ); 293 294 if ( ! variation.variation_is_visible ) { 295 template = wp_template( 'unavailable-variation-template' ); 296 } else { 297 template = wp_template( 'variation-template' ); 298 variation_id = variation.variation_id; 299 } 300 301 $template_html = template( { 302 variation: variation 303 } ); 304 $template_html = $template_html.replace( '/*<![CDATA[*/', '' ); 305 $template_html = $template_html.replace( '/*]]>*/', '' ); 306 307 form.$singleVariation.html( $template_html );
308 form.$form.find( 'input[name="variation_id"], input.variation_id' ).val( variation.variation_id ).trigger( 'change' ); 309 310 // Hide or show qty input 311 if ( variation.is_sold_individually === 'yes' ) { 312 $qty_input.val( '1' ).attr( 'min', '1' ).attr( 'max', '' ).trigger( 'change' ); 313 $qty.hide(); 314 } else { 315 316 var qty_val = parseFloat( $qty_input.val() ); 317 318 if ( isNaN( qty_val ) ) { 319 qty_val = variation.min_qty; 320 } else { 321 qty_val = qty_val > parseFloat( variation.max_qty ) ? variation.max_qty : qty_val; 322 qty_val = qty_val < parseFloat( variation.min_qty ) ? variation.min_qty : qty_val; 323 } 324 325 $qty_input.attr( 'min', variation.min_qty ).attr( 'max', variation.max_qty ).val( qty_val ).trigger( 'change' ); 326 $qty.show(); 327 } 328 329 // Enable or disable the add to cart button 330 if ( ! variation.is_purchasable || ! variation.is_in_stock || ! variation.variation_is_visible ) { 331 purchasable = false; 332 } 333 334 // Reveal 335 if ( form.$singleVariation.text().trim() ) { 336 form.$singleVariation.slideDown( 200 ).trigger( 'show_variation', [ variation, purchasable ] ); 337 } else { 338 form.$singleVariation.show().trigger( 'show_variation', [ variation, purchasable ] ); 339 } 340 }; 341 342 /** 343 * Triggered when an attribute field changes. 344 */ 345 VariationForm.prototype.onChange = function( event ) { 346 var form = event.data.variationForm; 347
348 form.$form.find( 'input[name="variation_id"], input.variation_id' ).val( '' ).trigger( 'change' ); 349 form.$form.trigger( 'clear_reset_announcement' ); 350 form.$form.find( '.wc-no-matching-variations' ).parent().remove(); 351 352 if ( form.useAjax ) { 353 form.$form.trigger( 'check_variations' ); 354 } else { 355 form.$form.trigger( 'woocommerce_variation_select_change' ); 356 form.$form.trigger( 'check_variations' ); 357 } 358 359 // Custom event for when variation selection has been changed 360 form.$form.trigger( 'woocommerce_variation_has_changed' ); 361 }; 362 363 /** 364 * Escape quotes in a string. 365 * @param {string} string 366 * @return {string} 367 */ 368 VariationForm.prototype.addSlashes = function( string ) { 369 string = string.replace( /'/g, '\\\'' ); 370 string = string.replace( /"/g, '\\\"' ); 371 return string; 372 }; 373 374 /** 375 * Updates attributes in the DOM to show valid values. 376 */ 377 VariationForm.prototype.onUpdateAttributes = function( event ) { 378 var form = event.data.variationForm, 379 attributes = form.getChosenAttributes(), 380 currentAttributes = attributes.data; 381 382 if ( form.useAjax ) { 383 return; 384 } 385 386 // Loop through selects and disable/enable options based on selections. 387 form.$attributeFields.each( function( index, el ) { 388 var current_attr_select = $( el ), 389 current_attr_name = current_attr_select.data( 'attribute_name' ) || current_attr_select.attr( 'name' ), 390 show_option_none = $( el ).data( 'show_option_none' ), 391 option_gt_filter = ':gt(0)', 392 attached_options_count = 0, 393 new_attr_select = $( '<select/>' ), 394 selected_attr_val = current_attr_select.val() || '', 395 selected_attr_val_valid = true; 396 397 // Reference options set at first. 398 if ( ! current_attr_select.data( 'attribute_html' ) ) { 399 var refSelect = current_attr_select.clone(); 400 401 refSelect.find( 'option' ).removeAttr( 'attached' ).prop( 'disabled', false ).prop( 'selected', false ); 402 403 // Legacy data attribute. 404 current_attr_select.data( 405 'attribute_options', 406 refSelect.find( 'option' + option_gt_filter ).get() 407 ); 408 current_attr_select.data( 'attribute_html', refSelect.html() ); 409 } 410 411 new_attr_select.html( current_attr_select.data( 'attribute_html' ) ); 412 413 // The attribute of this select field should not be taken into account when calculating its matching variations: 414 // The constraints of this attribute are shaped by the values of the other attributes. 415 var checkAttributes = $.extend( true, {}, currentAttributes ); 416 417 checkAttributes[ current_attr_name ] = ''; 418 419 var variations = form.findMatchingVariations( form.variationData, checkAttributes ); 420 421 // Loop through variations. 422 for ( var num in variations ) { 423 if ( typeof( variations[ num ] ) !== 'undefined' ) { 424 var variationAttributes = variations[ num ].attributes; 425 426 for ( var attr_name in variationAttributes ) { 427 if ( variationAttributes.hasOwnProperty( attr_name ) ) { 428 var attr_val = variationAttributes[ attr_name ], 429 variation_active = ''; 430 431 if ( attr_name === current_attr_name ) { 432 if ( variations[ num ].variation_is_active ) { 433 variation_active = 'enabled'; 434 } 435 436 if ( attr_val ) { 437 // Decode entities. 438 attr_val = $( '<div/>' ).html( attr_val ).text(); 439 440 // Attach to matching options by value. This is done to compare 441 // TEXT values rather than any HTML entities. 442 var $option_elements = new_attr_select.find( 'option' ); 443 if ( $option_elements.length ) { 444 for (var i = 0, len = $option_elements.length; i < len; i++) { 445 var $option_element = $( $option_elements[i] ), 446 option_value = $option_element.val(); 447 448 if ( attr_val === option_value ) { 449 $option_element.addClass( 'attached ' + variation_active ); 450 break; 451 } 452 } 453 } 454 } else { 455 // Attach all apart from placeholder. 456 new_attr_select.find( 'option:gt(0)' ).addClass( 'attached ' + variation_active ); 457 } 458 } 459 } 460 } 461 } 462 } 463 464 // Count available options. 465 attached_options_count = new_attr_select.find( 'option.attached' ).length; 466 467 // Check if current selection is in attached options. 468 if ( selected_attr_val ) { 469 selected_attr_val_valid = false;
470 471 if ( 0 !== attached_options_count ) { 472 new_attr_select.find( 'option.attached.enabled' ).each( function() { 473 var option_value = $( this ).val(); 474 475 if ( selected_attr_val === option_value ) { 476 selected_attr_val_valid = true; 477 return false; // break. 478 } 479 }); 480 } 481 } 482 483 // Detach the placeholder if: 484 // - Valid options exist. 485 // - The current selection is non-empty. 486 // - The current selection is valid. 487 // - Placeholders are not set to be permanently visible. 488 if ( attached_options_count > 0 && selected_attr_val && selected_attr_val_valid && ( 'no' === show_option_none ) ) { 489 new_attr_select.find( 'option:first' ).remove(); 490 option_gt_filter = ''; 491 } 492 493 // Detach unattached. 494 new_attr_select.find( 'option' + option_gt_filter + ':not(.attached)' ).remove(); 495 496 // Finally, copy to DOM and set value. 497 current_attr_select.html( new_attr_select.html() ); 498 current_attr_select.find( 'option' + option_gt_filter + ':not(.enabled)' ).prop( 'disabled', true ); 499 500 // Choose selected value. 501 if ( selected_attr_val ) { 502 // If the previously selected value is no longer available, fall back to the placeholder (it's going to be there). 503 if ( selected_attr_val_valid ) { 504 current_attr_select.val( selected_attr_val ); 505 } else { 506 current_attr_select.val( '' ).trigger( 'change' ); 507 } 508 } else { 509 current_attr_select.val( '' ); // No change event to prevent infinite loop. 510 } 511 }); 512 513 // Custom event for when variations have been updated. 514 form.$form.trigger( 'woocommerce_update_variation_values' ); 515 }; 516 517 /** 518 * Get chosen attributes from form. 519 * @return array 520 */ 521 VariationForm.prototype.getChosenAttributes = function() { 522 var data = {}; 523 var count = 0; 524 var chosen = 0; 525 526 this.$attributeFields.each( function() { 527 var attribute_name = $( this ).data( 'attribute_name' ) || $( this ).attr( 'name' ); 528 var value = $( this ).val() || ''; 529 530 if ( value.length > 0 ) { 531 chosen ++; 532 } 533 534 count ++; 535 data[ attribute_name ] = value; 536 }); 537 538 return { 539 'count' : count, 540 'chosenCount': chosen, 541 'data' : data 542 }; 543 }; 544 545 /** 546 * Find matching variations for attributes. 547 */ 548 VariationForm.prototype.findMatchingVariations = function( variations, attributes ) { 549 var matching = []; 550 for ( var i = 0; i < variations.length; i++ ) { 551 var variation = variations[i]; 552 553 if ( this.isMatch( variation.attributes, attributes ) ) { 554 matching.push( variation ); 555 } 556 } 557 return matching; 558 }; 559 560 /** 561 * See if attributes match. 562 * @return {Boolean} 563 */ 564 VariationForm.prototype.isMatch = function( variation_attributes, attributes ) { 565 var match = true; 566 for ( var attr_name in variation_attributes ) { 567 if ( variation_attributes.hasOwnProperty( attr_name ) ) { 568 var val1 = variation_attributes[ attr_name ]; 569 var val2 = attributes[ attr_name ]; 570 if ( val1 !== undefined && val2 !== undefined && val1.length !== 0 && val2.length !== 0 && val1 !== val2 ) { 571 match = false; 572 } 573 } 574 } 575 return match; 576 }; 577 578 /** 579 * Show or hide the reset link. 580 */ 581 VariationForm.prototype.toggleResetLink = function( on ) { 582 if ( on ) { 583 if ( this.$resetVariations.css( 'visibility' ) === 'hidden' ) { 584 this.$resetVariations.css( 'visibility', 'visible' ).hide().fadeIn(); 585 } 586 } else { 587 this.$resetVariations.css( 'visibility', 'hidden' ); 588 } 589 }; 590 591 /** 592 * Show no matching variation message. 593 */ 594 VariationForm.prototype.showNoMatchingVariationsMsg = function() { 595 this.$form 596 .find( '.single_variation' ) 597 .after( 598 '<div role="alert">' + 599 '<p class="wc-no-matching-variations woocommerce-info">' + 600 wc_add_to_cart_variation_params.i18n_no_matching_variations_text + 601 '</p>' + 602 '</div>' 603 ) 604 .next( 'div' ) 605 .find( '.wc-no-matching-variations' ) 606 .slideDown( 200 ); 607 }; 608 609 /** 610 * Handle reset key down event for accessibility. 611 * @param {KeyboardEvent} event - The keyboard event object 612 */ 613 VariationForm.prototype.onResetKeyDown = function ( event ) { 614 if ( event.code === 'Enter' || event.code === 'Space' ) { 615 event.preventDefault(); 616 event.data.variationForm.onReset( event ); 617 } 618 }; 619 620 /** 621 * Function to call wc_variation_form on jquery selector. 622 */ 623 $.fn.wc_variation_form = function() { 624 new VariationForm( this ); 625 return this; 626 }; 627 628 /** 629 * Stores the default text for an element so it can be reset later 630 */ 631 $.fn.wc_set_content = function( content ) { 632 if ( undefined === this.attr( 'data-o_content' ) ) { 633 this.attr( 'data-o_content', this.text() ); 634 } 635 this.text( content ); 636 }; 637 638 /** 639 * Stores the default text for an element so it can be reset later 640 */ 641 $.fn.wc_reset_content = function() { 642 if ( undefined !== this.attr( 'data-o_content' ) ) { 643 this.text( this.attr( 'data-o_content' ) ); 644 } 645 }; 646 647 /** 648 * Stores a default attribute for an element so it can be reset later 649 */ 650 $.fn.wc_set_variation_attr = function( attr, value ) { 651 if ( undefined === this.attr( 'data-o_' + attr ) ) { 652 this.attr( 'data-o_' + attr, ( ! this.attr( attr ) ) ? '' : this.attr( attr ) ); 653 } 654 if ( false === value ) { 655 this.removeAttr( attr ); 656 } else { 657 this.attr( attr, value ); 658 } 659 }; 660 661 /** 662 * Reset a default attribute for an element so it can be reset later 663 */ 664 $.fn.wc_reset_variation_attr = function( attr ) { 665 if ( undefined !== this.attr( 'data-o_' + attr ) ) { 666 this.attr( attr, this.attr( 'data-o_' + attr ) ); 667 } 668 }; 669 670 /** 671 * Reset the slide position if the variation has a different image than the current one 672 */ 673 $.fn.wc_maybe_trigger_slide_position_reset = function( variation ) { 674 var $form = $( this ), 675 $product = $form.closest( '.product' ), 676 $product_gallery = $product.find( '.images' ), 677 reset_slide_position = false, 678 new_image_id = ( variation && variation.image_id ) ? variation.image_id : ''; 679 680 if ( $form.attr( 'current-image' ) !== new_image_id ) { 681 reset_slide_position = true; 682 } 683 684 $form.attr( 'current-image', new_image_id ); 685 686 if ( reset_slide_position ) { 687 $product_gallery.trigger( 'woocommerce_gallery_reset_slide_position' ); 688 } 689 }; 690 691 /** 692 * Sets product images for the chosen variation 693 */ 694 $.fn.wc_variations_image_update = function( variation ) { 695 var $form = this, 696 $product = $form.closest( '.product' ), 697 $product_gallery = $product.find( '.images' ), 698 $gallery_nav = $product.find( '.flex-control-nav' ), 699 $gallery_img = $gallery_nav.find( 'li:eq(0) img' ), 700 $product_img_wrap = $product_gallery 701 .find( '.woocommerce-product-gallery__image, .woocommerce-product-gallery__image--placeholder' ) 702 .eq( 0 ), 703 $product_img = $product_img_wrap.find( '.wp-post-image' ), 704 $product_link = $product_img_wrap.find( 'a' ).eq( 0 ); 705 706 if ( variation && variation.image && variation.image.src && variation.image.src.length > 1 ) { 707 // See if the gallery has an image with the same original src as the image we want to switch to. 708 var galleryHasImage = $gallery_nav.find( 'li img[data-o_src="' + variation.image.gallery_thumbnail_src + '"]' ).length > 0; 709 710 // If the gallery has the image, reset the images. We'll scroll to the correct one. 711 if ( galleryHasImage ) { 712 $form.wc_variations_image_reset(); 713 } 714 715 // See if gallery has a matching image we can slide to.
716 var slideToImage = $gallery_nav.find( 'li img[src="' + variation.image.gallery_thumbnail_src + '"]' ); 717 718 if ( slideToImage.length > 0 ) { 719 slideToImage.trigger( 'flexslider-click' ); 720 $form.attr( 'current-image', variation.image_id ); 721 window.setTimeout( function() { 722 $( window ).trigger( 'resize' ); 723 $product_gallery.trigger( 'woocommerce_gallery_init_zoom' ); 724 }, 20 ); 725 return; 726 } 727 728 $product_img.wc_set_variation_attr( 'src', variation.image.src ); 729 $product_img.wc_set_variation_attr( 'height', variation.image.src_h ); 730 $product_img.wc_set_variation_attr( 'width', variation.image.src_w ); 731 $product_img.wc_set_variation_attr( 'srcset', variation.image.srcset ); 732 $product_img.wc_set_variation_attr( 'sizes', variation.image.sizes ); 733 $product_img.wc_set_variation_attr( 'title', variation.image.title ); 734 $product_img.wc_set_variation_attr( 'data-caption', variation.image.caption ); 735 $product_img.wc_set_variation_attr( 'alt', variation.image.alt ); 736 $product_img.wc_set_variation_attr( 'data-src', variation.image.full_src ); 737 $product_img.wc_set_variation_attr( 'data-large_image', variation.image.full_src ); 738 $product_img.wc_set_variation_attr( 'data-large_image_width', variation.image.full_src_w ); 739 $product_img.wc_set_variation_attr( 'data-large_image_height', variation.image.full_src_h ); 740 $product_img_wrap.wc_set_variation_attr( 'data-thumb', variation.image.src ); 741 $gallery_img.wc_set_variation_attr( 'src', variation.image.gallery_thumbnail_src ); 742 $product_link.wc_set_variation_attr( 'href', variation.image.full_src ); 743 } else { 744 $form.wc_variations_image_reset(); 745 } 746 747 window.setTimeout( function() { 748 $( window ).trigger( 'resize' ); 749 $form.wc_maybe_trigger_slide_position_reset( variation ); 750 $product_gallery.trigger( 'woocommerce_gallery_init_zoom' ); 751 }, 20 ); 752 }; 753 754 /** 755 * Reset main image to defaults. 756 */ 757 $.fn.wc_variations_image_reset = function() { 758 var $form = this, 759 $product = $form.closest( '.product' ), 760 $product_gallery = $product.find( '.images' ), 761 $gallery_nav = $product.find( '.flex-control-nav' ), 762 $gallery_img = $gallery_nav.find( 'li:eq(0) img' ), 763 $product_img_wrap = $product_gallery 764 .find( '.woocommerce-product-gallery__image, .woocommerce-product-gallery__image--placeholder' ) 765 .eq( 0 ), 766 $product_img = $product_img_wrap.find( '.wp-post-image' ), 767 $product_link = $product_img_wrap.find( 'a' ).eq( 0 ); 768 769 $product_img.wc_reset_variation_attr( 'src' ); 770 $product_img.wc_reset_variation_attr( 'width' ); 771 $product_img.wc_reset_variation_attr( 'height' ); 772 $product_img.wc_reset_variation_attr( 'srcset' ); 773 $product_img.wc_reset_variation_attr( 'sizes' ); 774 $product_img.wc_reset_variation_attr( 'title' ); 775 $product_img.wc_reset_variation_attr( 'data-caption' ); 776 $product_img.wc_reset_variation_attr( 'alt' ); 777 $product_img.wc_reset_variation_attr( 'data-src' ); 778 $product_img.wc_reset_variation_attr( 'data-large_image' ); 779 $product_img.wc_reset_variation_attr( 'data-large_image_width' ); 780 $product_img.wc_reset_variation_attr( 'data-large_image_height' ); 781 $product_img_wrap.wc_reset_variation_attr( 'data-thumb' ); 782 $gallery_img.wc_reset_variation_attr( 'src' ); 783 $product_link.wc_reset_variation_attr( 'href' ); 784 }; 785 786 $(function() { 787 if ( typeof wc_add_to_cart_variation_params !== 'undefined' ) { 788 $( '.variations_form' ).each( function() { 789 $( this ).wc_variation_form(); 790 }); 791 } 792 }); 793 794 /** 795 * Matches inline variation objects to chosen attributes 796 * @deprecated 2.6.9 797 * @type {Object} 798 */ 799 var wc_variation_form_matcher = { 800 find_matching_variations: function( product_variations, settings ) { 801 var matching = []; 802 for ( var i = 0; i < product_variations.length; i++ ) { 803 var variation = product_variations[i]; 804 805 if ( wc_variation_form_matcher.variations_match( variation.attributes, settings ) ) { 806 matching.push( variation ); 807 } 808 } 809 return matching; 810 }, 811 variations_match: function( attrs1, attrs2 ) { 812 var match = true; 813 for ( var attr_name in attrs1 ) { 814 if ( attrs1.hasOwnProperty( attr_name ) ) { 815 var val1 = attrs1[ attr_name ]; 816 var val2 = attrs2[ attr_name ]; 817 if ( val1 !== undefined && val2 !== undefined && val1.length !== 0 && val2.length !== 0 && val1 !== val2 ) { 818 match = false;
819 } 820 } 821 } 822 return match; 823 } 824 }; 825 826 /** 827 * Avoids using wp.template where possible in order to be CSP compliant. 828 * wp.template uses internally eval(). 829 * @param {string} templateId 830 * @return {Function} 831 */ 832 var wp_template = function( templateId ) { 833 var html = document.getElementById( 'tmpl-' + templateId ).textContent; 834 var hard = false; 835 // any <# #> interpolate (evaluate). 836 hard = hard || /<#\s?data\./.test( html ); 837 // any data that is NOT data.variation. 838 hard = hard || /{{{?\s?data\.(?!variation\.).+}}}?/.test( html ); 839 // any data access deeper than 1 level e.g. 840 // data.variation.object.item 841 // data.variation.object['item'] 842 // data.variation.array[0] 843 hard = hard || /{{{?\s?data\.variation\.[\w-]*[^\s}]/.test ( html ); 844 if ( hard ) { 845 return wp.template( templateId ); 846 } 847 return function template ( data ) { 848 var variation = data.variation || {}; 849 return html.replace( /({{{?)\s?data\.variation\.([\w-]*)\s?(}}}?)/g, function( _, open, key, close ) { 850 // Error in the format, ignore. 851 if ( open.length !== close.length ) { 852 return ''; 853 } 854 var replacement = variation[ key ] || ''; 855 // {{{ }}} => interpolate (unescaped). 856 // {{ }} => interpolate (escaped). 857 // https://codex.wordpress.org/Javascript_Reference/wp.template 858 if ( open.length === 2 ) { 859 return window.escape( replacement ); 860 } 861 return replacement; 862 }); 863 }; 864 }; 865 866})( 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.