1;(function ($) { 2 'use strict'; 3 4 /** 5 * Is there at least one in-stock, purchasable variation that matches the 6 * given attribute value combined with the attributes already chosen? 7 * 8 * Drives stock-aware swatch greying independently of the WooCommerce 9 * "Hide out of stock items" setting. An attribute value in a variation is 10 * treated as a wildcard when it is empty (the "any" value). 11 * 12 * @param {string} attrName e.g. "attribute_pa_size" 13 * @param {string} value term slug for this swatch 14 * @param {Object} chosen map of attribute_name -> selected slug 15 * @param {Array} variations variation data (is_in_stock/is_purchasable per item) 16 * @returns {boolean} true when no data is available, so existing behavior stands 17 */ 18 function matchingVariationInStock(attrName, value, chosen, variations) { 19 if (!Array.isArray(variations)) { 20 return true; 21 } 22 23 return variations.some(function (variation) { 24 if (!(variation.is_in_stock && variation.is_purchasable)) { 25 return false; 26 } 27 28 var attrs = variation.attributes || {}; 29 30 // This attribute must match the swatch value (or be a wildcard). 31 var ownValue = attrs[attrName]; 32 if (typeof ownValue !== 'undefined' && ownValue !== '' && ownValue !== value) { 33 return false; 34 } 35 36 // Every other already-selected attribute must match (or be a wildcard). 37 for (var key in chosen) { 38 if (key === attrName || !chosen.hasOwnProperty(key)) { 39 continue; 40 } 41 var otherValue = attrs[key]; 42 if (typeof otherValue !== 'undefined' && otherValue !== '' && otherValue !== chosen[key]) { 43 return false; 44 } 45 } 46 47 return true; 48 }); 49 } 50 51 /** 52 * SIMPLIFIED: Original variation swatches form - PHP-only optimized 53 * No AJAX delays, no loading states, instant response 54 */ 55 $.fn.tawcvs_variation_swatches_form = function () { 56 return this.each(function () { 57 var $form = $(this); 58 59 // OPTIMIZED: All data is loaded upfront by PHP, no special handling needed 60 if (typeof $form.find(".tawcvs-available-product-variation").data("product_variations") !== "undefined") { 61 $form.data("product_variations", $form.find(".tawcvs-available-product-variation").data("product_variations")) 62 .trigger('reload_product_variations') 63 .trigger('update_variation_values'); 64 } 65 66 $form 67 .addClass('swatches-support') 68 .on("found_variation", function (event, variation) { 69 $form.change_variation_image_on_shop_page(variation); 70 }) 71 .on("reset_image", function (event) { 72 $form.change_variation_image_on_shop_page(false); 73 }) 74 .on('click', '.swatch', function (e) { 75 e.preventDefault(); 76 e.stopImmediatePropagation(); 77 78 var $el = $(this); 79 let $select = false, value = false; 80 81 // PERFORMANCE: Quick debounce for rapid clicks 82 if ($el.data('clicking')) return; 83 $el.data('clicking', true); 84 setTimeout(() => $el.removeData('clicking'), 50); 85 86 if (!$("body").hasClass("single-product") && 87 $("body").find(".wc-product-table-wrapper").length) { 88 //For product table page 89 let attribute_name = $el.closest('.tawcvs-swatches').data("attribute_name"); 90 $select = $el.closest('.variations').find('select[data-attribute_name="' + attribute_name + '"]'); 91 value = $el.attr('data-value'); 92 } else { 93 //For normal variable product page 94 $select = $el.closest('.value').find('select'); 95 value = $el.attr('data-value'); 96 } 97 98 if ($el.hasClass('disabled')) { 99 return; 100 } 101 102 //Woo product bundle plugin uses "select" class instead of "value" class 103 if (!$select.length) { 104 if ($form.closest(".woosb-product").length) { 105 $select = $el.closest('.select').find('select'); 106 } 107 } 108 109 //Disabling other swatches, then resetting the select value to empty 110 $el.parents('.tawcvs-swatches').find(".swatch.selected").each(function () { 111 $(this).not($el).removeClass("selected"); 112 $select.val(''); 113 }) 114 115 // For old WC 116 $select.trigger('focusin'); 117 118 // Check if this combination is available 119 if (!$select.find('option[value="' + value + '"]').length) { 120 $el.siblings('.swatch').removeClass('selected'); 121 $select.val('').change(); 122 $form.trigger('tawcvs_no_matching_variations', [$el]); 123 return; 124 } 125 126 if ($el.hasClass('selected')) { 127 $select.val(''); 128 $el.removeClass('selected'); 129 130 if ($el.attr('type') == 'radio') { 131 setTimeout(function() { 132 $el.prop('checked', false); 133 }, 100); 134 } 135 } else { 136 $el.addClass('selected').siblings('.selected').removeClass('selected'); 137 $select.val(value); 138 139 if ($el.attr('type') == 'radio') { 140 setTimeout(function() { 141 $el.prop('checked', true); 142 }, 100); 143 } 144 } 145 146 $select.change(); 147 }) 148 .on('click', '.reset_variations', function () { 149 $form.find('.swatch.selected').removeClass('selected'); 150 if( !$form.find('.swatch.disabled').hasClass('blur-cross') ){ 151 $form.find('.swatch.disabled').removeClass('disabled'); 152 } 153 154 if ($form.find('input[type="radio"]').length > 1) { 155 $form.find('input[type="radio"]').prop('checked', false); 156 } 157 }) 158 .on('woocommerce_update_variation_values', function () { 159 // OPTIMIZED: Efficient variation value updates 160 setTimeout(function () { 161 // Stock-aware data: works regardless of the WooCommerce 162 // "Hide out of stock items" setting. Bails (returns "available") 163 // when no variation data is present so existing behavior stands. 164 var variations = $form.data('product_variations'); 165 166 // Attributes the shopper has currently selected. 167 var chosen = {}; 168 $form.find('.variation-selector select').each(function () { 169 var name = $(this).data('attribute_name') || $(this).attr('name'), 170 val = $(this).val(); 171 if (name && val) { 172 chosen[name] = val; 173 } 174 }); 175 176 $form.find('.variation-selector').each(function () { 177 var $variationSelector = $(this), 178 $select = $variationSelector.find('select'), 179 $options = $select.find('option'), 180 $selected = $options.filter(':selected'), 181 attrName = $select.data('attribute_name') || $select.attr('name'), 182 values = []; 183 184 $options.each(function (index, option) { 185 if (option.value !== '') { 186 values.push(option.value); 187 } 188 }); 189 190 $variationSelector.closest('.value').find('.swatch').each(function () { 191 var $swatch = $(this), 192 value = $swatch.attr('data-value'); 193 194 $swatch.closest('.swatch-item-wrapper').show(); 195 196 if (values.indexOf(value) > -1) { 197 if( !$swatch.hasClass('blur-cross')) { 198 $swatch.removeClass('disabled'); 199 } 200 201 // Combo exists but is out of stock for the current 202 // selection -> disable it the same way as a missing combo. 203 if (!$swatch.hasClass('blur-cross') && 204 !matchingVariationInStock(attrName, value, chosen, variations)) { 205 $swatch.addClass('disabled'); 206 207 if ($swatch.closest('.tawcvs-swatches').hasClass('oss-hide')) { 208 $swatch.closest('.swatch-item-wrapper').hide(); 209 } 210 211 if ($selected.length && value === $selected.val()) { 212 $swatch.removeClass('selected'); 213 } 214 } 215 } else { 216 $swatch.addClass('disabled'); 217 218 if ($swatch.closest('.tawcvs-swatches').hasClass('oss-hide')) { 219 $swatch.closest('.swatch-item-wrapper').hide(); 220 } 221 222 if ($selected.length && value === $selected.val()) { 223 $swatch.removeClass('selected'); 224 } 225 } 226 }); 227 }); 228 }, 50); // Faster response time 229 }) 230 .on('tawcvs_no_matching_variations', function () { 231 window.alert(wc_add_to_cart_variation_params.i18n_no_matching_variations_text); 232 }); 233 }); 234 }; 235 236 $.fn.change_variation_image_on_shop_page = function (variation) { 237 var $product = $(this).closest('.product'), 238 $product_img = $product.find('.woocommerce-LoopProduct-link img'); 239 240 if ($product_img.length !== 1) { 241 return false;
242 } 243 244 if (variation && variation.image && variation.image.src && variation.image.src.length > 1) { 245 $product_img.wc_set_variation_attr('src', variation.image.src); 246 $product_img.wc_set_variation_attr('height', variation.image.src_h); 247 $product_img.wc_set_variation_attr('width', variation.image.src_w); 248 $product_img.wc_set_variation_attr('srcset', variation.image.srcset); 249 $product_img.wc_set_variation_attr('sizes', variation.image.sizes); 250 $product_img.wc_set_variation_attr('title', variation.image.title); 251 $product_img.wc_set_variation_attr('data-caption', variation.image.caption); 252 $product_img.wc_set_variation_attr('alt', variation.image.alt); 253 $product_img.wc_set_variation_attr('data-src', variation.image.full_src); 254 $product_img.wc_set_variation_attr('data-large_image', variation.image.full_src); 255 $product_img.wc_set_variation_attr('data-large_image_width', variation.image.full_src_w); 256 $product_img.wc_set_variation_attr('data-large_image_height', variation.image.full_src_h); 257 } else { 258 $product_img.wc_reset_variation_attr('src'); 259 $product_img.wc_reset_variation_attr('width'); 260 $product_img.wc_reset_variation_attr('height'); 261 $product_img.wc_reset_variation_attr('srcset'); 262 $product_img.wc_reset_variation_attr('sizes'); 263 $product_img.wc_reset_variation_attr('title'); 264 $product_img.wc_reset_variation_attr('data-caption'); 265 $product_img.wc_reset_variation_attr('alt'); 266 $product_img.wc_reset_variation_attr('data-src'); 267 $product_img.wc_reset_variation_attr('data-large_image'); 268 $product_img.wc_reset_variation_attr('data-large_image_width'); 269 $product_img.wc_reset_variation_attr('data-large_image_height'); 270 } 271 } 272 273 //Tracking the reset_variations button on change visibility -> change the corresponding display state 274 function toggle_hidden_variation_btn() { 275 const resetVariationNodes = document.getElementsByClassName('reset_variations'); 276 if (resetVariationNodes.length) { 277 Array.prototype.forEach.call(resetVariationNodes, function (resetVariationEle) { 278 let observer = new MutationObserver(function () { 279 if (resetVariationEle.style.visibility !== 'hidden') { 280 resetVariationEle.style.display = 'block'; 281 } else { 282 resetVariationEle.style.display = 'none'; 283 } 284 }); 285 observer.observe(resetVariationEle, {attributes: true, childList: true}); 286 }) 287 } 288 } 289 290 // OPTIMIZED: Performance monitoring for large products 291 function logPerformanceInfo() { 292 if (typeof tawcvs_config !== 'undefined' && console && console.log) { 293 const $forms = $('.variations_form'); 294 const $largeProductForms = $forms.filter(function() { 295 const variationData = $(this).find('.tawcvs-available-product-variation').data('product_variations'); 296 return variationData && variationData.length > tawcvs_config.large_product_threshold; 297 }); 298 299 if ($largeProductForms.length > 0) { 300 console.log('TAWCVS: PHP-optimized handling for', $largeProductForms.length, 'large product(s)'); 301 302 $largeProductForms.each(function() { 303 const variationData = $(this).find('.tawcvs-available-product-variation').data('product_variations'); 304 if (variationData) { 305 console.log('TAWCVS: Loaded', variationData.length, 'variations instantly for product'); 306 } 307 }); 308 } 309 } 310 } 311 312 // OPTIMIZED: Efficient initialization 313 function initializeSwatches() { 314 $('.variations_form').tawcvs_variation_swatches_form().trigger('woocommerce_update_variation_values'); 315 $(document.body).trigger('tawcvs_initialized'); 316 toggle_hidden_variation_btn(); 317 logPerformanceInfo(); 318 } 319 320 // OPTIMIZED: Throttled re-initialization for AJAX content 321 let reinitTimeout; 322 function throttledReinit() { 323 clearTimeout(reinitTimeout); 324 reinitTimeout = setTimeout(() => { 325 var $variations_form = $('.variations_form:not(.swatches-support)'); 326 if ($variations_form.length > 0) { 327 $variations_form.each(function () { 328 $(this).wc_variation_form(); 329 }); 330 $variations_form.tawcvs_variation_swatches_form(); 331 } 332 }, 50); // Faster reinit 333 } 334 335 $(function () { 336 // Initialize swatches immediately 337 initializeSwatches(); 338 }); 339 340 $(document).ajaxComplete(function () { 341 throttledReinit(); 342 }); 343 344 // Handle dynamic content updates 345 $(document).on('updated_wc_div wc_fragments_refreshed', function() { 346 throttledReinit(); 347 }); 348 349})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.