PageSourceSearch

https://biolifestyle.com.my/wp-content/plugins/woocommerce/assets/js/frontend/add-to-cart-variation.js

js biolifestyle.com.my collected 2026-10-06 00:59:28 UTC 30,253 bytes, 871 lines download raw bytes

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.$resetAlert          = $form.find( '.reset_variations_alert' );
15		self.$product             = $form.closest( '.product' );
16		self.variationData        = $form.data( 'product_variations' );
17		self.useAjax              = false === self.variationData;
18		self.xhr                  = false;
19		self.loading              = true;
20
21		// Initial state.
22		self.$singleVariationWrap.show();
23		self.$form.off( '.wc-variation-form' );
24
25		// Methods.
26		self.getChosenAttributes         = self.getChosenAttributes.bind( self );
27		self.findMatchingVariations      = self.findMatchingVariations.bind( self );
28		self.isMatch                     = self.isMatch.bind( self );
29		self.toggleResetLink             = self.toggleResetLink.bind( self );
30		self.showNoMatchingVariationsMsg = self.showNoMatchingVariationsMsg.bind( self );
31
32		// Events.
33		$form.on( 'click.wc-variation-form', '.reset_variations', { variationForm: self }, self.onReset );
34		$form.on( 'reload_product_variations', { variationForm: self }, self.onReload );
35		$form.on( 'hide_variation', { variationForm: self }, self.onHide );
36		$form.on( 'show_variation', { variationForm: self }, self.onShow );
37		$form.on( 'click', '.single_add_to_cart_button', { variationForm: self }, self.onAddToCart );
38		$form.on( 'reset_data', { variationForm: self }, self.onResetDisplayedVariation );
39		$form.on( 'reset_focus', { variationForm: self }, self.onResetVariationFocus );
40		$form.on( 'announce_reset', { variationForm: self }, self.onAnnounceReset );
41		$form.on( 'clear_reset_announcement', { variationForm: self }, self.onClearResetAnnouncement );
42		$form.on( 'reset_image', { variationForm: self }, self.onResetImage );
43		$form.on( 'change.wc-variation-form', '.variations select', { variationForm: self }, self.onChange );
44		$form.on( 'found_variation.wc-variation-form', { variationForm: self }, self.onFoundVariation );
45		$form.on( 'check_variations.wc-variation-form', { variationForm: self }, self.onFindVariation );
46		$form.on( 'update_variation_values.wc-variation-form', { variationForm: self }, self.onUpdateAttributes );
47		$form.on(
48			'keydown.wc-variation-form',
49			'.reset_variations',
50			{ variationForm: self },
51			self.onResetKeyDown
52		);
53
54		// Init after gallery.
55		setTimeout( function() {
56			$form.trigger( 'check_variations' );
57			$form.trigger( 'wc_variation_form', self );
58			self.loading = false;
59		}, 100 );
60	};
61
62	/**
63	 * Reset all fields.
64	 */
65	VariationForm.prototype.onReset = function( event ) {
66		event.preventDefault();
67		event.data.variationForm.$attributeFields.val( '' ).trigger( 'change' );
68		event.data.variationForm.$form.trigger( 'announce_reset' );
69		event.data.variationForm.$form.trigger( 'reset_data' );
70		event.data.variationForm.$form.trigger( 'reset_focus' );
71	};
72
73	/**
74	 * Reload variation data from the DOM.
75	 */
76	VariationForm.prototype.onReload = function( event ) {
77		var form           = event.data.variationForm;
78		form.variationData = form.$form.data( 'product_variations' );
79		form.useAjax       = false === form.variationData;
80		form.$form.trigger( 'check_variations' );
81	};
82
83	/**
84	 * When a variation is hidden.
85	 */
86	VariationForm.prototype.onHide = function( event ) {
87		event.preventDefault();
88		event.data.variationForm.$form
89			.find( '.single_add_to_cart_button' )
90			.removeClass( 'wc-variation-is-unavailable' )
91			.addClass( 'disabled wc-variation-selection-needed' );
92		event.data.variationForm.$form
93			.find( '.woocommerce-variation-add-to-cart' )
94			.removeClass( 'woocommerce-variation-add-to-cart-enabled' )
95			.addClass( 'woocommerce-variation-add-to-cart-disabled' );
96	};
97
98	/**
99	 * When a variation is shown.
100	 */
101	VariationForm.prototype.onShow = function( event, variation, purchasable ) {
102		event.preventDefault();
103		if ( purchasable ) {
104			event.data.variationForm.$form
105				.find( '.single_add_to_cart_button' )
106				.removeClass( 'disabled wc-variation-selection-needed wc-variation-is-unavailable' );
107			event.data.variationForm.$form
108				.find( '.woocommerce-variation-add-to-cart' )
109				.removeClass( 'woocommerce-variation-add-to-cart-disabled' )
110				.addClass( 'woocommerce-variation-add-to-cart-enabled' );
111		} else {
112			event.data.variationForm.$form
113				.find( '.single_add_to_cart_button' )
114				.removeClass( 'wc-variation-selection-needed' )
115				.addClass( 'disabled wc-variation-is-unavailable' );
116			event.data.variationForm.$form
117				.find( '.woocommerce-variation-add-to-cart' )
118				.removeClass( 'woocommerce-variation-add-to-cart-enabled' )
119				.addClass( 'woocommerce-variation-add-to-cart-disabled' );
120		}
121
122		// If present, the media element library needs initialized on the variation description.
123		if ( wp.mediaelement ) {
124			event.data.variationForm.$form.find( '.wp-audio-shortcode, .wp-video-shortcode' )
125				.not( '.mejs-container' )
126				.filter(
127					function () {
128						return ! $( this ).parent().hasClass( 'mejs-mediaelement' );
129					}
130				)
131				.mediaelementplayer( wp.mediaelement.settings );
132		}
133	};
134
135	/**
136	 * When the cart button is pressed.
137	 */
138	VariationForm.prototype.onAddToCart = function( event ) {
139		if ( $( this ).is('.disabled') ) {
140			event.preventDefault();
141
142			if ( $( this ).is('.wc-variation-is-unavailable') ) {
143				window.alert( wc_add_to_cart_variation_params.i18n_unavailable_text );
144			} else if ( $( this ).is('.wc-variation-selection-needed') ) {
145				window.alert( wc_add_to_cart_variation_params.i18n_make_a_selection_text );
146			}
147		}
148	};
149
150	/**
151	 * When displayed variation data is reset.
152	 */
153	VariationForm.prototype.onResetDisplayedVariation = function( event ) {
154		var form = event.data.variationForm;
155		form.$product.find( '.product_meta' ).find( '.sku' ).wc_reset_content();
156		form.$product
157			.find( '.product_weight, .woocommerce-product-attributes-item--weight .woocommerce-product-attributes-item__value' )
158			.wc_reset_content();
159		form.$product
160			.find( '.product_dimensions, .woocommerce-product-attributes-item--dimensions .woocommerce-product-attributes-item__value' )
161			.wc_reset_content();
162		form.$form.trigger( 'reset_image' );
163		form.$singleVariation.slideUp( 200 ).trigger( 'hide_variation' );
164	};
165
166	/**
167	 * Announce reset to screen readers.
168	 */
169	VariationForm.prototype.onAnnounceReset = function( event ) {
170		event.data.variationForm.$resetAlert.text( wc_add_to_cart_variation_params.i18n_reset_alert_text );
171	}
172
173	/**
174	 * Focus variation reset
175	 */
176	VariationForm.prototype.onResetVariationFocus = function( event ) {
177		event.data.variationForm.$attributeFields[0].focus();
178	}
179
180	/** Clear reset announcement */
181	VariationForm.prototype.onClearResetAnnouncement = function( event ) {
182		event.data.variationForm.$resetAlert.text( '' );
183	}
184
185	/**
186	 * When the product image is reset.
187	 */
188	VariationForm.prototype.onResetImage = function( event ) {
189		event.data.variationForm.$form.wc_variations_image_update( false );
190	};
191
192	/**
193	 * Looks for matching variations for current selected attributes.
194	 */
195	VariationForm.prototype.onFindVariation = function( event, chosenAttributes ) {
196		var form              = event.data.variationForm,
197			attributes        = 'undefined' !== typeof chosenAttributes ? chosenAttributes : form.getChosenAttributes(),
198			currentAttributes = attributes.data;
199
200		if ( attributes.count && attributes.count === attributes.chosenCount ) {
201			if ( form.useAjax ) {
202				if ( form.xhr ) {
203					form.xhr.abort();
204				}
205				form.$form.block( { message: null, overlayCSS: { background: '#fff', opacity: 0.6 } } );
206				currentAttributes.product_id  = parseInt( form.$form.data( 'product_id' ), 10 );
207				currentAttributes.custom_data = form.$form.data( 'custom_data' );
208				form.xhr                      = $.ajax( {
209					url: wc_add_to_cart_variation_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'get_variation' ),
210					type: 'POST',
211					data: currentAttributes,
212					success: function( variation ) {
213						if ( variation ) {
214							form.$form.trigger( 'found_variation', [ variation ] );
215						} else {
216							form.$form.trigger( 'reset_data' );
217							attributes.chosenCount = 0;
218
219							if ( ! form.loading ) {
220								form.showNoMatchingVariationsMsg();
221							}
222						}
223					},
224					complete: function() {
225						form.$form.unblock();
226					}
227				} );
228			} else {
229				form.$form.trigger( 'update_variation_values' );
230
231				var matching_variations = form.findMatchingVariations( form.variationData, currentAttributes ),
232					variation           = matching_variations.shift();
233
234				if ( variation ) {
235					form.$form.trigger( 'found_variation', [ variation ] );
236				} else {
237					form.$form.trigger( 'reset_data' );
238					attributes.chosenCount = 0;
239
240					if ( ! form.loading ) {
241						form.showNoMatchingVariationsMsg();
242					}
243				}
244			}
245		} else {
246			form.$form.trigger( 'update_variation_values' );
247			form.$form.trigger( 'reset_data' );
248		}
249
250		// Show reset link.
251		form.toggleResetLink( attributes.chosenCount > 0 );
252	};
253
254	/**
255	 * Triggered when a variation has been found which matches all attributes.
256	 */
257	VariationForm.prototype.onFoundVariation = function( event, variation ) {
258		var form           = event.data.variationForm,
259			$sku           = form.$product.find( '.product_meta' ).find( '.sku' ),
260			$weight        = form.$product.find(
261				'.product_weight, .woocommerce-product-attributes-item--weight .woocommerce-product-attributes-item__value'
262			),
263			$dimensions    = form.$product.find(
264				'.product_dimensions, .woocommerce-product-attributes-item--dimensions .woocommerce-product-attributes-item__value'
265			),
266			$qty_input     = form.$singleVariationWrap.find( '.quantity input.qty[name="quantity"]' ),
267			$qty           = $qty_input.closest( '.quantity' ),
268			purchasable    = true,
269			variation_id   = '',
270			template       = false,
271			$template_html = '';
272
273		if ( variation.sku ) {
274			$sku.wc_set_content( variation.sku );
275		} else {
276			$sku.wc_reset_content();
277		}
278
279		if ( variation.weight ) {
280			$weight.wc_set_content( variation.weight_html );
281		} else {
282			$weight.wc_reset_content();
283		}
284
285		if ( variation.dimensions ) {
286			// Decode HTML entities.
287			$dimensions.wc_set_content( $.parseHTML( variation.dimensions_html )[0].data );
288		} else {
289			$dimensions.wc_reset_content();
290		}
291
292		form.$form.wc_variations_image_update( variation );
293
294		if ( ! variation.variation_is_visible ) {
295			template = wp_template( 'unavailable-variation-template' );
296		} else {
297			template     = wp_template( 'variation-template' );
298			variation_id = variation.variation_id;
299		}
300
301		$template_html = template( {
302			variation: variation
303		} );
304		$template_html = $template_html.replace( '/*<![CDATA[*/', '' );
305		$template_html = $template_html.replace( '/*]]>*/', '' );
306
307		form.$form.find( 'input[name="variation_id"], input.variation_id' ).val( variation.variation_id ).trigger( 'change' );
308
309		// Hide or show qty input
310		if ( variation.is_sold_individually === 'yes' ) {
311			$qty_input.val( '1' ).attr( 'min', '1' ).attr( 'max', '' ).trigger( 'change' );
312			$qty.hide();
313		} else {
314
315			var qty_val    = parseFloat( $qty_input.val() );
316
317			if ( isNaN( qty_val ) ) {
318				qty_val = variation.min_qty;
319			} else {
320				qty_val = qty_val > parseFloat( variation.max_qty ) ? variation.max_qty : qty_val;
321				qty_val = qty_val < parseFloat( variation.min_qty ) ? variation.min_qty : qty_val;
322			}
323
324			$qty_input.attr( 'min', variation.min_qty ).attr( 'max', variation.max_qty ).val( qty_val ).trigger( 'change' );
325			$qty.show();
326		}
327
328		// Enable or disable the add to cart button
329		if ( ! variation.is_purchasable || ! variation.is_in_stock || ! variation.variation_is_visible ) {
330			purchasable = false;
331		}
332
333		// Add a delay before updating the live region to ensure screen readers pick up the content changes.
334		setTimeout( function() {
335			form.$singleVariation.html( $template_html );
336
337			// Reveal
338			if ( form.$singleVariation.text().trim() ) {
339				form.$singleVariation.slideDown( 200 ).trigger( 'show_variation', [ variation, purchasable ] );
340			} else {
341				form.$singleVariation.show().trigger( 'show_variation', [ variation, purchasable ] );
342			}
343		}, 300);
344
345	};
346
347	/**
348	 * Triggered when an attribute field changes.
349	 */
350	VariationForm.prototype.onChange = function( event ) {
351		var form = event.data.variationForm;
352
353		form.$form.find( 'input[name="variation_id"], input.variation_id' ).val( '' ).trigger( 'change' );
354		form.$form.trigger( 'clear_reset_announcement' );
355		form.$form.find( '.wc-no-matching-variations' ).parent().remove();
356
357		if ( form.useAjax ) {
358			form.$form.trigger( 'check_variations' );
359		} else {
360			form.$form.trigger( 'woocommerce_variation_select_change' );
361			form.$form.trigger( 'check_variations' );
362		}
363
364		// Custom event for when variation selection has been changed
365		form.$form.trigger( 'woocommerce_variation_has_changed' );
366	};
367
368	/**
369	 * Escape quotes in a string.
370	 * @param {string} string
371	 * @return {string}
372	 */
373	VariationForm.prototype.addSlashes = function( string ) {
374		string = string.replace( /'/g, '\\\'' );
375		string = string.replace( /"/g, '\\\"' );
376		return string;
377	};
378
379	/**
380	 * Updates attributes in the DOM to show valid values.
381	 */
382	VariationForm.prototype.onUpdateAttributes = function( event ) {
383		var form              = event.data.variationForm,
384			attributes        = form.getChosenAttributes(),
385			currentAttributes = attributes.data;
386
387		if ( form.useAjax ) {
388			return;
389		}
390
391		// Loop through selects and disable/enable options based on selections.
392		form.$attributeFields.each( function( index, el ) {
393			var current_attr_select     = $( el ),
394				current_attr_name       = current_attr_select.data( 'attribute_name' ) || current_attr_select.attr( 'name' ),
395				show_option_none        = $( el ).data( 'show_option_none' ),
396				option_gt_filter        = ':gt(0)',
397				attached_options_count  = 0,
398				new_attr_select         = $( '<select/>' ),
399				selected_attr_val       = current_attr_select.val() || '',
400				selected_attr_val_valid = true;
401
402			// Reference options set at first.
403			if ( ! current_attr_select.data( 'attribute_html' ) ) {
404				var refSelect = current_attr_select.clone();
405
406				refSelect.find( 'option' ).removeAttr( 'attached' ).prop( 'disabled', false ).prop( 'selected', false );
407
408				// Legacy data attribute.
409				current_attr_select.data(
410					'attribute_options',
411					refSelect.find( 'option' + option_gt_filter ).get()
412				);
413				current_attr_select.data( 'attribute_html', refSelect.html() );
414			}
415
416			new_attr_select.html( current_attr_select.data( 'attribute_html' ) );
417
418			// The attribute of this select field should not be taken into account when calculating its matching variations:
419			// The constraints of this attribute are shaped by the values of the other attributes.
420			var checkAttributes = $.extend( true, {}, currentAttributes );
421
422			checkAttributes[ current_attr_name ] = '';
423
424			var variations = form.findMatchingVariations( form.variationData, checkAttributes );
425
426			// Loop through variations.
427			for ( var num in variations ) {
428				if ( typeof( variations[ num ] ) !== 'undefined' ) {
429					var variationAttributes = variations[ num ].attributes;
430
431					for ( var attr_name in variationAttributes ) {
432						if ( variationAttributes.hasOwnProperty( attr_name ) ) {
433							var attr_val         = variationAttributes[ attr_name ],
434								variation_active = '';
435
436							if ( attr_name === current_attr_name ) {
437								if ( variations[ num ].variation_is_active ) {
438									variation_active = 'enabled';
439								}
440
441								if ( attr_val ) {
442									// Decode entities.
443									attr_val = $( '<div/>' ).html( attr_val ).text();
444
445									// Attach to matching options by value. This is done to compare
446									// TEXT values rather than any HTML entities.
447									var $option_elements = new_attr_select.find( 'option' );
448									if ( $option_elements.length ) {
449										for (var i = 0, len = $option_elements.length; i < len; i++) {
450											var $option_element = $( $option_elements[i] ),
451												option_value = $option_element.val();
452
453											if ( attr_val === option_value ) {
454												$option_element.addClass( 'attached ' + variation_active );
455												break;
456											}
457										}
458									}
459								} else {
460									// Attach all apart from placeholder.
461									new_attr_select.find( 'option:gt(0)' ).addClass( 'attached ' + variation_active );
462								}
463							}
464						}
465					}
466				}
467			}
468
469			// Count available options.
470			attached_options_count = new_attr_select.find( 'option.attached' ).length;
471
472			// Check if current selection is in attached options.
473			if ( selected_attr_val ) {
474				selected_attr_val_valid = false;
475
476				if ( 0 !== attached_options_count ) {
477					new_attr_select.find( 'option.attached.enabled' ).each( function() {
478						var option_value = $( this ).val();
479
480						if ( selected_attr_val === option_value ) {
481							selected_attr_val_valid = true;
482							return false; // break.
483						}
484					});
485				}
486			}
487
488			// Detach the placeholder if:
489			// - Valid options exist.
490			// - The current selection is non-empty.
491			// - The current selection is valid.
492			// - Placeholders are not set to be permanently visible.
493			if ( attached_options_count > 0 && selected_attr_val && selected_attr_val_valid && ( 'no' === show_option_none ) ) {
494				new_attr_select.find( 'option:first' ).remove();
495				option_gt_filter = '';
496			}
497
498			// Detach unattached.
499			new_attr_select.find( 'option' + option_gt_filter + ':not(.attached)' ).remove();
500
501			// Finally, copy to DOM and set value.
502			current_attr_select.html( new_attr_select.html() );
503			current_attr_select.find( 'option' + option_gt_filter + ':not(.enabled)' ).prop( 'disabled', true );
504
505			// Choose selected value.
506			if ( selected_attr_val ) {
507				// If the previously selected value is no longer available, fall back to the placeholder (it's going to be there).
508				if ( selected_attr_val_valid ) {
509					current_attr_select.val( selected_attr_val );
510				} else {
511					current_attr_select.val( '' ).trigger( 'change' );
512				}
513			} else {
514				current_attr_select.val( '' ); // No change event to prevent infinite loop.
515			}
516		});
517
518		// Custom event for when variations have been updated.
519		form.$form.trigger( 'woocommerce_update_variation_values' );
520	};
521
522	/**
523	 * Get chosen attributes from form.
524	 * @return array
525	 */
526	VariationForm.prototype.getChosenAttributes = function() {
527		var data   = {};
528		var count  = 0;
529		var chosen = 0;
530
531		this.$attributeFields.each( function() {
532			var attribute_name = $( this ).data( 'attribute_name' ) || $( this ).attr( 'name' );
533			var value          = $( this ).val() || '';
534
535			if ( value.length > 0 ) {
536				chosen ++;
537			}
538
539			count ++;
540			data[ attribute_name ] = value;
541		});
542
543		return {
544			'count'      : count,
545			'chosenCount': chosen,
546			'data'       : data
547		};
548	};
549
550	/**
551	 * Find matching variations for attributes.
552	 */
553	VariationForm.prototype.findMatchingVariations = function( variations, attributes ) {
554		var matching = [];
555		for ( var i = 0; i < variations.length; i++ ) {
556			var variation = variations[i];
557
558			if ( this.isMatch( variation.attributes, attributes ) ) {
559				matching.push( variation );
560			}
561		}
562		return matching;
563	};
564
565	/**
566	 * See if attributes match.
567	 * @return {Boolean}
568	 */
569	VariationForm.prototype.isMatch = function( variation_attributes, attributes ) {
570		var match = true;
571		for ( var attr_name in variation_attributes ) {
572			if ( variation_attributes.hasOwnProperty( attr_name ) ) {
573				var val1 = variation_attributes[ attr_name ];
574				var val2 = attributes[ attr_name ];
575				if ( val1 !== undefined && val2 !== undefined && val1.length !== 0 && val2.length !== 0 && val1 !== val2 ) {
576					match = false;
577				}
578			}
579		}
580		return match;
581	};
582
583	/**
584	 * Show or hide the reset link.
585	 */
586	VariationForm.prototype.toggleResetLink = function( on ) {
587		if ( on ) {
588			if ( this.$resetVariations.css( 'visibility' ) === 'hidden' ) {
589				this.$resetVariations.css( 'visibility', 'visible' ).hide().fadeIn();
590			}
591		} else {
592			this.$resetVariations.css( 'visibility', 'hidden' );
593		}
594	};
595
596	/**
597	 * Show no matching variation message.
598	 */
599	VariationForm.prototype.showNoMatchingVariationsMsg = function() {
600		this.$form
601			.find( '.single_variation' )
602			.after(
603				'<div role="alert">' +
604					'<p class="wc-no-matching-variations woocommerce-info">' +
605						wc_add_to_cart_variation_params.i18n_no_matching_variations_text +
606					'</p>' +
607				'</div>'
608			)
609			.next( 'div' )
610			.find( '.wc-no-matching-variations' )
611			.slideDown( 200 );
612	};
613
614	/**
615	 * Handle reset key down event for accessibility.
616	 * @param {KeyboardEvent} event - The keyboard event object
617	 */
618	VariationForm.prototype.onResetKeyDown = function ( event ) {
619		if ( event.code === 'Enter' || event.code === 'Space' ) {
620			event.preventDefault();
621			event.data.variationForm.onReset( event );
622		}
623	};
624
625	/**
626	 * Function to call wc_variation_form on jquery selector.
627	 */
628	$.fn.wc_variation_form = function() {
629		new VariationForm( this );
630		return this;
631	};
632
633	/**
634	 * Stores the default text for an element so it can be reset later
635	 */
636	$.fn.wc_set_content = function( content ) {
637		if ( undefined === this.attr( 'data-o_content' ) ) {
638			this.attr( 'data-o_content', this.text() );
639		}
640		this.text( content );
641	};
642
643	/**
644	 * Stores the default text for an element so it can be reset later
645	 */
646	$.fn.wc_reset_content = function() {
647		if ( undefined !== this.attr( 'data-o_content' ) ) {
648			this.text( this.attr( 'data-o_content' ) );
649		}
650	};
651
652	/**
653	 * Stores a default attribute for an element so it can be reset later
654	 */
655	$.fn.wc_set_variation_attr = function( attr, value ) {
656		if ( undefined === this.attr( 'data-o_' + attr ) ) {
657			this.attr( 'data-o_' + attr, ( ! this.attr( attr ) ) ? '' : this.attr( attr ) );
658		}
659		if ( false === value ) {
660			this.removeAttr( attr );
661		} else {
662			this.attr( attr, value );
663		}
664	};
665
666	/**
667	 * Reset a default attribute for an element so it can be reset later
668	 */
669	$.fn.wc_reset_variation_attr = function( attr ) {
670		if ( undefined !== this.attr( 'data-o_' + attr ) ) {
671			this.attr( attr, this.attr( 'data-o_' + attr ) );
672		}
673	};
674
675	/**
676	 * Reset the slide position if the variation has a different image than the current one
677	 */
678	$.fn.wc_maybe_trigger_slide_position_reset = function( variation ) {
679		var $form                = $( this ),
680			$product             = $form.closest( '.product' ),
681			$product_gallery     = $product.find( '.images' ),
682			reset_slide_position = false,
683			new_image_id         = ( variation && variation.image_id ) ? variation.image_id : '';
684
685		if ( $form.attr( 'current-image' ) !== new_image_id ) {
686			reset_slide_position = true;
687		}
688
689		$form.attr( 'current-image', new_image_id );
690
691		if ( reset_slide_position ) {
692			$product_gallery.trigger( 'woocommerce_gallery_reset_slide_position' );
693		}
694	};
695
696	/**
697	 * Sets product images for the chosen variation
698	 */
699	$.fn.wc_variations_image_update = function( variation ) {
700		var $form             = this,
701			$product          = $form.closest( '.product' ),
702			$product_gallery  = $product.find( '.images' ),
703			$gallery_nav      = $product.find( '.flex-control-nav' ),
704			$gallery_img      = $gallery_nav.find( 'li:eq(0) img' ),
705			$product_img_wrap = $product_gallery
706				.find( '.woocommerce-product-gallery__image, .woocommerce-product-gallery__image--placeholder' )
707				.eq( 0 ),
708			$product_img      = $product_img_wrap.find( '.wp-post-image' ),
709			$product_link     = $product_img_wrap.find( 'a' ).eq( 0 );
710
711		if ( variation && variation.image && variation.image.src && variation.image.src.length > 1 ) {
712			// See if the gallery has an image with the same original src as the image we want to switch to.
713			var galleryHasImage = $gallery_nav.find( 'li img[data-o_src="' + variation.image.gallery_thumbnail_src + '"]' ).length > 0;
714
715			// If the gallery has the image, reset the images. We'll scroll to the correct one.
716			if ( galleryHasImage ) {
717				$form.wc_variations_image_reset();
718			}
719
720			// See if gallery has a matching image we can slide to.
721			var slideToImage = $gallery_nav.find( 'li img[src="' + variation.image.gallery_thumbnail_src + '"]' );
722
723			if ( slideToImage.length > 0 ) {
724				slideToImage.trigger( 'flexslider-click' );
725				$form.attr( 'current-image', variation.image_id );
726				window.setTimeout( function() {
727					$( window ).trigger( 'resize' );
728					$product_gallery.trigger( 'woocommerce_gallery_init_zoom' );
729				}, 20 );
730				return;
731			}
732
733			$product_img.wc_set_variation_attr( 'src', variation.image.src );
734			$product_img.wc_set_variation_attr( 'height', variation.image.src_h );
735			$product_img.wc_set_variation_attr( 'width', variation.image.src_w );
736			$product_img.wc_set_variation_attr( 'srcset', variation.image.srcset );
737			$product_img.wc_set_variation_attr( 'sizes', variation.image.sizes );
738			$product_img.wc_set_variation_attr( 'title', variation.image.title );
739			$product_img.wc_set_variation_attr( 'data-caption', variation.image.caption );
740			$product_img.wc_set_variation_attr( 'alt', variation.image.alt );
741			$product_img.wc_set_variation_attr( 'data-src', variation.image.full_src );
742			$product_img.wc_set_variation_attr( 'data-large_image', variation.image.full_src );
743			$product_img.wc_set_variation_attr( 'data-large_image_width', variation.image.full_src_w );
744			$product_img.wc_set_variation_attr( 'data-large_image_height', variation.image.full_src_h );
745			$product_img_wrap.wc_set_variation_attr( 'data-thumb', variation.image.src );
746			$gallery_img.wc_set_variation_attr( 'src', variation.image.gallery_thumbnail_src );
747			$product_link.wc_set_variation_attr( 'href', variation.image.full_src );
748		} else {
749			$form.wc_variations_image_reset();
750		}
751
752		window.setTimeout( function() {
753			$( window ).trigger( 'resize' );
754			$form.wc_maybe_trigger_slide_position_reset( variation );
755			$product_gallery.trigger( 'woocommerce_gallery_init_zoom' );
756		}, 20 );
757	};
758
759	/**
760	 * Reset main image to defaults.
761	 */
762	$.fn.wc_variations_image_reset = function() {
763		var $form             = this,
764			$product          = $form.closest( '.product' ),
765			$product_gallery  = $product.find( '.images' ),
766			$gallery_nav      = $product.find( '.flex-control-nav' ),
767			$gallery_img      = $gallery_nav.find( 'li:eq(0) img' ),
768			$product_img_wrap = $product_gallery
769				.find( '.woocommerce-product-gallery__image, .woocommerce-product-gallery__image--placeholder' )
770				.eq( 0 ),
771			$product_img      = $product_img_wrap.find( '.wp-post-image' ),
772			$product_link     = $product_img_wrap.find( 'a' ).eq( 0 );
773
774		$product_img.wc_reset_variation_attr( 'src' );
775		$product_img.wc_reset_variation_attr( 'width' );
776		$product_img.wc_reset_variation_attr( 'height' );
777		$product_img.wc_reset_variation_attr( 'srcset' );
778		$product_img.wc_reset_variation_attr( 'sizes' );
779		$product_img.wc_reset_variation_attr( 'title' );
780		$product_img.wc_reset_variation_attr( 'data-caption' );
781		$product_img.wc_reset_variation_attr( 'alt' );
782		$product_img.wc_reset_variation_attr( 'data-src' );
783		$product_img.wc_reset_variation_attr( 'data-large_image' );
784		$product_img.wc_reset_variation_attr( 'data-large_image_width' );
785		$product_img.wc_reset_variation_attr( 'data-large_image_height' );
786		$product_img_wrap.wc_reset_variation_attr( 'data-thumb' );
787		$gallery_img.wc_reset_variation_attr( 'src' );
788		$product_link.wc_reset_variation_attr( 'href' );
789	};
790
791	$(function() {
792		if ( typeof wc_add_to_cart_variation_params !== 'undefined' ) {
793			$( '.variations_form' ).each( function() {
794				$( this ).wc_variation_form();
795			});
796		}
797	});
798
799	/**
800	 * Matches inline variation objects to chosen attributes
801	 * @deprecated 2.6.9
802	 * @type {Object}
803	 */
804	var wc_variation_form_matcher = {
805		find_matching_variations: function( product_variations, settings ) {
806			var matching = [];
807			for ( var i = 0; i < product_variations.length; i++ ) {
808				var variation    = product_variations[i];
809
810				if ( wc_variation_form_matcher.variations_match( variation.attributes, settings ) ) {
811					matching.push( variation );
812				}
813			}
814			return matching;
815		},
816		variations_match: function( attrs1, attrs2 ) {
817			var match = true;
818			for ( var attr_name in attrs1 ) {
819				if ( attrs1.hasOwnProperty( attr_name ) ) {
820					var val1 = attrs1[ attr_name ];
821					var val2 = attrs2[ attr_name ];
822					if ( val1 !== undefined && val2 !== undefined && val1.length !== 0 && val2.length !== 0 && val1 !== val2 ) {
823						match = false;
824					}
825				}
826			}
827			return match;
828		}
829	};
830
831	/**
832	 * Avoids using wp.template where possible in order to be CSP compliant.
833	 * wp.template uses internally eval().
834	 * @param {string} templateId
835	 * @return {Function}
836	 */
837	var wp_template = function( templateId ) {
838		var html = document.getElementById( 'tmpl-' + templateId ).textContent;
839		var hard = false;
840		// any <# #> interpolate (evaluate).
841		hard = hard || /<#\s?data\./.test( html );
842		// any data that is NOT data.variation.
843		hard = hard || /{{{?\s?data\.(?!variation\.).+}}}?/.test( html );
844		// any data access deeper than 1 level e.g.
845		// data.variation.object.item
846		// data.variation.object['item']
847		// data.variation.array[0]
848		hard = hard || /{{{?\s?data\.variation\.[\w-]*[^\s}]/.test ( html );
849		if ( hard ) {
850			return wp.template( templateId );
851		}
852		return function template ( data ) {
853			var variation = data.variation || {};
854			return html.replace( /({{{?)\s?data\.variation\.([\w-]*)\s?(}}}?)/g, function( _, open, key, close ) {
855				// Error in the format, ignore.
856				if ( open.length !== close.length ) {
857					return '';
858				}
859				var replacement = variation[ key ] || '';
860				// {{{ }}} => interpolate (unescaped).
861				// {{  }}  => interpolate (escaped).
862				// https://codex.wordpress.org/Javascript_Reference/wp.template
863				if ( open.length === 2 ) {
864					return window.escape( replacement );
865				}
866				return replacement;
867			});
868		};
869	};
870
871})( jQuery, window, document );

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.