|
|
@@ -0,0 +1,682 @@ |
|
|
import 'core-js/es6/array'; |
|
|
import 'core-js/es6/object'; |
|
|
import 'core-js/es6/string'; |
|
|
// Global options |
|
|
import defaults from './_config'; |
|
|
// general helper functions |
|
|
import Helper from './_helpers'; |
|
|
// general pricing and currency functions |
|
|
import Pricing from './_pricing'; |
|
|
// throttle and debounce manager |
|
|
import Throttler from './throttler'; |
|
|
|
|
|
class rcWidget { |
|
|
constructor() { |
|
|
this.tooltip = null; |
|
|
this.options = {}; |
|
|
this.products = []; |
|
|
this.throttler = new Throttler(); |
|
|
|
|
|
if (arguments[0] && typeof(arguments[0]) === 'object') { |
|
|
Object.assign(this.options, defaults); |
|
|
Object.assign(this.options, arguments[0]); |
|
|
//this.options = { ...defaults, ...arguments[0]}; |
|
|
} else { |
|
|
Object.assign(this.options, defaults); |
|
|
//this.options = { ...defaults}; |
|
|
} |
|
|
} |
|
|
|
|
|
addProduct(product) { // if uninstalled, but active: Runs |
|
|
let self = this; |
|
|
|
|
|
self.installationCheck(); |
|
|
|
|
|
// Don't include duplicate products, search by product.id |
|
|
if (self.products.some(x => x.id === product.id)) { |
|
|
return; |
|
|
} |
|
|
|
|
|
// Create new copy of rcWidget options then override it with product specific options |
|
|
product.options = Object.assign( |
|
|
Object.assign({}, self.options), |
|
|
product |
|
|
); |
|
|
|
|
|
self.products.push(product); |
|
|
} |
|
|
|
|
|
installationCheck() { |
|
|
// If ReCharge isn't installed, trigger fail safe function |
|
|
if (typeof(ReCharge.is_installed) === 'undefined') { |
|
|
ReCharge.is_installed = null; |
|
|
} else { |
|
|
return; |
|
|
} |
|
|
|
|
|
let installationStatus = document.documentElement.innerHTML.indexOf('recharge.js') > -1; |
|
|
|
|
|
ReCharge.is_installed = installationStatus; |
|
|
|
|
|
if (!installationStatus) { |
|
|
rcWidget._failSafe(); |
|
|
} |
|
|
} |
|
|
|
|
|
run() { |
|
|
let self = this; |
|
|
|
|
|
if (!(self.options.active || self.checkTestMode())) { |
|
|
rcWidget._failSafe(); |
|
|
return; |
|
|
} |
|
|
|
|
|
if (self.checkTestMode()) { |
|
|
alert('ReCharge preview mode.'); |
|
|
} |
|
|
|
|
|
if (self.options.debug || self.checkTestMode()) { |
|
|
self.debugMode(); |
|
|
} |
|
|
|
|
|
self.buildWidget(); |
|
|
|
|
|
self.helpers(); |
|
|
|
|
|
self.showAddToCartButton(); |
|
|
|
|
|
self.showWidget(); |
|
|
} |
|
|
|
|
|
checkTestMode() { |
|
|
return !!Helper.getUrlParameter('recharge'); |
|
|
} |
|
|
|
|
|
debugMode() { |
|
|
console.log(this); |
|
|
} |
|
|
|
|
|
buildWidget() { |
|
|
let self = this; |
|
|
|
|
|
self.products.forEach(product => { |
|
|
if (product.status === 'complete') { return; } else { product.status = 'processing'; } |
|
|
product.forms = rcWidget._checkAndGetProductForms(product); |
|
|
product.elements = []; |
|
|
|
|
|
// Get form elements |
|
|
product.forms.forEach(form => { |
|
|
product.elements.push(rcWidget._getFormElements(product, form)); |
|
|
rcWidget._renameElements(form); |
|
|
}); |
|
|
|
|
|
// Get page elements |
|
|
product.elements.forEach(elements => { |
|
|
elements.productPrice = Pricing.bottomUpPriceSearch(product, elements); |
|
|
elements.variantInputs = rcWidget._getVariantInputs(product, elements); |
|
|
}); |
|
|
|
|
|
// Dump element object if productVariantSelect is missing. Do not run additional code on these items. |
|
|
product.elements = product.elements.filter(elements => elements.productVariantSelect); |
|
|
|
|
|
// Product initialization |
|
|
if (product.elements.length) { |
|
|
self.addListeners(product); |
|
|
|
|
|
// Set duplicate select value |
|
|
if (rcWidget._disabledForDuplicates(product)) { |
|
|
// Not needed if disabling duplicates and is also subscription only |
|
|
rcWidget._updateDuplicateSelect(product); |
|
|
} |
|
|
|
|
|
// Set form attributes |
|
|
rcWidget._updateActiveAttributes(product); |
|
|
|
|
|
// Set default interface |
|
|
if (!product.options.subscription_only) { |
|
|
// Not needed if subscription-only |
|
|
rcWidget._updatePricing(product); |
|
|
rcWidget._updateActiveRadio(product); |
|
|
rcWidget._updateWidgetPricing(product); |
|
|
rcWidget._highlightActivePurchaseType(product); |
|
|
} |
|
|
|
|
|
product.status = 'complete'; |
|
|
} else { |
|
|
product.status = 'failed'; |
|
|
} |
|
|
}) |
|
|
} |
|
|
|
|
|
addListeners(product) { |
|
|
product.elements.forEach((elements, i) => { |
|
|
rcWidget._addPurchaseTypeListeners(product, elements); |
|
|
rcWidget._addIntervalOptionListeners(product, elements); |
|
|
rcWidget._addVariantInputListeners(this.throttler, product, elements); |
|
|
rcWidget._addPricingListeners(product); |
|
|
}); |
|
|
} |
|
|
|
|
|
helpers() { |
|
|
rcWidget._identifyTheme(); |
|
|
rcWidget._disableAjaxCart(this.products); |
|
|
|
|
|
if (!window.ReCharge.products.length) { |
|
|
console.warn('No products found', window.ReCharge); |
|
|
} |
|
|
|
|
|
window.ReCharge.products.forEach(product => { |
|
|
if (!product.forms.length) { |
|
|
console.warn('Product form not found', product); |
|
|
} |
|
|
|
|
|
|
|
|
if (!product.elements.some(elements => elements.productPrice)) { |
|
|
console.warn('Product price not found. If missing, pass the price_selector parameter', product.price_selector); |
|
|
} |
|
|
}) |
|
|
|
|
|
window.addEventListener('pageshow', event => { |
|
|
if (event.persisted || window.performance && window.performance.navigation.type === 2) { |
|
|
window.location.reload(); |
|
|
} |
|
|
}, false); |
|
|
} |
|
|
|
|
|
showAddToCartButton() { |
|
|
let buttons = document.querySelectorAll('form[action^="/cart/add"] [type="submit"]'); |
|
|
|
|
|
Array.from(buttons) |
|
|
.forEach(elem => { |
|
|
elem.style.visibility = 'visible'; |
|
|
}); |
|
|
} |
|
|
|
|
|
showWidget() { |
|
|
/* |
|
|
Show the widget for products |
|
|
- Filter the products array |
|
|
- Check for product elements |
|
|
- Check for rcContainer element |
|
|
- On each rcContainer, embed style="display: block;" |
|
|
*/ |
|
|
let self = this; |
|
|
self.products.filter(product => { |
|
|
product.elements.length && product.elements.filter(elements => { |
|
|
elements.rcContainer.style.display = 'block'; |
|
|
}); |
|
|
}); |
|
|
} |
|
|
|
|
|
static _failSafe() { |
|
|
// Trigger functions necessariy to prevent customer disruption |
|
|
ReCharge.showAddToCartButton(); |
|
|
} |
|
|
|
|
|
static _identifyTheme() { |
|
|
if (window.Shopify) { |
|
|
let shopifyTheme = window.Shopify.theme.name, |
|
|
themeList = [ |
|
|
// Original |
|
|
'Alchemy', 'Atlantic', 'Blockshop', 'Brooklyn', |
|
|
'California', 'Classic', 'Clean', 'Envy', 'Fluid', 'Focal', |
|
|
'Kickstand', 'Launchpad', 'Limitless', 'Minimal', 'Mobilia', 'New Standard', |
|
|
'Pacific', 'Palo Alto', 'Parallax', 'Pop', 'Radiance', 'React', 'Responsive', 'Retina', |
|
|
'Solo', 'Startup', 'Supply', 'Vantage', 'Vintage', |
|
|
// Widget v3 |
|
|
'Boundless', 'Debut', 'District', 'Fashionopolism', 'Grid', 'Icon', 'Jumpstart', |
|
|
'Lookbook', 'Pipeline', 'Providence', 'Simple', 'Symmetry', 'Testament', 'Venture', 'Wonderskin', |
|
|
// Widget v3+ |
|
|
'Cookbook', 'Expression', 'Flex', 'Jitensha', 'Masonry', 'Mr Parker', 'Showtime', |
|
|
'Vanity', 'Palo Alto', 'Vintage', 'Weekend', 'Ms Parker', 'Trademark', 'Kingdom', 'Showcase', |
|
|
'Handy', 'Kagami', 'Avenues', 'Turbo', 'Slate', |
|
|
// Problematic themes |
|
|
'Betty', 'Prestige', 'Loft', |
|
|
]; |
|
|
// Search through themeList for (similair) matching theme name |
|
|
let knownTheme = themeList.find(theme => shopifyTheme.toLowerCase().indexOf(theme.toLowerCase()) > -1); |
|
|
// Set identified theme as found theme name or return shopifyTheme name. |
|
|
shopifyTheme = (knownTheme || shopifyTheme).toLowerCase(); |
|
|
if (shopifyTheme === 'turbo') { |
|
|
console.info('Turbo theme detected. If ReCharge widget fails to load, set Page Transitions to "Sport"'); |
|
|
} |
|
|
if (shopifyTheme === 'weekend') { |
|
|
console.info('Weekened theme detected. "properties[shipping_interval_frequency]", "properties[subscription_id]", "properties[shipping_interval_unit_type]" might be missing from data varaible used in addItem()'); |
|
|
} |
|
|
if (shopifyTheme === 'betty') { |
|
|
console.info('Betty theme detected. Ref Issue #12'); |
|
|
} |
|
|
if (shopifyTheme === 'prestige') { |
|
|
console.info('Prestige theme detected. Ref Issue #18'); |
|
|
} |
|
|
if (shopifyTheme === 'loft') { |
|
|
console.info('Loft theme detected. Ref Issue #19'); |
|
|
} |
|
|
document.body.className += ` rc_theme--${shopifyTheme.replace(/[\W_]/g, '-')}`; |
|
|
} |
|
|
} |
|
|
|
|
|
static _disableAjaxCart(products) { |
|
|
/* |
|
|
Not fully fleshed out. Attempts to disable AJAX for stores using `ShopifyAPI.addItemFromForm` |
|
|
*/ |
|
|
let disableAjax = products.some(prod => prod.disable_ajax); |
|
|
|
|
|
if (disableAjax && ShopifyAPI && ShopifyAPI.addItemFromForm) { |
|
|
ShopifyAPI.addItemFromForm = form => { form.submit(); } |
|
|
} |
|
|
} |
|
|
|
|
|
static _checkAndGetProductForms(product) { |
|
|
try { |
|
|
return rcWidget |
|
|
._getProductForms(product) |
|
|
.filter(elem => elem.querySelector('#rc_subscription_id')); |
|
|
} catch (err) { |
|
|
console.error(`Product (${product.id}) has no product forms.`, err); |
|
|
return []; |
|
|
} |
|
|
} |
|
|
|
|
|
static _getProductForms(product) { |
|
|
let query = product.form_selector || `form[data-productid="${product.id}"]`; |
|
|
|
|
|
return Array.from(document.querySelectorAll(query)); |
|
|
} |
|
|
|
|
|
static _renameElements(form) { |
|
|
Helper.renameForIdPair(form, 'rc_purchase_type_onetime'); |
|
|
Helper.renameForIdPair(form, 'rc_purchase_type_autodeliver'); |
|
|
Helper.renameForIdPair(form, 'rc_shipping_interval_frequency'); |
|
|
} |
|
|
|
|
|
static _getFormElements(product, form) { |
|
|
|
|
|
product.options.purchaseType = (product.options.subscription_only || product.options.select_subscription_first) ? 'autodeliver' : 'onetime'; |
|
|
|
|
|
let elements = { |
|
|
// Purchase types |
|
|
'rcContainer': form.querySelector('#rc_container'), |
|
|
'purchaseTypes': form.querySelectorAll('[name="purchase_type"]'), |
|
|
'radioOnetime': form.querySelector('#rc_purchase_type_onetime'), |
|
|
'radioAutodeliver': form.querySelector('#rc_purchase_type_autodeliver'), |
|
|
|
|
|
// Subscription options |
|
|
'subscriptionId': form.querySelector('#rc_subscription_id'), |
|
|
'subscriptionIntervalType': form.querySelector('#rc_shipping_interval_unit_type'), |
|
|
'shippingIntervalFrequency': form.querySelector('#rc_shipping_interval_frequency'), |
|
|
'intervalOptions': form.querySelector('#rc_autodeliver_options'), |
|
|
|
|
|
// Variant selectors |
|
|
'productVariantSelect': form.querySelector('[name="id"]'), |
|
|
'duplicateVariantSelect': form.querySelector('#rc_duplicate_selector'), |
|
|
|
|
|
// Price elements |
|
|
'onetimePrice': form.querySelector('#rc_price_onetime'), |
|
|
'autodeliverPrice': form.querySelector('#rc_price_autodeliver'), |
|
|
|
|
|
'form': form |
|
|
}; |
|
|
|
|
|
// Set active elements |
|
|
Object.assign(elements, { |
|
|
'activePurchaseType': form.querySelector('input[type="radio"][value="' + product.options.purchaseType + '"]'), |
|
|
'activeProductSelect': (product.options.purchaseType == 'autodeliver') ? elements.duplicateVariantSelect : elements.productVariantSelect, |
|
|
}); |
|
|
|
|
|
Object.keys(elements) |
|
|
.filter(k => !elements[k]) |
|
|
.forEach(k => console.info(`[${product.id}] Missing product element: ${k}`, elements[k])); |
|
|
|
|
|
return elements; |
|
|
} |
|
|
|
|
|
static _disabledForDuplicates(product) { |
|
|
/* |
|
|
Set to false if we're disabling duplicate products |
|
|
- Return `true` if disable_duplicates is not set |
|
|
- Return `true` if disable_duplicates is set and product is subscription_only |
|
|
- Otherwise, disable duplicates and return false to prevent code block |
|
|
*/ |
|
|
if (!ReCharge.options.disable_duplicates) { |
|
|
return true; |
|
|
} |
|
|
if (ReCharge.options.disable_duplicates && !product.options.subscription_only) { |
|
|
return true; |
|
|
} |
|
|
return false; |
|
|
} |
|
|
|
|
|
static _getVariantInputs(product, elements) { |
|
|
/* |
|
|
Find any elements that may change variant ID |
|
|
- Search for either provided `options_selector` or via the list |
|
|
- Filter results, ignoring child items of #rc_container |
|
|
- Return array |
|
|
*/ |
|
|
let query = product.options.options_selector || 'select, input, textarea, button, a, span, div'; |
|
|
|
|
|
return Array.from(elements.form.querySelectorAll(query)) |
|
|
.filter(input => !Helper.findAncestor(input, 'rc_container')); |
|
|
} |
|
|
|
|
|
static _getCheckedInput(inputs) { |
|
|
return inputs.find(elem => elem.checked); |
|
|
} |
|
|
|
|
|
static _updatePurchaseType(product, type) { |
|
|
/* |
|
|
Updates product.options.purchaseType as needed |
|
|
- Return true or false if updated |
|
|
*/ |
|
|
if (product.options.purchaseType != type) { |
|
|
product.options.purchaseType = type; |
|
|
return true |
|
|
} |
|
|
return false; |
|
|
} |
|
|
|
|
|
static _updateActiveRadio(product) { |
|
|
product.elements.forEach((elements, i) => { |
|
|
elements.activePurchaseType.checked = true; |
|
|
}); |
|
|
} |
|
|
|
|
|
static _updateDuplicateSelect(product) { |
|
|
product.elements.forEach((elements, i) => { |
|
|
let variantId = elements.productVariantSelect.value, |
|
|
duplicateId = product.options.variant_to_duplicate[variantId]; |
|
|
|
|
|
elements.duplicateVariantSelect.value = duplicateId; |
|
|
}); |
|
|
} |
|
|
|
|
|
static _updateActiveAttributes(product) { |
|
|
if (product.options.purchaseType === 'autodeliver') { |
|
|
rcWidget._activateAutodeliverAttributes(product); |
|
|
} else { |
|
|
rcWidget._activateOnetimeAttributes(product); |
|
|
} |
|
|
} |
|
|
|
|
|
static _activateAutodeliverAttributes(product) { |
|
|
/* |
|
|
Set Subscription properties and form inputs for Autodeliver |
|
|
- Add values for `name` attributes on Subscription inputs |
|
|
- Update visible and dupliate select with correct name="id" attribute value |
|
|
- Only update select attributes if disable_duplicate isn't set to `true` |
|
|
- Update the activePurchaseType from the element list |
|
|
*/ |
|
|
product.elements.forEach((elements, i) => { |
|
|
elements.shippingIntervalFrequency.setAttribute('name', 'properties[shipping_interval_frequency]'); |
|
|
elements.subscriptionId.setAttribute('name', 'properties[subscription_id]'); |
|
|
elements.subscriptionIntervalType.setAttribute('name', 'properties[shipping_interval_unit_type]'); |
|
|
if (rcWidget._disabledForDuplicates(product)) { |
|
|
// Not needed if we're disabling duplicates |
|
|
elements.productVariantSelect.setAttribute('name', ''); |
|
|
elements.duplicateVariantSelect.setAttribute('name', 'id'); |
|
|
elements.activeProductSelect = elements.duplicateVariantSelect; |
|
|
} |
|
|
elements.activePurchaseType = Array.from(elements.purchaseTypes).find(input => input.value === 'autodeliver'); |
|
|
}); |
|
|
} |
|
|
|
|
|
static _activateOnetimeAttributes(product) { |
|
|
/* |
|
|
Set Subscription properties and form inputs for Onetime |
|
|
- Only proceed if is_subscription_only == true |
|
|
- Remove values for `name` attributes on Subscription inputs |
|
|
- Update visible and dupliate select with correct name="id" attribute value |
|
|
- Update the activePurchaseType from the element list |
|
|
*/ |
|
|
if (product.options.is_subscription_only) { |
|
|
// Not needed if subscription only |
|
|
return; |
|
|
} |
|
|
product.elements.forEach((elements, i) => { |
|
|
elements.shippingIntervalFrequency.setAttribute('name', ''); |
|
|
elements.subscriptionId.setAttribute('name', ''); |
|
|
elements.subscriptionIntervalType.setAttribute('name', ''); |
|
|
|
|
|
elements.productVariantSelect.setAttribute('name', 'id'); |
|
|
elements.duplicateVariantSelect.setAttribute('name', ''); |
|
|
elements.activeProductSelect = elements.productVariantSelect; |
|
|
|
|
|
elements.activePurchaseType = Array.from(elements.purchaseTypes).find(input => input.value === 'onetime'); |
|
|
}); |
|
|
} |
|
|
|
|
|
static _highlightActivePurchaseType(product) { |
|
|
product.elements.forEach(elem => { |
|
|
if (elem.radioAutodeliver && elem.radioOnetime) { |
|
|
let autodeliver = elem.rcContainer.querySelector('.rc_block__type__autodeliver'), |
|
|
onetime = elem.rcContainer.querySelector('.rc_block__type__onetime'); |
|
|
|
|
|
autodeliver.className = autodeliver.className.replace(' rc_block__type--active', ''); |
|
|
onetime.className = onetime.className.replace(' rc_block__type--active', ''); |
|
|
|
|
|
if (product.options.purchaseType === 'autodeliver') { |
|
|
autodeliver.className += ' rc_block__type--active'; |
|
|
} else { |
|
|
onetime.className += ' rc_block__type--active'; |
|
|
} |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
static _updateWidgetPricing(product, force_update) { |
|
|
/* |
|
|
Updates widget UI pricing labels |
|
|
- Don't update pricing if pricing updates are disabled via update_pricing option |
|
|
- Don't update pricing if purchaseType is Onetime |
|
|
*/ |
|
|
if (product.options.update_pricing) { |
|
|
let force = force_update || false; |
|
|
rcWidget._updateOnetimePrice(product, force); |
|
|
rcWidget._updateAutodeliverPrice(product, force); |
|
|
} |
|
|
} |
|
|
|
|
|
static _updatePricing(product, force_update) { |
|
|
/* |
|
|
Updates primary price element |
|
|
- Don't update pricing if pricing updates are disabled via update_pricing option |
|
|
- Don't update pricing if purchaseType is Onetime |
|
|
- Force price update if force_update is true (set) |
|
|
*/ |
|
|
if (product.options.update_pricing) { |
|
|
let force = force_update || false; |
|
|
if (product.options.purchaseType !== 'onetime' || product.options.purchaseType == 'onetime' && force) { |
|
|
rcWidget._updateProductPrice(product, force); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
static _updateOnetimePrice(product, force_update) { |
|
|
/* |
|
|
Updates the Onetime price indicator on the widget |
|
|
- Updates each occurance of the OneTime price for the product |
|
|
- Uses the variant ID with the variant_to_price object map to locate price |
|
|
- Runs price (in cents) through getFormattedPrice |
|
|
- Updates the innerHTML of the element |
|
|
- Force price update if the price was initiated by addCurrencyListener |
|
|
*/ |
|
|
let force = force_update || false; |
|
|
product.elements.forEach((elements, i) => { |
|
|
if (elements.onetimePrice) { |
|
|
let variantId = elements.productVariantSelect.value, |
|
|
price = product.options.variant_to_price[variantId]; |
|
|
|
|
|
if (product.options.price_onetime === price && !force) { |
|
|
return; |
|
|
} else { |
|
|
product.options.price_onetime = price; |
|
|
} |
|
|
|
|
|
if (!price) { |
|
|
console.warn(`[${product.id}] Price not found. Check product.options.variant_to_price[${variantId}] map.`, price); |
|
|
} |
|
|
|
|
|
elements.onetimePrice.innerHTML = Pricing.getFormattedPrice(product, price); |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
static _updateAutodeliverPrice(product, force_update) { |
|
|
/* |
|
|
Updates the Autodeliver price indicator on the widget |
|
|
- Updates each occurance of the Autodeliver price for the product |
|
|
- Uses the variant ID with the duplicate_to_price object map to locate price |
|
|
- Runs price (in cents) through getFormattedPrice |
|
|
- Updates the innerHTML of the element |
|
|
- Force price update if the price was initiated by addCurrencyListener |
|
|
*/ |
|
|
let force = force_update || false; |
|
|
product.elements.forEach((elements, i) => { |
|
|
if (elements.autodeliverPrice) { |
|
|
let variantId = elements.duplicateVariantSelect.value, |
|
|
price = product.options.duplicate_to_price[variantId]; |
|
|
|
|
|
if (product.options.price_autodeliver === price && !force) { |
|
|
return; |
|
|
} else { |
|
|
product.options.price_autodeliver = price; |
|
|
} |
|
|
|
|
|
if (!price) { |
|
|
console.warn(`[${product.id}] Price not found. Check product.duplicate_to_price[${variantId}] map.`, price); |
|
|
} |
|
|
|
|
|
elements.autodeliverPrice.innerHTML = Pricing.getFormattedPrice(product, price); |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
static _updateProductPrice(product, force_update) { |
|
|
/* |
|
|
Updates the primary product price elements |
|
|
- If active_price_search is enabled, perform price search again |
|
|
- If product price elements were found, iterate over each one |
|
|
- Determine the correct product price (onetime vs autorenew) with getSelectedPrice |
|
|
- Format price |
|
|
- Updates the innerHTML of the element |
|
|
- Force price update if the price was initiated by addCurrencyListener |
|
|
*/ |
|
|
let force = force_update || false; |
|
|
product.elements.forEach((elements, i) => { |
|
|
if (ReCharge.options.active_price_search) { |
|
|
elements.productPrice = Pricing.bottomUpPriceSearch(product, elements); |
|
|
} |
|
|
if (elements.productPrice.length) { |
|
|
let price = Pricing.getSelectedPrice(product, elements); |
|
|
|
|
|
if (product.options.price_product === price && !force) { |
|
|
return; |
|
|
} else { |
|
|
product.options.price_product = price; |
|
|
} |
|
|
|
|
|
let formattedPrice = Pricing.getFormattedPrice(product, price); |
|
|
|
|
|
elements.productPrice.forEach(elem => { |
|
|
elem.innerHTML = formattedPrice; |
|
|
}); |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
static _addPurchaseTypeListeners(product, elements) { |
|
|
/* |
|
|
Listeners attached to one-time/auto-deliver radio options |
|
|
- Triggers when swiching between purchase types |
|
|
- If purchase type is changed, update product.options.purchaseType |
|
|
- If purchase type is changed, update active attributes |
|
|
- If purchase type is changed, update pricing |
|
|
- If purchase type is changed, update interface elements |
|
|
- Do not update pricing if subscription only (no discount) |
|
|
- Force updatePricing |
|
|
- Products with a discount will need main price updated when switching between purchase types |
|
|
*/ |
|
|
if (product.options.subscription_only) { return; } |
|
|
|
|
|
let purchaseTypes = Array.from(elements.purchaseTypes); |
|
|
|
|
|
purchaseTypes |
|
|
.forEach(elem => { |
|
|
elem.addEventListener('click', ev => { |
|
|
let checkedInput = rcWidget._getCheckedInput(purchaseTypes); |
|
|
// Update interface if needed |
|
|
if (rcWidget._updatePurchaseType(product, checkedInput.value)) { |
|
|
rcWidget._updateActiveAttributes(product); |
|
|
rcWidget._updatePricing(product); |
|
|
rcWidget._updateActiveRadio(product); |
|
|
rcWidget._highlightActivePurchaseType(product); |
|
|
} |
|
|
}); |
|
|
}); |
|
|
} |
|
|
|
|
|
static _addIntervalOptionListeners(product, elements) { |
|
|
/* |
|
|
Listeners attached to interval options and the child select, interval frequency |
|
|
- If interval frequency select is clicked, check the autodelivery radio |
|
|
- If interval frequency select is changed, match the value in all form instances |
|
|
*/ |
|
|
if (elements.intervalOptions) { |
|
|
elements.intervalOptions.addEventListener('click', () => { |
|
|
product.elements.forEach(elements => { |
|
|
elements.radioAutodeliver.click(); |
|
|
}); |
|
|
}); |
|
|
elements.shippingIntervalFrequency.addEventListener('change', (evt) => { |
|
|
product.elements.forEach(elements => { |
|
|
elements.shippingIntervalFrequency.value = evt.target.value; |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
static _addVariantInputListeners(throttler, product, elements) { |
|
|
/* |
|
|
Listeners attached to variant/option selectors |
|
|
- Identify the ideal event listener per element |
|
|
- Attach functioners to identified event |
|
|
- If an option is changed, update the dupcate select with the correct variant |
|
|
- If an option is changed, update pricing (we need to check if price changed before trying to change it) |
|
|
*/ |
|
|
elements.variantInputs.forEach((elem) => { |
|
|
let listenerAction = Helper.getListenerAction(elem); |
|
|
elem.addEventListener(listenerAction, ev => { |
|
|
throttler.throttleAndDebounce(ev, ev => { |
|
|
if (rcWidget._disabledForDuplicates(product)) { |
|
|
rcWidget._updateDuplicateSelect(product); |
|
|
} |
|
|
if (!product.options.subscription_only) { |
|
|
rcWidget._updatePricing(product, true); // Not sure we need to force this anymore |
|
|
rcWidget._updateWidgetPricing(product); |
|
|
} |
|
|
}, product.options.delay_listener); |
|
|
}) |
|
|
}); |
|
|
} |
|
|
|
|
|
static _addPricingListeners(product) { |
|
|
/* |
|
|
Add listeners to any supported Currency Switchers |
|
|
- First check if product is subscription_only |
|
|
- Query required Currency Triggers |
|
|
- Identify valid Currency object |
|
|
- If objects found check if product |
|
|
- Trigger `_updatePricing` and `_updateWidgetPricing` if valid |
|
|
*/ |
|
|
if (product.options.subscription_only) { return; } |
|
|
Pricing.addCurrencyListener(elem => { |
|
|
let currencyConvertObj = window.Currency ? window.Currency : window.DoublyGlobalCurrency; |
|
|
currencyConvertObj.currentCurrency = elem.target.value; |
|
|
rcWidget._updatePricing(product, true); |
|
|
rcWidget._updateWidgetPricing(product, true); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
export default rcWidget; |