1/* global wc_add_to_cart_variation_params */ 2 3( function( $, window, document ) { 4 'use strict'; 5 /** 6 * Handles variation forms and attributes. 7 * 8 * @param {Object} $form The product form 9 * @param {Object} element The product element 10 * @param {Object} currentCart The current cart form 11 * @param {Object} variableProductContainers The container for the variable product 12 * @param {Object} epoObject The EPO Object 13 */ 14 var VariationForm = function( $form, element, currentCart, variableProductContainers, epoObject ) { 15 var self = this; 16 17 self.field = element; 18 self.currentCart = currentCart; 19 self.variableProductContainers = variableProductContainers; 20 self.epoObject = epoObject; 21 self.$form = $form; 22 self.$wrap = $form.closest( '.tc-epo-element-product-container-wrap' ); 23 self.$attributeFields = $form.find( '.tc-epo-variable-product-selector' ); 24 self.$singleVariation = $form.find( '.tc-epo-element-single-variation' ); 25 self.$resetVariations = $form.find( '.tc-epo-element-variable-reset-variations' ); 26 self.variationData = $form.data( 'product_variations' ); 27 28 self.useAjax = false === self.variationData; 29 self.xhr = false; 30 self.loading = true; 31 self.variationId = $form.closest( '.tc-epo-element-product-li-container' ).find( '.tc-epo-element-product-container-variation-id input.product-variation-id' ); 32 33 // Methods. 34 self.getChosenAttributes = self.getChosenAttributes.bind( self ); 35 self.findMatchingVariations = self.findMatchingVariations.bind( self ); 36 self.isMatch = self.isMatch.bind( self ); 37 self.toggleResetLink = self.toggleResetLink.bind( self ); 38 39 // Events. 40 $form.on( 'click.tc-variation-form', '.tc-epo-element-variable-reset-variations', { variationForm: self }, self.onReset ); 41 $form.on( 'tc_hide_variation', { variationForm: self }, self.onHide ); 42 $form.on( 'tc_show_variation', { variationForm: self }, self.onShow ); 43 $form.on( 'tc_reset_data', { variationForm: self }, self.onResetDisplayedVariation ); 44 $form.on( 'tc_reset_image', { variationForm: self }, self.onResetImage ); 45 $form.on( 'change.tc-variation-form', '.tc-epo-variable-product-selector', { variationForm: self }, self.onChange ); 46 $form.on( 'tc_found_variation.tc-variation-form', { variationForm: self }, self.onFoundVariation ); 47 $form.on( 'tc_check_variations.tc-variation-form', { variationForm: self }, self.onFindVariation ); 48 $form.on( 'tc_update_variation_values.tc-variation-form', { variationForm: self }, self.onUpdateAttributes ); 49 $form.on( 'refresh.tc-variation-form', { variationForm: self }, self.onRefreshContainer ); 50 $form.on( 'update_field.tc-variation-form', { variationForm: self }, self.onUpdateField ); 51 52 self.variationId.val( '' ); 53 54 // Init after gallery. 55 setTimeout( function() { 56 $form.trigger( 'refresh' ); 57 $form.trigger( 'tc_variation_form' ); 58 self.loading = false; 59 }, 100 ); 60 }; 61 62 /** 63 * Refresh variations container 64 * 65 * @param {Event} event The event Object. 66 */ 67 VariationForm.prototype.onRefreshContainer = function( event ) { 68 var form = event.data.variationForm; 69 70 form.variableProductContainers.find( '.tc-epo-element-product-container' ).removeClass( 'variations_form' ); 71 72 form.$form.addClass( 'variations_form' ); 73 74 form.$form.trigger( 'tc_check_variations.tc-variation-form' ); 75 }; 76 77 /** 78 * Reset all fields. 79 * 80 * @param {Event} event The event Object. 81 * @param {Object} variation The variation Object. 82 */ 83 VariationForm.prototype.onUpdateField = function( event, variation ) { 84 var form = event.data.variationForm; 85 var field; 86 var associatedSetter; 87 var associatedPrice; 88 var associatedRawPrice; 89 var associatedOriginalPrice; 90 var associatedRawOriginalPrice; 91 92 event.preventDefault(); 93 94 if ( ! variation ) { 95 field = form.field; 96 97 associatedSetter = field; 98 if ( field.is( 'select' ) ) { 99 associatedSetter = field.find( 'option:selected' ); 100 } 101 102 associatedPrice = 0; 103 associatedRawPrice = 0; 104 associatedOriginalPrice = 0; 105 associatedRawOriginalPrice = 0; 106 107 field.data( 'price_set', 1 ); 108 associatedSetter.data( 'associated_price_set', 1 ); 109 associatedSetter.data( 'price_set', 1 ); 110 associatedSetter.data( 'raw_price', associatedRawPrice ); 111 associatedSetter.data( 'raw_original_price', associatedRawOriginalPrice ); 112 associatedSetter.data( 'price', associatedPrice ); 113 associatedSetter.data( 'original_price', associatedOriginalPrice ); 114 115 field.data( 'price-changed', 1 ); 116 117 form.$form.trigger( { 118 type: 'tm-epo-update', 119 norules: 2 120 } ); 121 form.currentCart.trigger( { 122 type: 'tm-epo-update', 123 norules: 2 124 } ); 125 } 126 }; 127 128 /** 129 * Reset all fields. 130 * 131 * @param {Event} event The event Object. 132 */ 133 VariationForm.prototype.onReset = function( event ) { 134 event.preventDefault(); 135 event.data.variationForm.$attributeFields.val( '' ).trigger( 'change' ); 136 event.data.variationForm.$form.trigger( 'tc_reset_data' ); 137 }; 138 139 /** 140 * When a variation is hidden. 141 * 142 * @param {Event} event The event Object. 143 */ 144 VariationForm.prototype.onHide = function( event ) { 145 event.preventDefault(); 146 }; 147 /** 148 * When a variation is shown.
149 * 150 * @param {Event} event The event Object. 151 */ 152 VariationForm.prototype.onShow = function( event ) { 153 event.preventDefault(); 154 }; 155 /** 156 * When displayed variation data is reset. 157 * 158 * @param {Event} event The event Object. 159 */ 160 VariationForm.prototype.onResetDisplayedVariation = function( event ) { 161 var form = event.data.variationForm; 162 form.$form.find( '.product-meta' ).find( '.tc-product-sku' ).tc_reset_content(); 163 form.$form.trigger( 'tc_reset_image' ); 164 form.$singleVariation.slideUp( 200 ).trigger( 'tc_hide_variation' ); 165 }; 166 167 /** 168 * When the product image is reset. 169 * 170 * @param {Event} event The event Object. 171 */ 172 VariationForm.prototype.onResetImage = function( event ) { 173 event.data.variationForm.$form.tc_variations_image_update( false ); 174 }; 175 176 /** 177 * Looks for matching variations for current selected attributes. 178 * 179 * @param {Event} event The event Object. 180 */ 181 VariationForm.prototype.onFindVariation = function( event ) { 182 var form = event.data.variationForm; 183 var attributes = form.getChosenAttributes(); 184 var currentAttributes = attributes.data; 185 var cpfElement; 186 var matching_variations; 187 var variation; 188 189 if ( attributes.count === attributes.chosenCount ) { 190 if ( form.useAjax ) { 191 if ( typeof wc_add_to_cart_variation_params === 'undefined' ) { 192 return; 193 } 194 if ( form.xhr ) { 195 form.xhr.abort(); 196 } 197 cpfElement = form.$form.closest( '.cpf-type-product' ); 198 form.$form.block( { message: null, overlayCSS: { background: '#fff', opacity: 0.6 } } ); 199 currentAttributes.product_id = parseInt( form.$form.data( 'product_id' ), 10 ); 200 currentAttributes.discount = cpfElement.attr( 'data-discount' ); 201 currentAttributes.discount_type = cpfElement.attr( 'data-discount-type' ); 202 currentAttributes.discount_exclude_addons = cpfElement.attr( 'data-discount-exclude-addons' ); 203 204 form.xhr = $.ajax( { 205 url: wc_add_to_cart_variation_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'get_variation' ), 206 type: 'POST', 207 data: currentAttributes, 208 success: function( svariation ) { 209 if ( svariation ) { 210 form.$form.trigger( 'tc_found_variation', [ svariation ] ); 211 } else { 212 form.$form.trigger( 'tc_reset_data' ); 213 attributes.chosenCount = 0; 214 215 if ( ! form.loading ) { 216 form.$form.find( '.tc-epo-element-single-variation' ).after( '<p class="wc-no-matching-variations woocommerce-info">' + wc_add_to_cart_variation_params.i18n_no_matching_variations_text + '</p>' ); 217 form.$form.find( '.wc-no-matching-variations' ).slideDown( 200 ); 218 form.$form.trigger( 'update_field', [] ); 219 } 220 } 221 }, 222 complete: function() { 223 form.$form.unblock(); 224 } 225 } ); 226 } else { 227 form.$form.trigger( 'tc_update_variation_values' ); 228 229 matching_variations = form.findMatchingVariations( form.variationData, currentAttributes ); 230 variation = matching_variations.shift(); 231 232 if ( variation ) { 233 form.$form.trigger( 'tc_found_variation', [ variation ] ); 234 } else { 235 form.$form.trigger( 'tc_reset_data' ); 236 attributes.chosenCount = 0; 237 238 if ( ! form.loading ) { 239 form.$form.find( '.tc-epo-element-single-variation' ).after( '<p class="wc-no-matching-variations woocommerce-info">' + wc_add_to_cart_variation_params.i18n_no_matching_variations_text + '</p>' ); 240 form.$form.find( '.wc-no-matching-variations' ).slideDown( 200 ); 241 } 242 } 243 } 244 } else { 245 form.variationId.val( '' ).trigger( 'change' ); 246 form.$form.trigger( 'update_field', [] ); 247 form.$form.trigger( 'tc_update_variation_values' ); 248 form.$form.trigger( 'tc_reset_data' ); 249 } 250 251 // Show reset link. 252 form.toggleResetLink( attributes.chosenCount > 0 ); 253 }; 254 255 /** 256 * Triggered when a variation has been found which matches all attributes. 257 * 258 * @param {Event} event The event Object. 259 * @param {Object} variation The variation Object. 260 */ 261 VariationForm.prototype.onFoundVariation = function( event, variation ) { 262 var form = event.data.variationForm, 263 $sku = form.$form.find( '.product-meta' ).find( '.tc-product-sku' ), 264 $qtyWrap = form.$wrap.find( '.tm-quantity-alt' ), 265 $qty = $qtyWrap.find( 'input.tm-qty-alt' ), 266 qtyMin = $.epoAPI.math.toInt( $qty.attr( 'data-min' ) ), 267 qtyMax = $.epoAPI.math.toInt( $qty.attr( 'data-max' ) ), 268 dataQtyMax = $qty.attr( 'data-max' ), 269 purchasable = true, 270 template = false, 271 $template_html = ''; 272 273 if ( variation.sku ) { 274 $sku.tc_set_content( variation.sku ); 275 } else { 276 $sku.tc_reset_content(); 277 } 278 279 form.$form.tc_variations_image_update( variation ); 280 if ( ! variation.variation_is_visible ) { 281 template = wpTemplate( 'unavailable-variation-template' ); 282 } else { 283 template = wpTemplate( 'variation-template' ); 284 } 285 286 if ( form.field.attr( 'data-no-price' ) ) { 287 variation.display_price = ''; 288 variation.display_regular_price = ''; 289 variation.price_html = ''; 290 } 291 292 form.$form.find( '.tc-epo-element-product-container-cart' ).find( '.stock' ).remove(); 293 294 $template_html = template( { 295 variation: variation 296 } ); 297 $template_html = $template_html.replace( '/*<![CDATA[*/', '' ); 298 $template_html = $template_html.replace( '/*]]>*/', '' ); 299 form.$singleVariation.html( $template_html ); 300 form.variationId.val( variation.variation_id ).trigger( 'change' ); 301 302 // Hide or show qty input 303 if ( variation.is_sold_individually === 'yes' ) { 304 $qty.val( '1' ).attr( 'min', '1' ).attr( 'max', '1' ); 305 $qtyWrap.hide(); 306 } else { 307 if ( variation.min_qty !== '' && variation.min_qty > qtyMin && qtyMin > 0 ) { 308 qtyMin = variation.min_qty; 309 } 310 311 if ( variation.max_qty !== '' && variation.max_qty < qtyMax ) { 312 qtyMax = variation.max_qty; 313 } 314 315 if ( qtyMin !== '' ) { 316 $qty.attr( 'min', qtyMin ); 317 } else { 318 $qty.removeAttr( 'min' ); 319 } 320 if ( dataQtyMax !== '' ) { 321 $qty.attr( 'max', qtyMax ); 322 } else { 323 $qty.removeAttr( 'max' ); 324 } 325 326 if ( qtyMax && $.epoAPI.math.toInt( $qty.val() ) > qtyMax ) { 327 $qty.val( qtyMax ); 328 } 329 if ( qtyMin > $.epoAPI.math.toInt( $qty.val() ) ) { 330 $qty.val( qtyMin ); 331 } 332 $qtyWrap.show(); 333 } 334 335 if ( ! variation.is_purchasable || ! variation.is_in_stock || ! variation.variation_is_visible ) { 336 purchasable = false;
337 $qty.val( '0' ).attr( 'min', '0' ).attr( 'max', '0' ); 338 $qtyWrap.hide(); 339 } 340 341 $qty.trigger( 'change' ); 342 343 // Reveal 344 if ( $.epoAPI.util.trim( form.$singleVariation.text() ) ) { 345 form.$singleVariation.slideDown( 200 ).trigger( 'tc_show_variation', [ variation, purchasable ] ); 346 } else { 347 form.$singleVariation.show().trigger( 'tc_show_variation', [ variation, purchasable ] ); 348 } 349 form.$form.trigger( 'update_field', [ variation ] ); 350 }; 351 352 /** 353 * Triggered when an attribute field changes. 354 * 355 * @param {Event} event The event Object. 356 */ 357 VariationForm.prototype.onChange = function( event ) { 358 var form = event.data.variationForm; 359 var field = form.field; 360 361 form.variationId.val( '' ).trigger( 'change' ); 362 form.$form.find( '.wc-no-matching-variations' ).remove(); 363 364 if ( form.useAjax ) { 365 form.$form.trigger( 'tc_check_variations' ); 366 } else { 367 form.$form.trigger( 'woocommerce_variation_select_change' ); 368 form.$form.trigger( 'tc_check_variations' ); 369 } 370 371 // Custom event for when variation selection has been changed 372 form.$form.trigger( 'woocommerce_variation_has_changed' ); 373 374 setTimeout( function() { 375 field.closest( '.tcowl-carousel' ).trigger( 'refresh.owl.carousel' ); 376 }, 50 ); 377 }; 378 379 /** 380 * Updates attributes in the DOM to show valid values. 381 * 382 * @param {Event} event The event Object. 383 */ 384 VariationForm.prototype.onUpdateAttributes = function( event ) { 385 var form = event.data.variationForm, 386 attributes = form.getChosenAttributes(), 387 currentAttributes = attributes.data; 388 389 if ( form.useAjax ) { 390 return; 391 } 392 393 // Loop through selects and disable/enable options based on selections. 394 form.$attributeFields.each( function( index, el ) { 395 var current_attr_select = $( el ); 396 var current_attr_name = current_attr_select.data( 'attribute_name' ) || current_attr_select.attr( 'name' ); 397 var show_option_none = $( el ).data( 'show_option_none' ); 398 var option_gt_filter = ':gt(0)'; 399 var attached_options_count = 0; 400 var new_attr_select = $( '<select/>' ); 401 var selected_attr_val = current_attr_select.val() || ''; 402 var selected_attr_val_valid = true; 403 var refSelect; 404 var checkAttributes; 405 var variations; 406 var num; 407 var i; 408 var len; 409 var variationAttributes; 410 var attr_name; 411 var attr_val; 412 var variation_active; 413 var $option_elements; 414 var $option_element; 415 var option_value; 416 417 // Reference options set at first. 418 if ( ! current_attr_select.data( 'attribute_html' ) ) { 419 refSelect = current_attr_select.clone(); 420 421 refSelect.find( 'option' ).prop( 'disabled', false ).prop( 'selected', false ).removeAttr( 'attached' ); 422 423 current_attr_select.data( 'attribute_options', refSelect.find( 'option' + option_gt_filter ).get() ); // Legacy data attribute. 424 current_attr_select.data( 'attribute_html', refSelect.html() ); 425 } 426 427 new_attr_select.html( current_attr_select.data( 'attribute_html' ) ); 428 429 // The attribute of this select field should not be taken into account when calculating its matching variations: 430 // The constraints of this attribute are shaped by the values of the other attributes. 431 checkAttributes = $.extend( true, {}, currentAttributes ); 432 433 checkAttributes[ current_attr_name ] = ''; 434 435 variations = form.findMatchingVariations( form.variationData, checkAttributes ); 436 437 // Loop through variations. 438 for ( num in variations ) { 439 if ( typeof variations[ num ] !== 'undefined' ) { 440 variationAttributes = variations[ num ].attributes; 441 442 for ( attr_name in variationAttributes ) { 443 if ( Object.prototype.hasOwnProperty.call( variationAttributes, attr_name ) ) { 444 attr_val = variationAttributes[ attr_name ]; 445 variation_active = ''; 446 447 if ( attr_name === current_attr_name ) { 448 if ( variations[ num ].variation_is_active ) { 449 variation_active = 'enabled'; 450 } 451 452 if ( attr_val ) { 453 // Decode entities. 454 attr_val = $( '<div/>' ).html( attr_val ).text(); 455 456 // Attach to matching options by value. This is done to compare 457 // TEXT values rather than any HTML entities. 458 $option_elements = new_attr_select.find( 'option' ); 459 if ( $option_elements.length ) { 460 for ( i = 0, len = $option_elements.length; i < len; i++ ) { 461 $option_element = $( $option_elements[ i ] ); 462 option_value = $option_element.val(); 463 464 if ( attr_val === option_value ) { 465 $option_element.addClass( 'attached ' + variation_active ); 466 break; 467 } 468 } 469 } 470 } else { 471 // Attach all apart from placeholder. 472 new_attr_select.find( 'option:gt(0)' ).addClass( 'attached ' + variation_active ); 473 } 474 } 475 } 476 } 477 } 478 } 479 480 // Count available options. 481 attached_options_count = new_attr_select.find( 'option.attached' ).length; 482 483 // Check if current selection is in attached options. 484 if ( selected_attr_val ) { 485 selected_attr_val_valid = false;
486 487 if ( 0 !== attached_options_count ) { 488 new_attr_select.find( 'option.attached.enabled' ).each( function() { 489 var thisVal = $( this ).val(); 490 491 if ( selected_attr_val === thisVal ) { 492 selected_attr_val_valid = true; 493 return false; // break. 494 } 495 } ); 496 } 497 } 498 499 /** 500 * Detach the placeholder if: 501 * - Valid options exist. 502 * - The current selection is non-empty. 503 * - The current selection is valid. 504 * - Placeholders are not set to be permanently visible. 505 */ 506 if ( attached_options_count > 0 && selected_attr_val && selected_attr_val_valid && 'no' === show_option_none ) { 507 new_attr_select.find( 'option:first' ).remove(); 508 option_gt_filter = ''; 509 } 510 511 // Detach unattached. 512 new_attr_select.find( 'option' + option_gt_filter + ':not(.attached)' ).remove(); 513 514 // Finally, copy to DOM and set value. 515 current_attr_select.html( new_attr_select.html() ); 516 current_attr_select.find( 'option' + option_gt_filter + ':not(.enabled)' ).prop( 'disabled', true ); 517 518 // Choose selected value. 519 if ( selected_attr_val ) { 520 // If the previously selected value is no longer available, fall back to the placeholder (it's going to be there). 521 if ( selected_attr_val_valid ) { 522 current_attr_select.val( selected_attr_val ); 523 } else { 524 current_attr_select.val( '' ).trigger( 'change' ); 525 } 526 } else { 527 current_attr_select.val( '' ); // No change event to prevent infinite loop. 528 } 529 } ); 530 531 // Custom event for when variations have been updated. 532 form.$form.trigger( 'woocommerce_update_variation_values' ); 533 }; 534 535 /** 536 * Get chosen attributes from form. 537 */ 538 VariationForm.prototype.getChosenAttributes = function() { 539 var data = {}; 540 var count = 0; 541 var chosen = 0; 542 543 this.$attributeFields.each( function() { 544 var attribute_name = $( this ).data( 'attribute_name' ) || $( this ).attr( 'name' ); 545 var value = $( this ).val() || ''; 546 547 if ( value.length > 0 ) { 548 chosen++; 549 } 550 551 count++; 552 data[ attribute_name ] = value; 553 } ); 554 555 return { 556 count: count, 557 chosenCount: chosen, 558 data: data 559 }; 560 }; 561 562 /** 563 * Find matching variations for attributes. 564 * 565 * @param {Array} variations Variations. 566 * @param {Array} attributes attributes. 567 */ 568 VariationForm.prototype.findMatchingVariations = function( variations, attributes ) { 569 var matching = []; 570 var i; 571 var variation; 572 for ( i = 0; i < variations.length; i++ ) { 573 variation = variations[ i ]; 574 575 if ( this.isMatch( variation.attributes, attributes ) ) { 576 matching.push( variation ); 577 } 578 } 579 return matching; 580 }; 581 582 /** 583 * See if attributes match. 584 * 585 * @param {Array} variation_attributes Variation attributes. 586 * @param {Array} attributes attributes. 587 */ 588 VariationForm.prototype.isMatch = function( variation_attributes, attributes ) { 589 var match = true; 590 var val1; 591 var val2; 592 var attr_name; 593 for ( attr_name in variation_attributes ) { 594 if ( Object.prototype.hasOwnProperty.call( variation_attributes, attr_name ) ) { 595 val1 = variation_attributes[ attr_name ]; 596 val2 = attributes[ attr_name ]; 597 if ( val1 !== undefined && val2 !== undefined && val1.length !== 0 && val2.length !== 0 && val1 !== val2 ) { 598 match = false; 599 } 600 } 601 } 602 return match; 603 }; 604 605 /** 606 * Show or hide the reset link. 607 * 608 * @param {bool} on true or false. 609 */ 610 VariationForm.prototype.toggleResetLink = function( on ) { 611 if ( on ) { 612 if ( this.$resetVariations.css( 'visibility' ) === 'hidden' ) { 613 this.$resetVariations.css( 'visibility', 'visible' ).hide().fadeIn(); 614 } 615 } else { 616 this.$resetVariations.css( 'visibility', 'hidden' ); 617 } 618 }; 619 620 /** 621 * Function to call tc_variation_form on jquery selector. 622 * 623 * @param {Object} element The product element 624 * @param {Object} currentCart The current cart form 625 * @param {Object} variableProductContainers The container for the variable product 626 * @param {Object} thisEpoObject The EPO Object 627 */ 628 $.fn.tc_product_variation_form = function( element, currentCart, variableProductContainers, thisEpoObject ) { 629 new VariationForm( this, element, currentCart, variableProductContainers, thisEpoObject ); 630 this.trigger( 'tc_product_variation_form' ); 631 return this; 632 }; 633 634 /** 635 * Stores the default text for an element so it can be reset later 636 * 637 * @param {string} content Content to set. 638 */ 639 $.fn.tc_set_content = function( content ) { 640 if ( undefined === this.attr( 'data-o_content' ) ) { 641 this.attr( 'data-o_content', this.text() ); 642 } 643 this.text( content ); 644 }; 645 /** 646 * Stores the default text for an element so it can be reset later 647 */ 648 $.fn.tc_reset_content = function() { 649 if ( undefined !== this.attr( 'data-o_content' ) ) { 650 this.text( this.attr( 'data-o_content' ) ); 651 } 652 }; 653 654 /** 655 * Stores a default attribute for an element so it can be reset later 656 * 657 * @param {string} attr Attribute name. 658 * @param {string} value Value name. 659 */ 660 $.fn.tc_set_variation_attr = function( attr, value ) { 661 if ( undefined === this.attr( 'data-o_' + attr ) ) { 662 this.attr( 'data-o_' + attr, ! this.attr( attr ) ? '' : this.attr( attr ) ); 663 } 664 if ( false === value ) { 665 this.removeAttr( attr ); 666 } else { 667 this.attr( attr, value ); 668 } 669 }; 670 671 /** 672 * Reset a default attribute for an element so it can be reset later 673 * 674 * @param {string} attr Attribute name. 675 */ 676 $.fn.tc_reset_variation_attr = function( attr ) { 677 if ( undefined !== this.attr( 'data-o_' + attr ) ) { 678 this.attr( attr, this.attr( 'data-o_' + attr ) ); 679 } 680 }; 681 /** 682 * Sets product images for the chosen variation 683 * 684 * @param {Object} variation variation object. 685 */ 686 $.fn.tc_variations_image_update = function( variation ) { 687 var $form = this, 688 $product_img_wrap = $form.find( '.tc-product-image, .woocommerce-product-gallery__image, .woocommerce-product-gallery__image--placeholder' ).eq( 0 ), 689 $product_img = $product_img_wrap.find( '.wp-post-image' ), 690 $product_link = $product_img_wrap.find( 'a' ).eq( 0 ); 691 692 if ( variation && variation.image && variation.image.src && variation.image.src.length > 1 ) { 693 $product_img.tc_set_variation_attr( 'src', variation.image.src ); 694 $product_img.tc_set_variation_attr( 'height', variation.image.src_h ); 695 $product_img.tc_set_variation_attr( 'width', variation.image.src_w ); 696 $product_img.tc_set_variation_attr( 'srcset', variation.image.srcset ); 697 $product_img.tc_set_variation_attr( 'sizes', variation.image.sizes ); 698 $product_img.tc_set_variation_attr( 'title', variation.image.title ); 699 $product_img.tc_set_variation_attr( 'data-caption', variation.image.caption ); 700 $product_img.tc_set_variation_attr( 'alt', variation.image.alt ); 701 $product_img.tc_set_variation_attr( 'data-src', variation.image.full_src ); 702 $product_img.tc_set_variation_attr( 'data-large_image', variation.image.full_src ); 703 $product_img.tc_set_variation_attr( 'data-large_image_width', variation.image.full_src_w ); 704 $product_img.tc_set_variation_attr( 'data-large_image_height', variation.image.full_src_h ); 705 $product_img_wrap.tc_set_variation_attr( 'data-thumb', variation.image.src ); 706 $product_link.tc_set_variation_attr( 'href', variation.image.full_src ); 707 } else { 708 $form.tc_variations_image_reset(); 709 } 710 }; 711 712 /** 713 * Reset main image to defaults. 714 */ 715 $.fn.tc_variations_image_reset = function() { 716 var $form = this, 717 $product_img_wrap = $form.find( '.tc-product-image, .woocommerce-product-gallery__image, .woocommerce-product-gallery__image--placeholder' ).eq( 0 ), 718 $product_img = $product_img_wrap.find( '.wp-post-image' ), 719 $product_link = $product_img_wrap.find( 'a' ).eq( 0 ); 720 721 $product_img.tc_reset_variation_attr( 'src' ); 722 $product_img.tc_reset_variation_attr( 'width' ); 723 $product_img.tc_reset_variation_attr( 'height' ); 724 $product_img.tc_reset_variation_attr( 'srcset' ); 725 $product_img.tc_reset_variation_attr( 'sizes' ); 726 $product_img.tc_reset_variation_attr( 'title' ); 727 $product_img.tc_reset_variation_attr( 'data-caption' ); 728 $product_img.tc_reset_variation_attr( 'alt' ); 729 $product_img.tc_reset_variation_attr( 'data-src' ); 730 $product_img.tc_reset_variation_attr( 'data-large_image' ); 731 $product_img.tc_reset_variation_attr( 'data-large_image_width' ); 732 $product_img.tc_reset_variation_attr( 'data-large_image_height' ); 733 $product_img_wrap.tc_reset_variation_attr( 'data-thumb' ); 734 $product_link.tc_reset_variation_attr( 'href' ); 735 }; 736 737 /** 738 * Avoids using wp.template where possible in order to be CSP compliant. 739 * wp.template uses internally eval(). 740 * 741 * @param {string} templateId The id of the template. 742 */ 743 function wpTemplate( templateId ) { 744 var html = document.getElementById( 'tmpl-' + templateId ).textContent; 745 var hard = false;
746 // any <# #> interpolate (evaluate). 747 hard = hard || /<#\s?data\./.test( html ); 748 // any data that is NOT data.variation. 749 hard = hard || /{{{?\s?data\.(?!variation\.).+}}}?/.test( html ); 750 // any data access deeper than 1 level e.g. 751 // data.variation.object.item 752 // data.variation.object['item'] 753 // data.variation.array[0] 754 hard = hard || /{{{?\s?data\.variation\.[\w-]*[^\s}]/.test( html ); 755 if ( hard ) { 756 return window.wp.template( templateId ); 757 } 758 return function template( data ) { 759 var variation = data.variation || {}; 760 var replacement; 761 return html.replace( /({{{?)\s?data\.variation\.([\w-]*)\s?(}}}?)/g, function( _, open, key, close ) { 762 // Error in the format, ignore. 763 if ( open.length !== close.length ) { 764 return ''; 765 } 766 replacement = variation[ key ] || ''; 767 // {{{ }}} => interpolate (unescaped). 768 // {{ }} => interpolate (escaped). 769 // https://codex.wordpress.org/Javascript_Reference/wp.template 770 if ( open.length === 2 ) { 771 return window.escape( replacement ); 772 } 773 return replacement; 774 } ); 775 }; 776 } 777}( window.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.