PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.87
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.87
51.1.87 51.1.86 51.1.84 51.1.85 51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 All 41 releases
← All changes | includes/widgets/Woo_Product_Variations/script.js +184 -23 51.1.76 → 51.1.87 View file →
@@ -1,8 +1,9 @@
1 1 /**
2 2 * Woo Product Variations widget behavior.
3 3 *
4 - * Enhances variation selects with swatches.
4 + * Enhances variation selects with swatches and keeps WooCommerce's
5 + * variation resolver in sync when the form lives in a split builder widget.
5 6 */
6 7 (function ($) {
7 8 "use strict";
8 9
@@ -10,15 +11,13 @@
10 11
11 12 const initVariations = (wrap) => {
12 13 if (!wrap || !wrap.dataset) return;
13 14 if (wrap.dataset[INIT_FLAG] === "1") return;
14 - wrap.dataset[INIT_FLAG] = "1";
15 15
16 16 const form = wrap.querySelector('form.variations_form');
17 17 if (!form) return;
18 + wrap.dataset[INIT_FLAG] = "1";
18 19
19 - if (wrap.dataset.swatches !== 'yes') return;
20 -
21 20 let map = {};
22 21 try {
23 22 map = wrap.dataset.swatchesMap ? JSON.parse(wrap.dataset.swatchesMap) : {};
24 23 } catch (e) {
@@ -34,8 +33,173 @@
34 33 const selects = form.querySelectorAll('select');
35 34 const updateCallbacks = [];
36 35 const runAll = () => updateCallbacks.forEach((cb) => cb());
37 36
37 + // Woo leaves out-of-stock options enabled in the native select when
38 + // "hide out of stock" is off. Hide/disable would then be a no-op,
39 + // so also read the variation JSON Woo already prints on the form.
40 + let productVariations = [];
41 + try {
42 + const raw = form.getAttribute('data-product_variations');
43 + productVariations = raw && raw !== 'false' ? JSON.parse(raw) : [];
44 + } catch (e) {
45 + productVariations = [];
46 + }
47 +
48 + const priceRoots = () => document.querySelectorAll('.ka-woo-product-price');
49 + priceRoots().forEach((el) => {
50 + if (!el.dataset.kaOriginalPrice) {
51 + el.dataset.kaOriginalPrice = el.innerHTML;
52 + }
53 + });
54 +
55 + const setAtcNotice = (hide) => {
56 + document.querySelectorAll('.ka-woo-product-atc__notice').forEach((el) => {
57 + el.hidden = !!hide;
58 + });
59 + };
60 +
61 + const applyVariationPrice = (variation) => {
62 + priceRoots().forEach((el) => {
63 + if (!variation || !variation.price_html) {
64 + if (el.dataset.kaOriginalPrice) {
65 + el.innerHTML = el.dataset.kaOriginalPrice;
66 + }
67 + return;
68 + }
69 + const discount = el.querySelector('.ka-woo-product-price__discount');
70 + const discountHtml = discount ? discount.outerHTML : '';
71 + el.innerHTML = variation.price_html + discountHtml;
72 + });
73 + };
74 +
75 + const ensureWcVariationForm = () => {
76 + if (!window.jQuery || typeof window.jQuery.fn.wc_variation_form !== 'function') {
77 + return false;
78 + }
79 + const $form = window.jQuery(form);
80 + if (!$form.data('wc_variation_form')) {
81 + $form.wc_variation_form();
82 + }
83 + return true;
84 + };
85 +
86 + const findMatchedVariation = () => {
87 + if (!Array.isArray(productVariations) || !productVariations.length) {
88 + return null;
89 + }
90 + const selected = {};
91 + let hasEmpty = false;
92 + form.querySelectorAll('select[name^="attribute_"]').forEach((sel) => {
93 + selected[sel.name] = sel.value;
94 + if (!sel.value) {
95 + hasEmpty = true;
96 + }
97 + });
98 + if (hasEmpty) {
99 + return null;
100 + }
101 + return productVariations.find((variation) => {
102 + const attrs = variation.attributes || {};
103 + return Object.keys(attrs).every((key) => {
104 + const want = attrs[key];
105 + return !want || want === selected[key];
106 + });
107 + }) || null;
108 + };
109 +
110 + const applyFallbackVariation = () => {
111 + if (window.jQuery && window.jQuery(form).data('wc_variation_form')) {
112 + return;
113 + }
114 + const match = findMatchedVariation();
115 + const varInput = form.querySelector('input.variation_id, input[name="variation_id"]');
116 + const single = form.querySelector('.single_variation');
117 + if (match) {
118 + if (varInput) {
119 + varInput.value = String(match.variation_id || '');
120 + }
121 + if (single) {
122 + single.innerHTML = (match.price_html ? '<div class="woocommerce-variation-price">' + match.price_html + '</div>' : '')
123 + + (match.availability_html ? '<div class="woocommerce-variation-availability">' + match.availability_html + '</div>' : '');
124 + }
125 + applyVariationPrice(match);
126 + setAtcNotice(true);
127 + } else {
128 + if (varInput) {
129 + varInput.value = '0';
130 + }
131 + if (single) {
132 + single.innerHTML = '';
133 + }
134 + applyVariationPrice(null);
135 + setAtcNotice(false);
136 + }
137 + };
138 +
139 + const wcReady = ensureWcVariationForm();
140 + if (window.jQuery) {
141 + const $form = window.jQuery(form);
142 + $form.on('found_variation', function (event, variation) {
143 + applyVariationPrice(variation);
144 + setAtcNotice(!!(variation && variation.variation_id));
145 + });
146 + $form.on('reset_data hide_variation', function () {
147 + applyVariationPrice(null);
148 + setAtcNotice(false);
149 + });
150 + $form.on('woocommerce_update_variation_values woocommerce_variation_has_changed reset_data found_variation hide_variation', runAll);
151 + }
152 + if (!wcReady) {
153 + form.addEventListener('change', applyFallbackVariation);
154 + applyFallbackVariation();
155 + if (window.jQuery) {
156 + window.jQuery(function () {
157 + if (ensureWcVariationForm()) {
158 + const selected = form.querySelector('select[name^="attribute_"]');
159 + if (selected && selected.value) {
160 + window.jQuery(selected).trigger('change');
161 + }
162 + } else {
163 + applyFallbackVariation();
164 + }
165 + });
166 + }
167 + }
168 +
169 + if (wrap.dataset.swatches !== 'yes') {
170 + return;
171 + }
172 +
173 + const attributeMatches = (variationAttr, selected) => {
174 + if (!variationAttr) {
175 + return true;
176 + }
177 + return variationAttr === selected;
178 + };
179 +
180 + const valueIsAvailable = (attrName, val) => {
181 + if (!Array.isArray(productVariations) || !productVariations.length) {
182 + const opt = form.querySelector(`select[name="${attrName}"] option[value="${val}"]`);
183 + return !(opt && opt.disabled);
184 + }
185 + return productVariations.some((variation) => {
186 + if (!variation || variation.is_in_stock === false) {
187 + return false;
188 + }
189 + const attrs = variation.attributes || {};
190 + if (!attributeMatches(attrs[attrName], val)) {
191 + return false;
192 + }
193 + return Array.from(selects).every((other) => {
194 + if (other.name === attrName || !other.value) {
195 + return true;
196 + }
197 + return attributeMatches(attrs[other.name], other.value);
198 + });
199 + });
200 + };
201 +
38 202 selects.forEach((select) => {
39 203 const attrName = select.name;
40 204 const swatchesInfo = attrMap[attrName];
41 205 if (!swatchesInfo) {
@@ -83,13 +247,18 @@
83 247 btn.appendChild(span);
84 248 }
85 249
86 250 btn.addEventListener('click', () => {
251 + if (!valueIsAvailable(attrName, opt.value)) {
252 + return;
253 + }
87 254 select.value = opt.value;
88 255 select.dispatchEvent(new Event('change', { bubbles: true }));
89 256 if (window.jQuery) {
257 + window.jQuery(select).trigger('check_variations');
90 258 window.jQuery(select).trigger('change');
91 259 }
260 + applyFallbackVariation();
92 261 runAll();
93 262 });
94 263
95 264 container.appendChild(btn);
@@ -105,12 +274,12 @@
105 274 const current = select.value;
106 275 Array.from(container.children).forEach((btn) => {
107 276 const val = btn.dataset.value;
108 277 const opt = select.querySelector(`option[value="${val}"]`);
109 - const disabled = opt && opt.disabled;
278 + const unavailable = !valueIsAvailable(attrName, val) || !!(opt && opt.disabled);
110 279 btn.classList.toggle('is-active', current === val);
111 - btn.classList.toggle('is-disabled', !!disabled);
112 - if (disabled && unavailableBehavior === 'hide') {
280 + btn.classList.toggle('is-disabled', unavailable);
281 + if (unavailable && unavailableBehavior === 'hide') {
113 282 btn.style.display = 'none';
114 283 } else {
115 284 btn.style.display = '';
116 285 }
@@ -127,18 +296,16 @@
127 296 });
128 297
129 298 runAll();
130 299
131 - // Sync with WooCommerce variation script events.
132 - if (window.jQuery) {
133 - const $form = window.jQuery(form);
134 - $form.on('woocommerce_update_variation_values woocommerce_variation_has_changed reset_data found_variation hide_variation', runAll);
135 - const resetBtn = form.querySelector('.reset_variations');
136 - if (resetBtn) {
137 - resetBtn.addEventListener('click', () => {
138 - setTimeout(runAll, 50);
139 - });
140 - }
300 + const resetBtn = form.querySelector('.reset_variations');
301 + if (resetBtn) {
302 + resetBtn.addEventListener('click', () => {
303 + setTimeout(() => {
304 + runAll();
305 + applyFallbackVariation();
306 + }, 50);
307 + });
141 308 }
142 309 };
143 310
144 311 const initAll = (root) => {
@@ -156,10 +323,4 @@
156 323 }
157 324 );
158 325 });
159 326 })(jQuery);
160 -
161 -
162 -
163 -
164 -
165 -