| 1 |
/** |
| 2 |
* Woo Product Add To Cart widget behavior. |
| 3 |
* |
| 4 |
* Adds products to cart via WooCommerce AJAX and refreshes fragments. |
| 5 |
*/ |
| 6 |
(function ($) { |
| 7 |
"use strict"; |
| 8 |
|
| 9 |
const INIT_FLAG = "kaWooProductAtcInit"; |
| 10 |
|
| 11 |
const ajaxUrl = (window?.king_addons_grid_data && window.king_addons_grid_data.ajaxUrl) || window.ajaxurl || ''; |
| 12 |
|
| 13 |
const postAddToCart = async (payload) => { |
| 14 |
const formData = new FormData(); |
| 15 |
formData.append('action', 'woocommerce_ajax_add_to_cart'); |
| 16 |
Object.entries(payload).forEach(([k, v]) => formData.append(k, v)); |
| 17 |
|
| 18 |
const response = await fetch(ajaxUrl, { |
| 19 |
method: 'POST', |
| 20 |
credentials: 'same-origin', |
| 21 |
body: formData, |
| 22 |
}); |
| 23 |
|
| 24 |
if (!response.ok) { |
| 25 |
throw new Error('Request failed'); |
| 26 |
} |
| 27 |
return response.json(); |
| 28 |
}; |
| 29 |
|
| 30 |
const updateFragments = (data) => { |
| 31 |
if (!window.jQuery || !data || !data.fragments) return; |
| 32 |
window.jQuery(document.body).trigger('wc_fragment_refresh'); |
| 33 |
window.jQuery.each(data.fragments, (key, value) => { |
| 34 |
window.jQuery(key).replaceWith(value); |
| 35 |
}); |
| 36 |
window.jQuery(document.body).trigger('added_to_cart', [data.fragments, data.cart_hash, data.cart_totals]); |
| 37 |
}; |
| 38 |
|
| 39 |
const showState = (wrapper, type, text) => { |
| 40 |
const stateEl = wrapper.querySelector('.ka-woo-product-atc__state'); |
| 41 |
if (!stateEl) return; |
| 42 |
stateEl.textContent = text || ''; |
| 43 |
stateEl.dataset.state = type || ''; |
| 44 |
stateEl.classList.toggle('is-visible', !!text); |
| 45 |
}; |
| 46 |
|
| 47 |
const collectVariation = () => { |
| 48 |
const variationForm = document.querySelector('form.variations_form'); |
| 49 |
if (!variationForm) return { valid: true, payload: {} }; |
| 50 |
|
| 51 |
const payload = {}; |
| 52 |
let hasEmpty = false; |
| 53 |
|
| 54 |
const variationIdField = variationForm.querySelector('input[name="variation_id"]'); |
| 55 |
const variationId = variationIdField ? parseInt(variationIdField.value || '0', 10) : 0; |
| 56 |
const attrs = variationForm.querySelectorAll('select[name^="attribute_"]'); |
| 57 |
attrs.forEach((sel) => { |
| 58 |
const name = sel.name; |
| 59 |
if (!name) return; |
| 60 |
const val = sel.value; |
| 61 |
if (!val) { |
| 62 |
hasEmpty = true; |
| 63 |
return; |
| 64 |
} |
| 65 |
payload[name] = val; |
| 66 |
}); |
| 67 |
|
| 68 |
if (variationId) { |
| 69 |
payload.variation_id = variationId; |
| 70 |
} |
| 71 |
|
| 72 |
const valid = variationId || !hasEmpty; |
| 73 |
return { valid, payload }; |
| 74 |
}; |
| 75 |
|
| 76 |
const handleATC = (wrapper) => { |
| 77 |
if (!wrapper || !wrapper.dataset) return; |
| 78 |
if (wrapper.dataset[INIT_FLAG] === "1") return; |
| 79 |
wrapper.dataset[INIT_FLAG] = "1"; |
| 80 |
|
| 81 |
const btns = wrapper.querySelectorAll('.ka-woo-product-atc__button'); |
| 82 |
if (!btns.length) return; |
| 83 |
const qtyInput = wrapper.querySelector('.ka-woo-product-atc__qty input[type="number"]'); |
| 84 |
const productId = parseInt(wrapper.dataset.productId || '0', 10); |
| 85 |
const ajax = wrapper.dataset.ajax === 'yes'; |
| 86 |
const defaultRedirect = wrapper.dataset.redirect || 'stay'; |
| 87 |
const successText = wrapper.dataset.success || ''; |
| 88 |
const errorText = wrapper.dataset.error || ''; |
| 89 |
const hasVariations = wrapper.dataset.hasVariations === 'yes'; |
| 90 |
|
| 91 |
const setLoading = (isLoading) => { |
| 92 |
btns.forEach((b) => { |
| 93 |
b.disabled = isLoading; |
| 94 |
b.classList.toggle('is-loading', isLoading); |
| 95 |
}); |
| 96 |
if (qtyInput) { |
| 97 |
qtyInput.disabled = isLoading; |
| 98 |
} |
| 99 |
}; |
| 100 |
|
| 101 |
btns.forEach((btn) => { |
| 102 |
btn.addEventListener('click', async (e) => { |
| 103 |
if (!ajax) return; |
| 104 |
e.preventDefault(); |
| 105 |
const redirect = btn.dataset.redirect || defaultRedirect; |
| 106 |
const qty = qtyInput ? parseInt(qtyInput.value || '1', 10) || 1 : 1; |
| 107 |
|
| 108 |
if (!productId) return; |
| 109 |
|
| 110 |
const variation = collectVariation(); |
| 111 |
if (hasVariations && !variation.valid) { |
| 112 |
showState(wrapper, 'error', errorText || 'Please choose product options.'); |
| 113 |
return; |
| 114 |
} |
| 115 |
|
| 116 |
setLoading(true); |
| 117 |
showState(wrapper, '', ''); |
| 118 |
|
| 119 |
try { |
| 120 |
const payload = { |
| 121 |
product_id: productId, |
| 122 |
quantity: qty, |
| 123 |
...variation.payload, |
| 124 |
}; |
| 125 |
|
| 126 |
const result = await postAddToCart(payload); |
| 127 |
|
| 128 |
if (redirect === 'cart' && window.wc_add_to_cart_params?.cart_url) { |
| 129 |
window.location.href = window.wc_add_to_cart_params.cart_url; |
| 130 |
return; |
| 131 |
} |
| 132 |
if (redirect === 'checkout' && window.wc_add_to_cart_params?.checkout_url) { |
| 133 |
window.location.href = window.wc_add_to_cart_params.checkout_url; |
| 134 |
return; |
| 135 |
} |
| 136 |
|
| 137 |
updateFragments(result); |
| 138 |
showState(wrapper, 'success', successText || 'Added to cart'); |
| 139 |
} catch (err) { |
| 140 |
showState(wrapper, 'error', errorText || 'Could not add to cart.'); |
| 141 |
} finally { |
| 142 |
setLoading(false); |
| 143 |
} |
| 144 |
}); |
| 145 |
}); |
| 146 |
}; |
| 147 |
|
| 148 |
const initAll = (root) => { |
| 149 |
const ctx = root && root.querySelectorAll ? root : document; |
| 150 |
ctx.querySelectorAll('.ka-woo-product-atc[data-ajax="yes"]').forEach(handleATC); |
| 151 |
}; |
| 152 |
|
| 153 |
document.addEventListener("DOMContentLoaded", () => initAll(document)); |
| 154 |
|
| 155 |
$(window).on("elementor/frontend/init", function () { |
| 156 |
elementorFrontend.hooks.addAction( |
| 157 |
"frontend/element_ready/woo_product_add_to_cart.default", |
| 158 |
function ($scope) { |
| 159 |
initAll($scope && $scope[0] ? $scope[0] : document); |
| 160 |
} |
| 161 |
); |
| 162 |
}); |
| 163 |
})(jQuery); |
| 164 |
|
| 165 |
|
| 166 |
|
| 167 |
|
| 168 |
|