PageSourceSearch

https://propmd.com/wp-content/plugins/woocommerce/assets/js/frontend/add-to-cart-variation.js?ver=10.7.0

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