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
king-addons / includes / extensions / Woo_Builder / script.js

script.js in King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder 51.1.87, at includes/extensions/Woo_Builder/script.js

464 lines 18.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function (factory) {
2 if (typeof window.jQuery === 'undefined') {
3 return;
4 }
5 factory(window.jQuery);
6 })(function ($) {
7 'use strict';
8
9 const $rulesContainer = $('#ka-woo-rules');
10 if (!$rulesContainer.length || typeof $.fn.select2 === 'undefined') {
11 return;
12 }
13
14 const restRoot = (window.wpApiSettings && window.wpApiSettings.root) ? window.wpApiSettings.root : '';
15 const restNonce = (window.wpApiSettings && window.wpApiSettings.nonce) ? window.wpApiSettings.nonce : '';
16 const noValueTypes = ['always', 'all_products', 'cart', 'checkout', 'my_account', 'is_shop'];
17
18 const productTypeOptions = [
19 {id: 'simple', text: 'Simple'},
20 {id: 'variable', text: 'Variable'},
21 {id: 'grouped', text: 'Grouped'},
22 {id: 'external', text: 'External/Affiliate'}
23 ];
24
25 const endpoints = {
26 product_in: {url: restRoot + 'kingaddons/v1/ajaxselect2/getPostsByPostType/', params: {query_slug: 'product'}},
27 product_cat_in: {url: restRoot + 'kingaddons/v1/ajaxselect2/getTaxonomies/', params: {query_slug: 'product_cat'}},
28 product_cat_archive_in: {url: restRoot + 'kingaddons/v1/ajaxselect2/getTaxonomies/', params: {query_slug: 'product_cat'}},
29 product_tag_archive_in: {url: restRoot + 'kingaddons/v1/ajaxselect2/getTaxonomies/', params: {query_slug: 'product_tag'}},
30 product_tag_in: {url: restRoot + 'kingaddons/v1/ajaxselect2/getTaxonomies/', params: {query_slug: 'product_tag'}},
31 products: {url: restRoot + 'kingaddons/v1/ajaxselect2/getPostsByPostType/', params: {query_slug: 'product'}},
32 product_categories: {url: restRoot + 'kingaddons/v1/ajaxselect2/getTaxonomies/', params: {query_slug: 'product_cat'}},
33 product_cat_archives: {url: restRoot + 'kingaddons/v1/ajaxselect2/getTaxonomies/', params: {query_slug: 'product_cat'}},
34 product_tags: {url: restRoot + 'kingaddons/v1/ajaxselect2/getTaxonomies/', params: {query_slug: 'product_tag'}},
35 };
36
37 const getPlaceholder = () => {
38 if (window.KingAddonsWooBuilder && window.KingAddonsWooBuilder.labels && window.KingAddonsWooBuilder.labels.valuePlaceholder) {
39 return window.KingAddonsWooBuilder.labels.valuePlaceholder;
40 }
41 return '';
42 };
43
44 const initRule = ($rule, idx) => {
45 const $type = $rule.find('.ka-woo-rule-type');
46 const $values = $rule.find('.ka-woo-rule-values');
47
48 $type.attr('name', 'ka_woo_rule_type[' + idx + ']');
49 $values.attr('name', 'ka_woo_rule_values[' + idx + '][]');
50
51 const typeVal = $type.val() || '';
52
53 if (noValueTypes.includes(typeVal)) {
54 if ($values.hasClass('select2-hidden-accessible')) {
55 $values.select2('destroy');
56 }
57 $values.prop('disabled', true).val(null);
58 return;
59 }
60
61 $values.prop('disabled', false);
62
63 if ($values.hasClass('select2-hidden-accessible')) {
64 $values.select2('destroy');
65 }
66
67 const ajaxConfig = endpoints[typeVal] || null;
68 const select2Options = {
69 width: '100%',
70 allowClear: true,
71 placeholder: getPlaceholder(),
72 };
73
74 if (ajaxConfig && restRoot) {
75 select2Options.ajax = {
76 url: ajaxConfig.url,
77 dataType: 'json',
78 delay: 120,
79 headers: restNonce ? {'X-WP-Nonce': restNonce} : {},
80 data: function (params) {
81 const query = Object.assign({}, ajaxConfig.params || {});
82 if (params && params.term) {
83 query.s = params.term;
84 }
85 if ($values.data('selected-ids')) {
86 query.ids = $values.data('selected-ids');
87 }
88 return query;
89 },
90 processResults: function (data) {
91 return data;
92 }
93 };
94 } else if (typeVal === 'product_type_in' || typeVal === 'product_types') {
95 select2Options.data = productTypeOptions;
96 }
97
98 $values.select2(select2Options);
99
100 // Clear pre-set data-selected-ids after init to avoid duplicate queries.
101 $values.removeData('selected-ids');
102 };
103
104 const rebuildIndexes = () => {
105 $rulesContainer.find('.ka-woo-rule').each((idx, el) => initRule($(el), idx));
106 };
107
108 $rulesContainer.on('click', '.ka-woo-remove-rule', function () {
109 if ($rulesContainer.find('.ka-woo-rule').length <= 1) {
110 return;
111 }
112 $(this).closest('.ka-woo-rule').remove();
113 rebuildIndexes();
114 });
115
116 $('#ka-woo-add-rule').on('click', function () {
117 const $first = $rulesContainer.find('.ka-woo-rule').first();
118 if (!$first.length) {
119 return;
120 }
121 const $clone = $first.clone();
122 $clone.find('option').prop('selected', false);
123 $clone.find('.ka-woo-rule-values').empty();
124 $clone.appendTo($rulesContainer);
125 rebuildIndexes();
126 });
127
128 $rulesContainer.on('change', '.ka-woo-rule-type', function () {
129 const $rule = $(this).closest('.ka-woo-rule');
130 initRule($rule, $rulesContainer.find('.ka-woo-rule').index($rule));
131 });
132
133 $rulesContainer.find('.ka-woo-rule-values').each(function () {
134 const existingValues = $(this).val();
135 if (existingValues && existingValues.length) {
136 $(this).data('selected-ids', Array.isArray(existingValues) ? existingValues.join(',') : existingValues);
137 }
138 });
139
140 rebuildIndexes();
141 });
142
143 (function () {
144 'use strict';
145
146 const defaultPanels = [
147 '.woocommerce-billing-fields',
148 '.woocommerce-shipping-fields',
149 '#payment',
150 '.woocommerce-checkout-review-order-table',
151 ];
152
153 const resolvePanels = (container) => {
154 const steps = Array.from(container.querySelectorAll('.ka-woo-checkout-step'));
155 return steps.map((step, idx) => ({
156 selector: (step.dataset.target || '').trim() || defaultPanels[idx] || '',
157 idx,
158 }));
159 };
160
161 const revealWithAncestors = (el) => {
162 if (!el) {
163 return;
164 }
165 el.classList.remove('ka-woo-hidden');
166 let parent = el.parentElement;
167 while (parent && parent !== document.body) {
168 parent.classList.remove('ka-woo-hidden');
169 parent = parent.parentElement;
170 }
171 };
172
173 const togglePanels = (stepsMap, activeIndex, scroll) => {
174 const activeSelector = stepsMap[activeIndex] ? stepsMap[activeIndex].selector : '';
175 const activeEls = activeSelector ? Array.from(document.querySelectorAll(activeSelector)) : [];
176
177 stepsMap.forEach((item, idx) => {
178 if (!item.selector) {
179 return;
180 }
181 document.querySelectorAll(item.selector).forEach((el) => {
182 const isActive = idx === activeIndex;
183 const isAncestorOfActive = activeEls.some((active) => el.contains(active));
184 if (isActive || isAncestorOfActive) {
185 el.classList.remove('ka-woo-hidden');
186 } else {
187 el.classList.add('ka-woo-hidden');
188 }
189 });
190 });
191
192 activeEls.forEach(revealWithAncestors);
193
194 if (scroll && scroll === 'yes') {
195 const target = activeEls[0] || null;
196 if (target) {
197 target.scrollIntoView({ behavior: 'smooth', block: 'start' });
198 }
199 }
200 };
201
202 const initSteps = () => {
203 document.querySelectorAll('.ka-woo-checkout-steps--pro').forEach((container) => {
204 if (container.dataset.kaCheckoutStepsInit === '1') {
205 return;
206 }
207 container.dataset.kaCheckoutStepsInit = '1';
208 const steps = Array.from(container.querySelectorAll('.ka-woo-checkout-step'));
209 if (!steps.length) {
210 return;
211 }
212 let current = 0;
213 const scrollTop = container.dataset.scrollTop || 'no';
214 const stepsMap = resolvePanels(container);
215 const wrapper = container.parentElement;
216 const enableNav = wrapper?.dataset.enableNav === 'true';
217 const navPrev = wrapper?.querySelector('.ka-woo-checkout-steps__prev');
218 const navNext = wrapper?.querySelector('.ka-woo-checkout-steps__next');
219 const form = document.querySelector('form.checkout');
220 const submitButton = form ? form.querySelector('button[type="submit"]') : null;
221 const errorClass = 'ka-woo-checkout-step--error';
222 const serverErrors = document.querySelector('.woocommerce-NoticeGroup-checkout, .woocommerce-error');
223
224 const validateCurrent = () => {
225 const panelSelector = stepsMap[current] ? stepsMap[current].selector : null;
226 if (!panelSelector || !form) {
227 return true;
228 }
229 const panel = document.querySelector(panelSelector);
230 if (!panel) {
231 return true;
232 }
233 const fields = panel.querySelectorAll('input, select, textarea');
234 let valid = true;
235 fields.forEach((field) => {
236 if (typeof field.reportValidity === 'function') {
237 if (!field.reportValidity()) {
238 valid = false;
239 }
240 }
241 });
242 return valid;
243 };
244
245 const applyCurrent = (scroll) => {
246 const map = resolvePanels(container);
247 steps.forEach((step, i) => {
248 step.classList.toggle('is-active', i === current);
249 step.classList.toggle('is-complete', i < current);
250 });
251 togglePanels(map, current, scroll);
252 if (enableNav && navPrev && navNext) {
253 navPrev.disabled = current === 0;
254 const isLast = current >= steps.length - 1;
255 navNext.dataset.isLast = isLast ? 'true' : 'false';
256 const defaultNext = container.dataset.nextText || navNext.textContent;
257 const submitText = container.dataset.submitText || navNext.textContent;
258 navNext.textContent = isLast ? submitText : defaultNext;
259 navNext.classList.toggle('is-submit', isLast);
260 if (submitButton) {
261 submitButton.disabled = !isLast;
262 }
263 }
264 steps.forEach((step) => step.classList.remove(errorClass));
265 };
266
267 const setActive = (idx) => {
268 current = idx;
269 applyCurrent(scrollTop);
270 };
271
272 container.kaApplyCheckoutStep = () => applyCurrent('no');
273
274 steps.forEach((step, idx) => {
275 step.addEventListener('click', () => setActive(idx));
276 });
277
278 if (enableNav && navPrev && navNext) {
279 navPrev.addEventListener('click', () => {
280 setActive(Math.max(0, current - 1));
281 });
282 navNext.addEventListener('click', (e) => {
283 if (!validateCurrent()) {
284 e.preventDefault();
285 return;
286 }
287 if (navNext.dataset.isLast === 'true') {
288 if (submitButton) {
289 submitButton.click();
290 } else if (form) {
291 form.dispatchEvent(new Event('submit', { cancelable: true, bubbles: true }));
292 }
293 return;
294 }
295 setActive(Math.min(steps.length - 1, current + 1));
296 });
297 }
298
299 setActive(0);
300
301 // React to Woo checkout errors: highlight step containing error.
302 document.body.addEventListener('checkout_error', () => {
303 const errorField = document.querySelector('.woocommerce-error, .woocommerce-invalid');
304 if (!errorField) {
305 return;
306 }
307 const errorStepIdx = stepsMap.findIndex((map) => map.selector && errorField.closest(map.selector));
308 if (errorStepIdx >= 0) {
309 steps[errorStepIdx]?.classList.add(errorClass);
310 setActive(errorStepIdx);
311 errorField.scrollIntoView({ behavior: 'smooth', block: 'center' });
312 }
313 });
314
315 // React to Woo AJAX errors (server-side validation)
316 if (serverErrors) {
317 const observer = new MutationObserver(() => {
318 const err = document.querySelector('.woocommerce-error, .woocommerce-invalid');
319 if (!err) {
320 return;
321 }
322 const idx = stepsMap.findIndex((map) => map.selector && err.closest(map.selector));
323 if (idx >= 0) {
324 steps[idx]?.classList.add(errorClass);
325 setActive(idx);
326 err.scrollIntoView({ behavior: 'smooth', block: 'center' });
327 }
328 });
329 observer.observe(serverErrors.parentElement || document.body, { childList: true, subtree: true });
330 }
331 });
332 };
333
334 const initSticky = () => {
335 const items = document.querySelectorAll('[data-ka-sticky="true"]');
336 items.forEach((el) => {
337 const offset = parseInt(el.dataset.kaStickyOffset || '20', 10);
338 const breakpoint = parseInt(el.dataset.kaStickyBreakpoint || '768', 10);
339 const footer = document.querySelector('.site-footer, footer');
340
341 const applySticky = () => {
342 if (window.innerWidth < breakpoint) {
343 el.classList.remove('ka-woo-sticky-ready');
344 el.style.top = '';
345 el.style.maxHeight = '';
346 el.style.overflow = '';
347 return;
348 }
349 let available = window.innerHeight - offset - 20;
350 if (footer) {
351 const rect = footer.getBoundingClientRect();
352 if (rect.top > 0) {
353 available = Math.min(available, rect.top - offset - 10);
354 }
355 }
356 el.classList.add('ka-woo-sticky-ready');
357 el.style.top = offset + 'px';
358 if (available > 100) {
359 el.style.maxHeight = available + 'px';
360 el.style.overflow = 'auto';
361 }
362 };
363
364 const ro = new ResizeObserver(() => applySticky());
365 ro.observe(document.body);
366 if (footer) {
367 const fo = new ResizeObserver(() => applySticky());
368 fo.observe(footer);
369 }
370 // Adjust on content changes inside sticky container
371 const contentObserver = new MutationObserver(() => applySticky());
372 contentObserver.observe(el, { childList: true, subtree: true });
373 applySticky();
374 window.addEventListener('resize', applySticky, { passive: true });
375 });
376 };
377
378 const initPayments = () => {
379 document.querySelectorAll('.ka-woo-checkout-payment').forEach((wrapper) => {
380 const icons = wrapper.dataset.kaIcons ? JSON.parse(wrapper.dataset.kaIcons) : {};
381 const placeorder = wrapper.dataset.kaPlaceorder ? JSON.parse(wrapper.dataset.kaPlaceorder) : {};
382 const accordion = wrapper.dataset.kaAccordion === 'true';
383 const methods = wrapper.querySelectorAll('.wc_payment_methods .wc_payment_method');
384 const orderButton = wrapper.querySelector('.place-order button[type="submit"]') || document.querySelector('.place-order button[type="submit"]');
385
386 const applyIcons = () => {
387 methods.forEach((method) => {
388 const input = method.querySelector('input[name="payment_method"]');
389 const label = input ? wrapper.querySelector('label[for="' + input.id + '"]') : null;
390 if (!input || !label) {
391 return;
392 }
393 const gid = input.value;
394 if (icons && icons[gid] && !label.querySelector('.ka-woo-payment-icon')) {
395 const img = document.createElement('img');
396 img.src = icons[gid];
397 img.alt = gid;
398 img.className = 'ka-woo-payment-icon';
399 label.appendChild(img);
400 }
401 });
402 };
403
404 const updatePlaceOrder = () => {
405 if (!orderButton) return;
406 const selected = wrapper.querySelector('input[name="payment_method"]:checked');
407 if (!selected) return;
408 const gid = selected.value;
409 if (placeorder && placeorder[gid]) {
410 orderButton.textContent = placeorder[gid];
411 }
412 };
413
414 const toggleAccordion = () => {
415 if (!accordion) return;
416 methods.forEach((method) => {
417 const input = method.querySelector('input[name="payment_method"]');
418 const box = method.querySelector('.payment_box');
419 const active = input && input.checked;
420 method.classList.toggle('is-active', !!active);
421 if (box) {
422 box.style.display = active ? '' : 'none';
423 }
424 });
425 };
426
427 methods.forEach((method) => {
428 const input = method.querySelector('input[name="payment_method"]');
429 if (input) {
430 input.addEventListener('change', () => {
431 updatePlaceOrder();
432 toggleAccordion();
433 });
434 }
435 });
436
437 applyIcons();
438 updatePlaceOrder();
439 toggleAccordion();
440 });
441 };
442
443 document.addEventListener('DOMContentLoaded', () => {
444 initSteps();
445 initSticky();
446 initPayments();
447 });
448
449 if (window.jQuery) {
450 window.jQuery(document.body).on('updated_checkout', function () {
451 document.querySelectorAll('.ka-woo-checkout-steps--pro').forEach((container) => {
452 if (typeof container.kaApplyCheckoutStep === 'function') {
453 container.kaApplyCheckoutStep();
454 }
455 });
456 });
457 }
458 })();
459
460
461
462
463
464