/* global wc_add_to_cart_variation_params */ ( function( $, window, document ) { 'use strict'; /** * Handles variation forms and attributes. * * @param {Object} $form The product form * @param {Object} element The product element * @param {Object} currentCart The current cart form * @param {Object} variableProductContainers The container for the variable product * @param {Object} epoObject The EPO Object */ var VariationForm = function( $form, element, currentCart, variableProductContainers, epoObject ) { var self = this; self.field = element; self.currentCart = currentCart; self.variableProductContainers = variableProductContainers; self.epoObject = epoObject; self.$form = $form; self.$wrap = $form.closest( '.tc-epo-element-product-container-wrap' ); self.$attributeFields = $form.find( '.tc-epo-variable-product-selector' ); self.$singleVariation = $form.find( '.tc-epo-element-single-variation' ); self.$resetVariations = $form.find( '.tc-epo-element-variable-reset-variations' ); self.variationData = $form.data( 'product_variations' ); self.useAjax = false === self.variationData; self.xhr = false; self.loading = true; self.variationId = $form.closest( '.tc-epo-element-product-li-container' ).find( '.tc-epo-element-product-container-variation-id input.product-variation-id' ); // Methods. self.getChosenAttributes = self.getChosenAttributes.bind( self ); self.findMatchingVariations = self.findMatchingVariations.bind( self ); self.isMatch = self.isMatch.bind( self ); self.toggleResetLink = self.toggleResetLink.bind( self ); // Events. $form.on( 'click.tc-variation-form', '.tc-epo-element-variable-reset-variations', { variationForm: self }, self.onReset ); $form.on( 'tc_hide_variation', { variationForm: self }, self.onHide ); $form.on( 'tc_show_variation', { variationForm: self }, self.onShow ); $form.on( 'tc_reset_data', { variationForm: self }, self.onResetDisplayedVariation ); $form.on( 'tc_reset_image', { variationForm: self }, self.onResetImage ); $form.on( 'change.tc-variation-form', '.tc-epo-variable-product-selector', { variationForm: self }, self.onChange ); $form.on( 'tc_found_variation.tc-variation-form', { variationForm: self }, self.onFoundVariation ); $form.on( 'tc_check_variations.tc-variation-form', { variationForm: self }, self.onFindVariation ); $form.on( 'tc_update_variation_values.tc-variation-form', { variationForm: self }, self.onUpdateAttributes ); $form.on( 'refresh.tc-variation-form', { variationForm: self }, self.onRefreshContainer ); $form.on( 'update_field.tc-variation-form', { variationForm: self }, self.onUpdateField ); self.variationId.val( '' ); // Init after gallery. setTimeout( function() { $form.trigger( 'refresh' ); $form.trigger( 'tc_variation_form' ); self.loading = false; }, 100 ); }; /** * Refresh variations container * * @param {Event} event The event Object. */ VariationForm.prototype.onRefreshContainer = function( event ) { var form = event.data.variationForm; form.variableProductContainers.find( '.tc-epo-element-product-container' ).removeClass( 'variations_form' ); form.$form.addClass( 'variations_form' ); form.$form.trigger( 'tc_check_variations.tc-variation-form' ); }; /** * Reset all fields. * * @param {Event} event The event Object. * @param {Object} variation The variation Object. */ VariationForm.prototype.onUpdateField = function( event, variation ) { var form = event.data.variationForm; var field; var associatedSetter; var associatedPrice; var associatedRawPrice; var associatedOriginalPrice; var associatedRawOriginalPrice; event.preventDefault(); if ( ! variation ) { field = form.field; associatedSetter = field; if ( field.is( 'select' ) ) { associatedSetter = field.find( 'option:selected' ); } associatedPrice = 0; associatedRawPrice = 0; associatedOriginalPrice = 0; associatedRawOriginalPrice = 0; field.data( 'price_set', 1 ); associatedSetter.data( 'associated_price_set', 1 ); associatedSetter.data( 'price_set', 1 ); associatedSetter.data( 'raw_price', associatedRawPrice ); associatedSetter.data( 'raw_original_price', associatedRawOriginalPrice ); associatedSetter.data( 'price', associatedPrice ); associatedSetter.data( 'original_price', associatedOriginalPrice ); field.data( 'price-changed', 1 ); form.$form.trigger( { type: 'tm-epo-update', norules: 2 } ); form.currentCart.trigger( { type: 'tm-epo-update', norules: 2 } ); } }; /** * Reset all fields. * * @param {Event} event The event Object. */ VariationForm.prototype.onReset = function( event ) { event.preventDefault(); event.data.variationForm.$attributeFields.val( '' ).trigger( 'change' ); event.data.variationForm.$form.trigger( 'tc_reset_data' ); }; /** * When a variation is hidden. * * @param {Event} event The event Object. */ VariationForm.prototype.onHide = function( event ) { event.preventDefault(); }; /** * When a variation is shown. * * @param {Event} event The event Object. */ VariationForm.prototype.onShow = function( event ) { event.preventDefault(); }; /** * When displayed variation data is reset. * * @param {Event} event The event Object. */ VariationForm.prototype.onResetDisplayedVariation = function( event ) { var form = event.data.variationForm; form.$form.find( '.product-meta' ).find( '.tc-product-sku' ).tc_reset_content(); form.$form.trigger( 'tc_reset_image' ); form.$singleVariation.slideUp( 200 ).trigger( 'tc_hide_variation' ); }; /** * When the product image is reset. * * @param {Event} event The event Object. */ VariationForm.prototype.onResetImage = function( event ) { event.data.variationForm.$form.tc_variations_image_update( false ); }; /** * Looks for matching variations for current selected attributes. * * @param {Event} event The event Object. */ VariationForm.prototype.onFindVariation = function( event ) { var form = event.data.variationForm; var attributes = form.getChosenAttributes(); var currentAttributes = attributes.data; var cpfElement; var matching_variations; var variation; if ( attributes.count === attributes.chosenCount ) { if ( form.useAjax ) { if ( typeof wc_add_to_cart_variation_params === 'undefined' ) { return; } if ( form.xhr ) { form.xhr.abort(); } cpfElement = form.$form.closest( '.cpf-type-product' ); form.$form.block( { message: null, overlayCSS: { background: '#fff', opacity: 0.6 } } ); currentAttributes.product_id = parseInt( form.$form.data( 'product_id' ), 10 ); currentAttributes.discount = cpfElement.attr( 'data-discount' ); currentAttributes.discount_type = cpfElement.attr( 'data-discount-type' ); currentAttributes.discount_exclude_addons = cpfElement.attr( 'data-discount-exclude-addons' ); form.xhr = $.ajax( { url: wc_add_to_cart_variation_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'get_variation' ), type: 'POST', data: currentAttributes, success: function( svariation ) { if ( svariation ) { form.$form.trigger( 'tc_found_variation', [ svariation ] ); } else { form.$form.trigger( 'tc_reset_data' ); attributes.chosenCount = 0; if ( ! form.loading ) { form.$form.find( '.tc-epo-element-single-variation' ).after( '

' + wc_add_to_cart_variation_params.i18n_no_matching_variations_text + '

' ); form.$form.find( '.wc-no-matching-variations' ).slideDown( 200 ); form.$form.trigger( 'update_field', [] ); } } }, complete: function() { form.$form.unblock(); } } ); } else { form.$form.trigger( 'tc_update_variation_values' ); matching_variations = form.findMatchingVariations( form.variationData, currentAttributes ); variation = matching_variations.shift(); if ( variation ) { form.$form.trigger( 'tc_found_variation', [ variation ] ); } else { form.$form.trigger( 'tc_reset_data' ); attributes.chosenCount = 0; if ( ! form.loading ) { form.$form.find( '.tc-epo-element-single-variation' ).after( '

' + wc_add_to_cart_variation_params.i18n_no_matching_variations_text + '

' ); form.$form.find( '.wc-no-matching-variations' ).slideDown( 200 ); } } } } else { form.variationId.val( '' ).trigger( 'change' ); form.$form.trigger( 'update_field', [] ); form.$form.trigger( 'tc_update_variation_values' ); form.$form.trigger( 'tc_reset_data' ); } // Show reset link. form.toggleResetLink( attributes.chosenCount > 0 ); }; /** * Triggered when a variation has been found which matches all attributes. * * @param {Event} event The event Object. * @param {Object} variation The variation Object. */ VariationForm.prototype.onFoundVariation = function( event, variation ) { var form = event.data.variationForm, $sku = form.$form.find( '.product-meta' ).find( '.tc-product-sku' ), $qtyWrap = form.$wrap.find( '.tm-quantity-alt' ), $qty = $qtyWrap.find( 'input.tm-qty-alt' ), qtyMin = $.epoAPI.math.toInt( $qty.attr( 'data-min' ) ), qtyMax = $.epoAPI.math.toInt( $qty.attr( 'data-max' ) ), dataQtyMax = $qty.attr( 'data-max' ), purchasable = true, template = false, $template_html = ''; if ( variation.sku ) { $sku.tc_set_content( variation.sku ); } else { $sku.tc_reset_content(); } form.$form.tc_variations_image_update( variation ); if ( ! variation.variation_is_visible ) { template = wpTemplate( 'unavailable-variation-template' ); } else { template = wpTemplate( 'variation-template' ); } if ( form.field.attr( 'data-no-price' ) ) { variation.display_price = ''; variation.display_regular_price = ''; variation.price_html = ''; } form.$form.find( '.tc-epo-element-product-container-cart' ).find( '.stock' ).remove(); $template_html = template( { variation: variation } ); $template_html = $template_html.replace( '/**/', '' ); form.$singleVariation.html( $template_html ); form.variationId.val( variation.variation_id ).trigger( 'change' ); // Hide or show qty input if ( variation.is_sold_individually === 'yes' ) { $qty.val( '1' ).attr( 'min', '1' ).attr( 'max', '1' ); $qtyWrap.hide(); } else { if ( variation.min_qty !== '' && variation.min_qty > qtyMin && qtyMin > 0 ) { qtyMin = variation.min_qty; } if ( variation.max_qty !== '' && variation.max_qty < qtyMax ) { qtyMax = variation.max_qty; } if ( qtyMin !== '' ) { $qty.attr( 'min', qtyMin ); } else { $qty.removeAttr( 'min' ); } if ( dataQtyMax !== '' ) { $qty.attr( 'max', qtyMax ); } else { $qty.removeAttr( 'max' ); } if ( qtyMax && $.epoAPI.math.toInt( $qty.val() ) > qtyMax ) { $qty.val( qtyMax ); } if ( qtyMin > $.epoAPI.math.toInt( $qty.val() ) ) { $qty.val( qtyMin ); } $qtyWrap.show(); } if ( ! variation.is_purchasable || ! variation.is_in_stock || ! variation.variation_is_visible ) { purchasable = false; $qty.val( '0' ).attr( 'min', '0' ).attr( 'max', '0' ); $qtyWrap.hide(); } $qty.trigger( 'change' ); // Reveal if ( $.epoAPI.util.trim( form.$singleVariation.text() ) ) { form.$singleVariation.slideDown( 200 ).trigger( 'tc_show_variation', [ variation, purchasable ] ); } else { form.$singleVariation.show().trigger( 'tc_show_variation', [ variation, purchasable ] ); } form.$form.trigger( 'update_field', [ variation ] ); }; /** * Triggered when an attribute field changes. * * @param {Event} event The event Object. */ VariationForm.prototype.onChange = function( event ) { var form = event.data.variationForm; var field = form.field; form.variationId.val( '' ).trigger( 'change' ); form.$form.find( '.wc-no-matching-variations' ).remove(); if ( form.useAjax ) { form.$form.trigger( 'tc_check_variations' ); } else { form.$form.trigger( 'woocommerce_variation_select_change' ); form.$form.trigger( 'tc_check_variations' ); } // Custom event for when variation selection has been changed form.$form.trigger( 'woocommerce_variation_has_changed' ); setTimeout( function() { field.closest( '.tcowl-carousel' ).trigger( 'refresh.owl.carousel' ); }, 50 ); }; /** * Updates attributes in the DOM to show valid values. * * @param {Event} event The event Object. */ VariationForm.prototype.onUpdateAttributes = function( event ) { var form = event.data.variationForm, attributes = form.getChosenAttributes(), currentAttributes = attributes.data; if ( form.useAjax ) { return; } // Loop through selects and disable/enable options based on selections. form.$attributeFields.each( function( index, el ) { var current_attr_select = $( el ); var current_attr_name = current_attr_select.data( 'attribute_name' ) || current_attr_select.attr( 'name' ); var show_option_none = $( el ).data( 'show_option_none' ); var option_gt_filter = ':gt(0)'; var attached_options_count = 0; var new_attr_select = $( '