PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 5.0.1
WooCommerce v5.0.1
10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / assets / js / frontend / add-to-cart-variation.js
woocommerce / assets / js / frontend Last commit date
add-payment-method.js 8 years ago add-payment-method.min.js 5 years ago add-to-cart-variation.js 5 years ago add-to-cart-variation.min.js 5 years ago add-to-cart.js 5 years ago add-to-cart.min.js 5 years ago address-i18n.js 5 years ago address-i18n.min.js 5 years ago cart-fragments.js 5 years ago cart-fragments.min.js 5 years ago cart.js 6 years ago cart.min.js 5 years ago checkout.js 5 years ago checkout.min.js 5 years ago country-select.js 5 years ago country-select.min.js 5 years ago credit-card-form.js 8 years ago credit-card-form.min.js 8 years ago geolocation.js 6 years ago geolocation.min.js 5 years ago lost-password.js 8 years ago lost-password.min.js 8 years ago password-strength-meter.js 6 years ago password-strength-meter.min.js 5 years ago price-slider.js 5 years ago price-slider.min.js 5 years ago single-product.js 5 years ago single-product.min.js 5 years ago tokenization-form.js 6 years ago tokenization-form.min.js 5 years ago woocommerce.js 5 years ago woocommerce.min.js 5 years ago
add-to-cart-variation.js
819 lines
1 /*global wc_add_to_cart_variation_params */
2 ;(function ( $, window, document, undefined ) {
3 /**
4 * VariationForm class which handles variation forms and attributes.
5 */
6 var VariationForm = function( $form ) {
7 var self = this;
8
9 self.$form = $form;
10 self.$attributeFields = $form.find( '.variations select' );
11 self.$singleVariation = $form.find( '.single_variation' );
12 self.$singleVariationWrap = $form.find( '.single_variation_wrap' );
13 self.$resetVariations = $form.find( '.reset_variations' );
14 self.$product = $form.closest( '.product' );
15 self.variationData = $form.data( 'product_variations' );
16 self.useAjax = false === self.variationData;
17 self.xhr = false;
18 self.loading = true;
19
20 // Initial state.
21 self.$singleVariationWrap.show();
22 self.$form.off( '.wc-variation-form' );
23
24 // Methods.
25 self.getChosenAttributes = self.getChosenAttributes.bind( self );
26 self.findMatchingVariations = self.findMatchingVariations.bind( self );
27 self.isMatch = self.isMatch.bind( self );
28 self.toggleResetLink = self.toggleResetLink.bind( self );
29
30 // Events.
31 $form.on( 'click.wc-variation-form', '.reset_variations', { variationForm: self }, self.onReset );
32 $form.on( 'reload_product_variations', { variationForm: self }, self.onReload );
33 $form.on( 'hide_variation', { variationForm: self }, self.onHide );
34 $form.on( 'show_variation', { variationForm: self }, self.onShow );
35 $form.on( 'click', '.single_add_to_cart_button', { variationForm: self }, self.onAddToCart );
36 $form.on( 'reset_data', { variationForm: self }, self.onResetDisplayedVariation );
37 $form.on( 'reset_image', { variationForm: self }, self.onResetImage );
38 $form.on( 'change.wc-variation-form', '.variations select', { variationForm: self }, self.onChange );
39 $form.on( 'found_variation.wc-variation-form', { variationForm: self }, self.onFoundVariation );
40 $form.on( 'check_variations.wc-variation-form', { variationForm: self }, self.onFindVariation );
41 $form.on( 'update_variation_values.wc-variation-form', { variationForm: self }, self.onUpdateAttributes );
42
43 // Init after gallery.
44 setTimeout( function() {
45 $form.trigger( 'check_variations' );
46 $form.trigger( 'wc_variation_form', self );
47 self.loading = false;
48 }, 100 );
49 };
50
51 /**
52 * Reset all fields.
53 */
54 VariationForm.prototype.onReset = function( event ) {
55 event.preventDefault();
56 event.data.variationForm.$attributeFields.val( '' ).change();
57 event.data.variationForm.$form.trigger( 'reset_data' );
58 };
59
60 /**
61 * Reload variation data from the DOM.
62 */
63 VariationForm.prototype.onReload = function( event ) {
64 var form = event.data.variationForm;
65 form.variationData = form.$form.data( 'product_variations' );
66 form.useAjax = false === form.variationData;
67 form.$form.trigger( 'check_variations' );
68 };
69
70 /**
71 * When a variation is hidden.
72 */
73 VariationForm.prototype.onHide = function( event ) {
74 event.preventDefault();
75 event.data.variationForm.$form
76 .find( '.single_add_to_cart_button' )
77 .removeClass( 'wc-variation-is-unavailable' )
78 .addClass( 'disabled wc-variation-selection-needed' );
79 event.data.variationForm.$form
80 .find( '.woocommerce-variation-add-to-cart' )
81 .removeClass( 'woocommerce-variation-add-to-cart-enabled' )
82 .addClass( 'woocommerce-variation-add-to-cart-disabled' );
83 };
84
85 /**
86 * When a variation is shown.
87 */
88 VariationForm.prototype.onShow = function( event, variation, purchasable ) {
89 event.preventDefault();
90 if ( purchasable ) {
91 event.data.variationForm.$form
92 .find( '.single_add_to_cart_button' )
93 .removeClass( 'disabled wc-variation-selection-needed wc-variation-is-unavailable' );
94 event.data.variationForm.$form
95 .find( '.woocommerce-variation-add-to-cart' )
96 .removeClass( 'woocommerce-variation-add-to-cart-disabled' )
97 .addClass( 'woocommerce-variation-add-to-cart-enabled' );
98 } else {
99 event.data.variationForm.$form
100 .find( '.single_add_to_cart_button' )
101 .removeClass( 'wc-variation-selection-needed' )
102 .addClass( 'disabled wc-variation-is-unavailable' );
103 event.data.variationForm.$form
104 .find( '.woocommerce-variation-add-to-cart' )
105 .removeClass( 'woocommerce-variation-add-to-cart-enabled' )
106 .addClass( 'woocommerce-variation-add-to-cart-disabled' );
107 }
108
109 // If present, the media element library needs initialized on the variation description.
110 if ( wp.mediaelement ) {
111 event.data.variationForm.$form.find( '.wp-audio-shortcode, .wp-video-shortcode' )
112 .not( '.mejs-container' )
113 .filter(
114 function () {
115 return ! $( this ).parent().hasClass( 'mejs-mediaelement' );
116 }
117 )
118 .mediaelementplayer( wp.mediaelement.settings );
119 }
120 };
121
122 /**
123 * When the cart button is pressed.
124 */
125 VariationForm.prototype.onAddToCart = function( event ) {
126 if ( $( this ).is('.disabled') ) {
127 event.preventDefault();
128
129 if ( $( this ).is('.wc-variation-is-unavailable') ) {
130 window.alert( wc_add_to_cart_variation_params.i18n_unavailable_text );
131 } else if ( $( this ).is('.wc-variation-selection-needed') ) {
132 window.alert( wc_add_to_cart_variation_params.i18n_make_a_selection_text );
133 }
134 }
135 };
136
137 /**
138 * When displayed variation data is reset.
139 */
140 VariationForm.prototype.onResetDisplayedVariation = function( event ) {
141 var form = event.data.variationForm;
142 form.$product.find( '.product_meta' ).find( '.sku' ).wc_reset_content();
143 form.$product
144 .find( '.product_weight, .woocommerce-product-attributes-item--weight .woocommerce-product-attributes-item__value' )
145 .wc_reset_content();
146 form.$product
147 .find( '.product_dimensions, .woocommerce-product-attributes-item--dimensions .woocommerce-product-attributes-item__value' )
148 .wc_reset_content();
149 form.$form.trigger( 'reset_image' );
150 form.$singleVariation.slideUp( 200 ).trigger( 'hide_variation' );
151 };
152
153 /**
154 * When the product image is reset.
155 */
156 VariationForm.prototype.onResetImage = function( event ) {
157 event.data.variationForm.$form.wc_variations_image_update( false );
158 };
159
160 /**
161 * Looks for matching variations for current selected attributes.
162 */
163 VariationForm.prototype.onFindVariation = function( event, chosenAttributes ) {
164 var form = event.data.variationForm,
165 attributes = 'undefined' !== typeof chosenAttributes ? chosenAttributes : form.getChosenAttributes(),
166 currentAttributes = attributes.data;
167
168 if ( attributes.count && attributes.count === attributes.chosenCount ) {
169 if ( form.useAjax ) {
170 if ( form.xhr ) {
171 form.xhr.abort();
172 }
173 form.$form.block( { message: null, overlayCSS: { background: '#fff', opacity: 0.6 } } );
174 currentAttributes.product_id = parseInt( form.$form.data( 'product_id' ), 10 );
175 currentAttributes.custom_data = form.$form.data( 'custom_data' );
176 form.xhr = $.ajax( {
177 url: wc_add_to_cart_variation_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'get_variation' ),
178 type: 'POST',
179 data: currentAttributes,
180 success: function( variation ) {
181 if ( variation ) {
182 form.$form.trigger( 'found_variation', [ variation ] );
183 } else {
184 form.$form.trigger( 'reset_data' );
185 attributes.chosenCount = 0;
186
187 if ( ! form.loading ) {
188 form.$form
189 .find( '.single_variation' )
190 .after(
191 '<p class="wc-no-matching-variations woocommerce-info">' +
192 wc_add_to_cart_variation_params.i18n_no_matching_variations_text +
193 '</p>'
194 );
195 form.$form.find( '.wc-no-matching-variations' ).slideDown( 200 );
196 }
197 }
198 },
199 complete: function() {
200 form.$form.unblock();
201 }
202 } );
203 } else {
204 form.$form.trigger( 'update_variation_values' );
205
206 var matching_variations = form.findMatchingVariations( form.variationData, currentAttributes ),
207 variation = matching_variations.shift();
208
209 if ( variation ) {
210 form.$form.trigger( 'found_variation', [ variation ] );
211 } else {
212 form.$form.trigger( 'reset_data' );
213 attributes.chosenCount = 0;
214
215 if ( ! form.loading ) {
216 form.$form
217 .find( '.single_variation' )
218 .after(
219 '<p class="wc-no-matching-variations woocommerce-info">' +
220 wc_add_to_cart_variation_params.i18n_no_matching_variations_text +
221 '</p>'
222 );
223 form.$form.find( '.wc-no-matching-variations' ).slideDown( 200 );
224 }
225 }
226 }
227 } else {
228 form.$form.trigger( 'update_variation_values' );
229 form.$form.trigger( 'reset_data' );
230 }
231
232 // Show reset link.
233 form.toggleResetLink( attributes.chosenCount > 0 );
234 };
235
236 /**
237 * Triggered when a variation has been found which matches all attributes.
238 */
239 VariationForm.prototype.onFoundVariation = function( event, variation ) {
240 var form = event.data.variationForm,
241 $sku = form.$product.find( '.product_meta' ).find( '.sku' ),
242 $weight = form.$product.find(
243 '.product_weight, .woocommerce-product-attributes-item--weight .woocommerce-product-attributes-item__value'
244 ),
245 $dimensions = form.$product.find(
246 '.product_dimensions, .woocommerce-product-attributes-item--dimensions .woocommerce-product-attributes-item__value'
247 ),
248 $qty = form.$singleVariationWrap.find( '.quantity' ),
249 purchasable = true,
250 variation_id = '',
251 template = false,
252 $template_html = '';
253
254 if ( variation.sku ) {
255 $sku.wc_set_content( variation.sku );
256 } else {
257 $sku.wc_reset_content();
258 }
259
260 if ( variation.weight ) {
261 $weight.wc_set_content( variation.weight_html );
262 } else {
263 $weight.wc_reset_content();
264 }
265
266 if ( variation.dimensions ) {
267 // Decode HTML entities.
268 $dimensions.wc_set_content( $.parseHTML( variation.dimensions_html )[0].data );
269 } else {
270 $dimensions.wc_reset_content();
271 }
272
273 form.$form.wc_variations_image_update( variation );
274
275 if ( ! variation.variation_is_visible ) {
276 template = wp_template( 'unavailable-variation-template' );
277 } else {
278 template = wp_template( 'variation-template' );
279 variation_id = variation.variation_id;
280 }
281
282 $template_html = template( {
283 variation: variation
284 } );
285 $template_html = $template_html.replace( '/*<![CDATA[*/', '' );
286 $template_html = $template_html.replace( '/*]]>*/', '' );
287
288 form.$singleVariation.html( $template_html );
289 form.$form.find( 'input[name="variation_id"], input.variation_id' ).val( variation.variation_id ).change();
290
291 // Hide or show qty input
292 if ( variation.is_sold_individually === 'yes' ) {
293 $qty.find( 'input.qty' ).val( '1' ).attr( 'min', '1' ).attr( 'max', '' ).change();
294 $qty.hide();
295 } else {
296
297 var $qty_input = $qty.find( 'input.qty' ),
298 qty_val = parseFloat( $qty_input.val() );
299
300 if ( isNaN( qty_val ) ) {
301 qty_val = variation.min_qty;
302 } else {
303 qty_val = qty_val > parseFloat( variation.max_qty ) ? variation.max_qty : qty_val;
304 qty_val = qty_val < parseFloat( variation.min_qty ) ? variation.min_qty : qty_val;
305 }
306
307 $qty_input.attr( 'min', variation.min_qty ).attr( 'max', variation.max_qty ).val( qty_val ).change();
308 $qty.show();
309 }
310
311 // Enable or disable the add to cart button
312 if ( ! variation.is_purchasable || ! variation.is_in_stock || ! variation.variation_is_visible ) {
313 purchasable = false;
314 }
315
316 // Reveal
317 if ( $.trim( form.$singleVariation.text() ) ) {
318 form.$singleVariation.slideDown( 200 ).trigger( 'show_variation', [ variation, purchasable ] );
319 } else {
320 form.$singleVariation.show().trigger( 'show_variation', [ variation, purchasable ] );
321 }
322 };
323
324 /**
325 * Triggered when an attribute field changes.
326 */
327 VariationForm.prototype.onChange = function( event ) {
328 var form = event.data.variationForm;
329
330 form.$form.find( 'input[name="variation_id"], input.variation_id' ).val( '' ).change();
331 form.$form.find( '.wc-no-matching-variations' ).remove();
332
333 if ( form.useAjax ) {
334 form.$form.trigger( 'check_variations' );
335 } else {
336 form.$form.trigger( 'woocommerce_variation_select_change' );
337 form.$form.trigger( 'check_variations' );
338 }
339
340 // Custom event for when variation selection has been changed
341 form.$form.trigger( 'woocommerce_variation_has_changed' );
342 };
343
344 /**
345 * Escape quotes in a string.
346 * @param {string} string
347 * @return {string}
348 */
349 VariationForm.prototype.addSlashes = function( string ) {
350 string = string.replace( /'/g, '\\\'' );
351 string = string.replace( /"/g, '\\\"' );
352 return string;
353 };
354
355 /**
356 * Updates attributes in the DOM to show valid values.
357 */
358 VariationForm.prototype.onUpdateAttributes = function( event ) {
359 var form = event.data.variationForm,
360 attributes = form.getChosenAttributes(),
361 currentAttributes = attributes.data;
362
363 if ( form.useAjax ) {
364 return;
365 }
366
367 // Loop through selects and disable/enable options based on selections.
368 form.$attributeFields.each( function( index, el ) {
369 var current_attr_select = $( el ),
370 current_attr_name = current_attr_select.data( 'attribute_name' ) || current_attr_select.attr( 'name' ),
371 show_option_none = $( el ).data( 'show_option_none' ),
372 option_gt_filter = ':gt(0)',
373 attached_options_count = 0,
374 new_attr_select = $( '<select/>' ),
375 selected_attr_val = current_attr_select.val() || '',
376 selected_attr_val_valid = true;
377
378 // Reference options set at first.
379 if ( ! current_attr_select.data( 'attribute_html' ) ) {
380 var refSelect = current_attr_select.clone();
381
382 refSelect.find( 'option' ).removeAttr( 'disabled attached' ).removeAttr( 'selected' );
383
384 // Legacy data attribute.
385 current_attr_select.data(
386 'attribute_options',
387 refSelect.find( 'option' + option_gt_filter ).get()
388 );
389 current_attr_select.data( 'attribute_html', refSelect.html() );
390 }
391
392 new_attr_select.html( current_attr_select.data( 'attribute_html' ) );
393
394 // The attribute of this select field should not be taken into account when calculating its matching variations:
395 // The constraints of this attribute are shaped by the values of the other attributes.
396 var checkAttributes = $.extend( true, {}, currentAttributes );
397
398 checkAttributes[ current_attr_name ] = '';
399
400 var variations = form.findMatchingVariations( form.variationData, checkAttributes );
401
402 // Loop through variations.
403 for ( var num in variations ) {
404 if ( typeof( variations[ num ] ) !== 'undefined' ) {
405 var variationAttributes = variations[ num ].attributes;
406
407 for ( var attr_name in variationAttributes ) {
408 if ( variationAttributes.hasOwnProperty( attr_name ) ) {
409 var attr_val = variationAttributes[ attr_name ],
410 variation_active = '';
411
412 if ( attr_name === current_attr_name ) {
413 if ( variations[ num ].variation_is_active ) {
414 variation_active = 'enabled';
415 }
416
417 if ( attr_val ) {
418 // Decode entities.
419 attr_val = $( '<div/>' ).html( attr_val ).text();
420
421 // Attach to matching options by value. This is done to compare
422 // TEXT values rather than any HTML entities.
423 var $option_elements = new_attr_select.find( 'option' );
424 if ( $option_elements.length ) {
425 for (var i = 0, len = $option_elements.length; i < len; i++) {
426 var $option_element = $( $option_elements[i] ),
427 option_value = $option_element.val();
428
429 if ( attr_val === option_value ) {
430 $option_element.addClass( 'attached ' + variation_active );
431 break;
432 }
433 }
434 }
435 } else {
436 // Attach all apart from placeholder.
437 new_attr_select.find( 'option:gt(0)' ).addClass( 'attached ' + variation_active );
438 }
439 }
440 }
441 }
442 }
443 }
444
445 // Count available options.
446 attached_options_count = new_attr_select.find( 'option.attached' ).length;
447
448 // Check if current selection is in attached options.
449 if ( selected_attr_val ) {
450 selected_attr_val_valid = false;
451
452 if ( 0 !== attached_options_count ) {
453 new_attr_select.find( 'option.attached.enabled' ).each( function() {
454 var option_value = $( this ).val();
455
456 if ( selected_attr_val === option_value ) {
457 selected_attr_val_valid = true;
458 return false; // break.
459 }
460 });
461 }
462 }
463
464 // Detach the placeholder if:
465 // - Valid options exist.
466 // - The current selection is non-empty.
467 // - The current selection is valid.
468 // - Placeholders are not set to be permanently visible.
469 if ( attached_options_count > 0 && selected_attr_val && selected_attr_val_valid && ( 'no' === show_option_none ) ) {
470 new_attr_select.find( 'option:first' ).remove();
471 option_gt_filter = '';
472 }
473
474 // Detach unattached.
475 new_attr_select.find( 'option' + option_gt_filter + ':not(.attached)' ).remove();
476
477 // Finally, copy to DOM and set value.
478 current_attr_select.html( new_attr_select.html() );
479 current_attr_select.find( 'option' + option_gt_filter + ':not(.enabled)' ).prop( 'disabled', true );
480
481 // Choose selected value.
482 if ( selected_attr_val ) {
483 // If the previously selected value is no longer available, fall back to the placeholder (it's going to be there).
484 if ( selected_attr_val_valid ) {
485 current_attr_select.val( selected_attr_val );
486 } else {
487 current_attr_select.val( '' ).change();
488 }
489 } else {
490 current_attr_select.val( '' ); // No change event to prevent infinite loop.
491 }
492 });
493
494 // Custom event for when variations have been updated.
495 form.$form.trigger( 'woocommerce_update_variation_values' );
496 };
497
498 /**
499 * Get chosen attributes from form.
500 * @return array
501 */
502 VariationForm.prototype.getChosenAttributes = function() {
503 var data = {};
504 var count = 0;
505 var chosen = 0;
506
507 this.$attributeFields.each( function() {
508 var attribute_name = $( this ).data( 'attribute_name' ) || $( this ).attr( 'name' );
509 var value = $( this ).val() || '';
510
511 if ( value.length > 0 ) {
512 chosen ++;
513 }
514
515 count ++;
516 data[ attribute_name ] = value;
517 });
518
519 return {
520 'count' : count,
521 'chosenCount': chosen,
522 'data' : data
523 };
524 };
525
526 /**
527 * Find matching variations for attributes.
528 */
529 VariationForm.prototype.findMatchingVariations = function( variations, attributes ) {
530 var matching = [];
531 for ( var i = 0; i < variations.length; i++ ) {
532 var variation = variations[i];
533
534 if ( this.isMatch( variation.attributes, attributes ) ) {
535 matching.push( variation );
536 }
537 }
538 return matching;
539 };
540
541 /**
542 * See if attributes match.
543 * @return {Boolean}
544 */
545 VariationForm.prototype.isMatch = function( variation_attributes, attributes ) {
546 var match = true;
547 for ( var attr_name in variation_attributes ) {
548 if ( variation_attributes.hasOwnProperty( attr_name ) ) {
549 var val1 = variation_attributes[ attr_name ];
550 var val2 = attributes[ attr_name ];
551 if ( val1 !== undefined && val2 !== undefined && val1.length !== 0 && val2.length !== 0 && val1 !== val2 ) {
552 match = false;
553 }
554 }
555 }
556 return match;
557 };
558
559 /**
560 * Show or hide the reset link.
561 */
562 VariationForm.prototype.toggleResetLink = function( on ) {
563 if ( on ) {
564 if ( this.$resetVariations.css( 'visibility' ) === 'hidden' ) {
565 this.$resetVariations.css( 'visibility', 'visible' ).hide().fadeIn();
566 }
567 } else {
568 this.$resetVariations.css( 'visibility', 'hidden' );
569 }
570 };
571
572 /**
573 * Function to call wc_variation_form on jquery selector.
574 */
575 $.fn.wc_variation_form = function() {
576 new VariationForm( this );
577 return this;
578 };
579
580 /**
581 * Stores the default text for an element so it can be reset later
582 */
583 $.fn.wc_set_content = function( content ) {
584 if ( undefined === this.attr( 'data-o_content' ) ) {
585 this.attr( 'data-o_content', this.text() );
586 }
587 this.text( content );
588 };
589
590 /**
591 * Stores the default text for an element so it can be reset later
592 */
593 $.fn.wc_reset_content = function() {
594 if ( undefined !== this.attr( 'data-o_content' ) ) {
595 this.text( this.attr( 'data-o_content' ) );
596 }
597 };
598
599 /**
600 * Stores a default attribute for an element so it can be reset later
601 */
602 $.fn.wc_set_variation_attr = function( attr, value ) {
603 if ( undefined === this.attr( 'data-o_' + attr ) ) {
604 this.attr( 'data-o_' + attr, ( ! this.attr( attr ) ) ? '' : this.attr( attr ) );
605 }
606 if ( false === value ) {
607 this.removeAttr( attr );
608 } else {
609 this.attr( attr, value );
610 }
611 };
612
613 /**
614 * Reset a default attribute for an element so it can be reset later
615 */
616 $.fn.wc_reset_variation_attr = function( attr ) {
617 if ( undefined !== this.attr( 'data-o_' + attr ) ) {
618 this.attr( attr, this.attr( 'data-o_' + attr ) );
619 }
620 };
621
622 /**
623 * Reset the slide position if the variation has a different image than the current one
624 */
625 $.fn.wc_maybe_trigger_slide_position_reset = function( variation ) {
626 var $form = $( this ),
627 $product = $form.closest( '.product' ),
628 $product_gallery = $product.find( '.images' ),
629 reset_slide_position = false,
630 new_image_id = ( variation && variation.image_id ) ? variation.image_id : '';
631
632 if ( $form.attr( 'current-image' ) !== new_image_id ) {
633 reset_slide_position = true;
634 }
635
636 $form.attr( 'current-image', new_image_id );
637
638 if ( reset_slide_position ) {
639 $product_gallery.trigger( 'woocommerce_gallery_reset_slide_position' );
640 }
641 };
642
643 /**
644 * Sets product images for the chosen variation
645 */
646 $.fn.wc_variations_image_update = function( variation ) {
647 var $form = this,
648 $product = $form.closest( '.product' ),
649 $product_gallery = $product.find( '.images' ),
650 $gallery_nav = $product.find( '.flex-control-nav' ),
651 $gallery_img = $gallery_nav.find( 'li:eq(0) img' ),
652 $product_img_wrap = $product_gallery
653 .find( '.woocommerce-product-gallery__image, .woocommerce-product-gallery__image--placeholder' )
654 .eq( 0 ),
655 $product_img = $product_img_wrap.find( '.wp-post-image' ),
656 $product_link = $product_img_wrap.find( 'a' ).eq( 0 );
657
658 if ( variation && variation.image && variation.image.src && variation.image.src.length > 1 ) {
659 // See if the gallery has an image with the same original src as the image we want to switch to.
660 var galleryHasImage = $gallery_nav.find( 'li img[data-o_src="' + variation.image.gallery_thumbnail_src + '"]' ).length > 0;
661
662 // If the gallery has the image, reset the images. We'll scroll to the correct one.
663 if ( galleryHasImage ) {
664 $form.wc_variations_image_reset();
665 }
666
667 // See if gallery has a matching image we can slide to.
668 var slideToImage = $gallery_nav.find( 'li img[src="' + variation.image.gallery_thumbnail_src + '"]' );
669
670 if ( slideToImage.length > 0 ) {
671 slideToImage.trigger( 'click' );
672 $form.attr( 'current-image', variation.image_id );
673 window.setTimeout( function() {
674 $( window ).trigger( 'resize' );
675 $product_gallery.trigger( 'woocommerce_gallery_init_zoom' );
676 }, 20 );
677 return;
678 }
679
680 $product_img.wc_set_variation_attr( 'src', variation.image.src );
681 $product_img.wc_set_variation_attr( 'height', variation.image.src_h );
682 $product_img.wc_set_variation_attr( 'width', variation.image.src_w );
683 $product_img.wc_set_variation_attr( 'srcset', variation.image.srcset );
684 $product_img.wc_set_variation_attr( 'sizes', variation.image.sizes );
685 $product_img.wc_set_variation_attr( 'title', variation.image.title );
686 $product_img.wc_set_variation_attr( 'data-caption', variation.image.caption );
687 $product_img.wc_set_variation_attr( 'alt', variation.image.alt );
688 $product_img.wc_set_variation_attr( 'data-src', variation.image.full_src );
689 $product_img.wc_set_variation_attr( 'data-large_image', variation.image.full_src );
690 $product_img.wc_set_variation_attr( 'data-large_image_width', variation.image.full_src_w );
691 $product_img.wc_set_variation_attr( 'data-large_image_height', variation.image.full_src_h );
692 $product_img_wrap.wc_set_variation_attr( 'data-thumb', variation.image.src );
693 $gallery_img.wc_set_variation_attr( 'src', variation.image.gallery_thumbnail_src );
694 $product_link.wc_set_variation_attr( 'href', variation.image.full_src );
695 } else {
696 $form.wc_variations_image_reset();
697 }
698
699 window.setTimeout( function() {
700 $( window ).trigger( 'resize' );
701 $form.wc_maybe_trigger_slide_position_reset( variation );
702 $product_gallery.trigger( 'woocommerce_gallery_init_zoom' );
703 }, 20 );
704 };
705
706 /**
707 * Reset main image to defaults.
708 */
709 $.fn.wc_variations_image_reset = function() {
710 var $form = this,
711 $product = $form.closest( '.product' ),
712 $product_gallery = $product.find( '.images' ),
713 $gallery_nav = $product.find( '.flex-control-nav' ),
714 $gallery_img = $gallery_nav.find( 'li:eq(0) img' ),
715 $product_img_wrap = $product_gallery
716 .find( '.woocommerce-product-gallery__image, .woocommerce-product-gallery__image--placeholder' )
717 .eq( 0 ),
718 $product_img = $product_img_wrap.find( '.wp-post-image' ),
719 $product_link = $product_img_wrap.find( 'a' ).eq( 0 );
720
721 $product_img.wc_reset_variation_attr( 'src' );
722 $product_img.wc_reset_variation_attr( 'width' );
723 $product_img.wc_reset_variation_attr( 'height' );
724 $product_img.wc_reset_variation_attr( 'srcset' );
725 $product_img.wc_reset_variation_attr( 'sizes' );
726 $product_img.wc_reset_variation_attr( 'title' );
727 $product_img.wc_reset_variation_attr( 'data-caption' );
728 $product_img.wc_reset_variation_attr( 'alt' );
729 $product_img.wc_reset_variation_attr( 'data-src' );
730 $product_img.wc_reset_variation_attr( 'data-large_image' );
731 $product_img.wc_reset_variation_attr( 'data-large_image_width' );
732 $product_img.wc_reset_variation_attr( 'data-large_image_height' );
733 $product_img_wrap.wc_reset_variation_attr( 'data-thumb' );
734 $gallery_img.wc_reset_variation_attr( 'src' );
735 $product_link.wc_reset_variation_attr( 'href' );
736 };
737
738 $(function() {
739 if ( typeof wc_add_to_cart_variation_params !== 'undefined' ) {
740 $( '.variations_form' ).each( function() {
741 $( this ).wc_variation_form();
742 });
743 }
744 });
745
746 /**
747 * Matches inline variation objects to chosen attributes
748 * @deprecated 2.6.9
749 * @type {Object}
750 */
751 var wc_variation_form_matcher = {
752 find_matching_variations: function( product_variations, settings ) {
753 var matching = [];
754 for ( var i = 0; i < product_variations.length; i++ ) {
755 var variation = product_variations[i];
756
757 if ( wc_variation_form_matcher.variations_match( variation.attributes, settings ) ) {
758 matching.push( variation );
759 }
760 }
761 return matching;
762 },
763 variations_match: function( attrs1, attrs2 ) {
764 var match = true;
765 for ( var attr_name in attrs1 ) {
766 if ( attrs1.hasOwnProperty( attr_name ) ) {
767 var val1 = attrs1[ attr_name ];
768 var val2 = attrs2[ attr_name ];
769 if ( val1 !== undefined && val2 !== undefined && val1.length !== 0 && val2.length !== 0 && val1 !== val2 ) {
770 match = false;
771 }
772 }
773 }
774 return match;
775 }
776 };
777
778 /**
779 * Avoids using wp.template where possible in order to be CSP compliant.
780 * wp.template uses internally eval().
781 * @param {string} templateId
782 * @return {Function}
783 */
784 var wp_template = function( templateId ) {
785 var html = document.getElementById( 'tmpl-' + templateId ).textContent;
786 var hard = false;
787 // any <# #> interpolate (evaluate).
788 hard = hard || /<#\s?data\./.test( html );
789 // any data that is NOT data.variation.
790 hard = hard || /{{{?\s?data\.(?!variation\.).+}}}?/.test( html );
791 // any data access deeper than 1 level e.g.
792 // data.variation.object.item
793 // data.variation.object['item']
794 // data.variation.array[0]
795 hard = hard || /{{{?\s?data\.variation\.[\w-]*[^\s}]/.test ( html );
796 if ( hard ) {
797 return wp.template( templateId );
798 }
799 return function template ( data ) {
800 var variation = data.variation || {};
801 return html.replace( /({{{?)\s?data\.variation\.([\w-]*)\s?(}}}?)/g, function( _, open, key, close ) {
802 // Error in the format, ignore.
803 if ( open.length !== close.length ) {
804 return '';
805 }
806 var replacement = variation[ key ] || '';
807 // {{{ }}} => interpolate (unescaped).
808 // {{ }} => interpolate (escaped).
809 // https://codex.wordpress.org/Javascript_Reference/wp.template
810 if ( open.length === 2 ) {
811 return window.escape( replacement );
812 }
813 return replacement;
814 });
815 };
816 };
817
818 })( jQuery, window, document );
819