Commit 01c8ca8304 for woocommerce

commit 01c8ca8304985e271792f7f8eadcda34b5dcdcab
Author: Albert Juhé Lluveras <contact@albertjuhe.com>
Date:   Wed Dec 31 10:20:56 2025 +0100

    Disable add to cart button in variable products until the script has loaded (#62631)

    * Disabled add to cart button in variable products until the script has loaded

    * Autoformatting

    * Add changelog file

    * Simplify logic

diff --git a/plugins/woocommerce/changelog/fix-62448-disable-add-to-cart-button-variable-products b/plugins/woocommerce/changelog/fix-62448-disable-add-to-cart-button-variable-products
new file mode 100644
index 0000000000..299c7b31ee
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-62448-disable-add-to-cart-button-variable-products
@@ -0,0 +1,4 @@
+Significance: patch
+Type: enhancement
+
+Disable add to cart button in variable products until the script has loaded
diff --git a/plugins/woocommerce/client/legacy/js/frontend/add-to-cart-variation.js b/plugins/woocommerce/client/legacy/js/frontend/add-to-cart-variation.js
index 5f6de5e031..2df4717f71 100644
--- a/plugins/woocommerce/client/legacy/js/frontend/add-to-cart-variation.js
+++ b/plugins/woocommerce/client/legacy/js/frontend/add-to-cart-variation.js
@@ -1,49 +1,97 @@
 /*global wc_add_to_cart_variation_params */
-;(function ( $, window, document, undefined ) {
+( function ( $, window, document, undefined ) {
 	/**
 	 * VariationForm class which handles variation forms and attributes.
 	 */
-	var VariationForm = function( $form ) {
+	var VariationForm = function ( $form ) {
 		var self = this;

-		self.$form                = $form;
-		self.$attributeFields     = $form.find( '.variations select' );
-		self.$singleVariation     = $form.find( '.single_variation' );
+		self.$form = $form;
+		self.$attributeFields = $form.find( '.variations select' );
+		self.$singleVariation = $form.find( '.single_variation' );
 		self.$singleVariationWrap = $form.find( '.single_variation_wrap' );
-		self.$resetVariations     = $form.find( '.reset_variations' );
-		self.$resetAlert          = $form.find( '.reset_variations_alert' );
-		self.$product             = $form.closest( '.product' );
-		self.variationData        = $form.data( 'product_variations' );
-		self.useAjax              = false === self.variationData;
-		self.xhr                  = false;
-		self.loading              = true;
+		self.$resetVariations = $form.find( '.reset_variations' );
+		self.$resetAlert = $form.find( '.reset_variations_alert' );
+		self.$product = $form.closest( '.product' );
+		self.variationData = $form.data( 'product_variations' );
+		self.useAjax = false === self.variationData;
+		self.xhr = false;
+		self.loading = true;

 		// Initial state.
 		self.$singleVariationWrap.show();
 		self.$form.off( '.wc-variation-form' );

 		// Methods.
-		self.getChosenAttributes         = self.getChosenAttributes.bind( self );
-		self.findMatchingVariations      = self.findMatchingVariations.bind( self );
-		self.isMatch                     = self.isMatch.bind( self );
-		self.toggleResetLink             = self.toggleResetLink.bind( self );
-		self.showNoMatchingVariationsMsg = self.showNoMatchingVariationsMsg.bind( self );
+		self.getChosenAttributes = self.getChosenAttributes.bind( self );
+		self.findMatchingVariations = self.findMatchingVariations.bind( self );
+		self.isMatch = self.isMatch.bind( self );
+		self.toggleResetLink = self.toggleResetLink.bind( self );
+		self.showNoMatchingVariationsMsg =
+			self.showNoMatchingVariationsMsg.bind( self );

 		// Events.
-		$form.on( 'click.wc-variation-form', '.reset_variations', { variationForm: self }, self.onReset );
-		$form.on( 'reload_product_variations', { variationForm: self }, self.onReload );
+		$form.on(
+			'click.wc-variation-form',
+			'.reset_variations',
+			{ variationForm: self },
+			self.onReset
+		);
+		$form.on(
+			'reload_product_variations',
+			{ variationForm: self },
+			self.onReload
+		);
 		$form.on( 'hide_variation', { variationForm: self }, self.onHide );
 		$form.on( 'show_variation', { variationForm: self }, self.onShow );
-		$form.on( 'click', '.single_add_to_cart_button', { variationForm: self }, self.onAddToCart );
-		$form.on( 'reset_data', { variationForm: self }, self.onResetDisplayedVariation );
-		$form.on( 'reset_focus', { variationForm: self }, self.onResetVariationFocus );
-		$form.on( 'announce_reset', { variationForm: self }, self.onAnnounceReset );
-		$form.on( 'clear_reset_announcement', { variationForm: self }, self.onClearResetAnnouncement );
+		$form.on(
+			'click',
+			'.single_add_to_cart_button',
+			{ variationForm: self },
+			self.onAddToCart
+		);
+		$form.on(
+			'reset_data',
+			{ variationForm: self },
+			self.onResetDisplayedVariation
+		);
+		$form.on(
+			'reset_focus',
+			{ variationForm: self },
+			self.onResetVariationFocus
+		);
+		$form.on(
+			'announce_reset',
+			{ variationForm: self },
+			self.onAnnounceReset
+		);
+		$form.on(
+			'clear_reset_announcement',
+			{ variationForm: self },
+			self.onClearResetAnnouncement
+		);
 		$form.on( 'reset_image', { variationForm: self }, self.onResetImage );
-		$form.on( 'change.wc-variation-form', '.variations select', { variationForm: self }, self.onChange );
-		$form.on( 'found_variation.wc-variation-form', { variationForm: self }, self.onFoundVariation );
-		$form.on( 'check_variations.wc-variation-form', { variationForm: self }, self.onFindVariation );
-		$form.on( 'update_variation_values.wc-variation-form', { variationForm: self }, self.onUpdateAttributes );
+		$form.on(
+			'change.wc-variation-form',
+			'.variations select',
+			{ variationForm: self },
+			self.onChange
+		);
+		$form.on(
+			'found_variation.wc-variation-form',
+			{ variationForm: self },
+			self.onFoundVariation
+		);
+		$form.on(
+			'check_variations.wc-variation-form',
+			{ variationForm: self },
+			self.onFindVariation
+		);
+		$form.on(
+			'update_variation_values.wc-variation-form',
+			{ variationForm: self },
+			self.onUpdateAttributes
+		);
 		$form.on(
 			'keydown.wc-variation-form',
 			'.reset_variations',
@@ -52,9 +100,16 @@
 		);

 		// Init after gallery.
-		setTimeout( function() {
+		setTimeout( function () {
 			$form.trigger( 'check_variations' );
 			$form.trigger( 'wc_variation_form', self );
+
+			// For variable products, we disable the button by default to avoid
+			// shoppers submitting the form with invalid data. We remove the
+			// disabled attribute as soon as the script is loaded and let the
+			// script handle the button state.
+			// @see https://github.com/woocommerce/woocommerce/issues/62448
+			$form.find( '.single_add_to_cart_button' ).removeAttr( 'disabled' );
 			self.loading = false;
 		}, 100 );
 	};
@@ -62,7 +117,7 @@
 	/**
 	 * Reset all fields.
 	 */
-	VariationForm.prototype.onReset = function( event ) {
+	VariationForm.prototype.onReset = function ( event ) {
 		event.preventDefault();
 		event.data.variationForm.$attributeFields.val( '' ).trigger( 'change' );
 		event.data.variationForm.$form.trigger( 'announce_reset' );
@@ -73,17 +128,17 @@
 	/**
 	 * Reload variation data from the DOM.
 	 */
-	VariationForm.prototype.onReload = function( event ) {
-		var form           = event.data.variationForm;
+	VariationForm.prototype.onReload = function ( event ) {
+		var form = event.data.variationForm;
 		form.variationData = form.$form.data( 'product_variations' );
-		form.useAjax       = false === form.variationData;
+		form.useAjax = false === form.variationData;
 		form.$form.trigger( 'check_variations' );
 	};

 	/**
 	 * When a variation is hidden.
 	 */
-	VariationForm.prototype.onHide = function( event ) {
+	VariationForm.prototype.onHide = function ( event ) {
 		event.preventDefault();
 		event.data.variationForm.$form
 			.find( '.single_add_to_cart_button' )
@@ -98,12 +153,18 @@
 	/**
 	 * When a variation is shown.
 	 */
-	VariationForm.prototype.onShow = function( event, variation, purchasable ) {
+	VariationForm.prototype.onShow = function (
+		event,
+		variation,
+		purchasable
+	) {
 		event.preventDefault();
 		if ( purchasable ) {
 			event.data.variationForm.$form
 				.find( '.single_add_to_cart_button' )
-				.removeClass( 'disabled wc-variation-selection-needed wc-variation-is-unavailable' );
+				.removeClass(
+					'disabled wc-variation-selection-needed wc-variation-is-unavailable'
+				);
 			event.data.variationForm.$form
 				.find( '.woocommerce-variation-add-to-cart' )
 				.removeClass( 'woocommerce-variation-add-to-cart-disabled' )
@@ -121,13 +182,12 @@

 		// If present, the media element library needs initialized on the variation description.
 		if ( wp.mediaelement ) {
-			event.data.variationForm.$form.find( '.wp-audio-shortcode, .wp-video-shortcode' )
+			event.data.variationForm.$form
+				.find( '.wp-audio-shortcode, .wp-video-shortcode' )
 				.not( '.mejs-container' )
-				.filter(
-					function () {
-						return ! $( this ).parent().hasClass( 'mejs-mediaelement' );
-					}
-				)
+				.filter( function () {
+					return ! $( this ).parent().hasClass( 'mejs-mediaelement' );
+				} )
 				.mediaelementplayer( wp.mediaelement.settings );
 		}
 	};
@@ -135,14 +195,18 @@
 	/**
 	 * When the cart button is pressed.
 	 */
-	VariationForm.prototype.onAddToCart = function( event ) {
-		if ( $( this ).is('.disabled') ) {
+	VariationForm.prototype.onAddToCart = function ( event ) {
+		if ( $( this ).is( '.disabled' ) ) {
 			event.preventDefault();

-			if ( $( this ).is('.wc-variation-is-unavailable') ) {
-				window.alert( wc_add_to_cart_variation_params.i18n_unavailable_text );
-			} else if ( $( this ).is('.wc-variation-selection-needed') ) {
-				window.alert( wc_add_to_cart_variation_params.i18n_make_a_selection_text );
+			if ( $( this ).is( '.wc-variation-is-unavailable' ) ) {
+				window.alert(
+					wc_add_to_cart_variation_params.i18n_unavailable_text
+				);
+			} else if ( $( this ).is( '.wc-variation-selection-needed' ) ) {
+				window.alert(
+					wc_add_to_cart_variation_params.i18n_make_a_selection_text
+				);
 			}
 		}
 	};
@@ -150,14 +214,18 @@
 	/**
 	 * When displayed variation data is reset.
 	 */
-	VariationForm.prototype.onResetDisplayedVariation = function( event ) {
+	VariationForm.prototype.onResetDisplayedVariation = function ( event ) {
 		var form = event.data.variationForm;
 		form.$product.find( '.product_meta' ).find( '.sku' ).wc_reset_content();
 		form.$product
-			.find( '.product_weight, .woocommerce-product-attributes-item--weight .woocommerce-product-attributes-item__value' )
+			.find(
+				'.product_weight, .woocommerce-product-attributes-item--weight .woocommerce-product-attributes-item__value'
+			)
 			.wc_reset_content();
 		form.$product
-			.find( '.product_dimensions, .woocommerce-product-attributes-item--dimensions .woocommerce-product-attributes-item__value' )
+			.find(
+				'.product_dimensions, .woocommerce-product-attributes-item--dimensions .woocommerce-product-attributes-item__value'
+			)
 			.wc_reset_content();
 		form.$form.trigger( 'reset_image' );
 		form.$singleVariation.slideUp( 200 ).trigger( 'hide_variation' );
@@ -166,35 +234,43 @@
 	/**
 	 * Announce reset to screen readers.
 	 */
-	VariationForm.prototype.onAnnounceReset = function( event ) {
-		event.data.variationForm.$resetAlert.text( wc_add_to_cart_variation_params.i18n_reset_alert_text );
-	}
+	VariationForm.prototype.onAnnounceReset = function ( event ) {
+		event.data.variationForm.$resetAlert.text(
+			wc_add_to_cart_variation_params.i18n_reset_alert_text
+		);
+	};

 	/**
 	 * Focus variation reset
 	 */
-	VariationForm.prototype.onResetVariationFocus = function( event ) {
-		event.data.variationForm.$attributeFields[0].focus();
-	}
+	VariationForm.prototype.onResetVariationFocus = function ( event ) {
+		event.data.variationForm.$attributeFields[ 0 ].focus();
+	};

 	/** Clear reset announcement */
-	VariationForm.prototype.onClearResetAnnouncement = function( event ) {
+	VariationForm.prototype.onClearResetAnnouncement = function ( event ) {
 		event.data.variationForm.$resetAlert.text( '' );
-	}
+	};

 	/**
 	 * When the product image is reset.
 	 */
-	VariationForm.prototype.onResetImage = function( event ) {
+	VariationForm.prototype.onResetImage = function ( event ) {
 		event.data.variationForm.$form.wc_variations_image_update( false );
 	};

 	/**
 	 * Looks for matching variations for current selected attributes.
 	 */
-	VariationForm.prototype.onFindVariation = function( event, chosenAttributes ) {
-		var form              = event.data.variationForm,
-			attributes        = 'undefined' !== typeof chosenAttributes ? chosenAttributes : form.getChosenAttributes(),
+	VariationForm.prototype.onFindVariation = function (
+		event,
+		chosenAttributes
+	) {
+		var form = event.data.variationForm,
+			attributes =
+				'undefined' !== typeof chosenAttributes
+					? chosenAttributes
+					: form.getChosenAttributes(),
 			currentAttributes = attributes.data;

 		if ( attributes.count && attributes.count === attributes.chosenCount ) {
@@ -202,16 +278,27 @@
 				if ( form.xhr ) {
 					form.xhr.abort();
 				}
-				form.$form.block( { message: null, overlayCSS: { background: '#fff', opacity: 0.6 } } );
-				currentAttributes.product_id  = parseInt( form.$form.data( 'product_id' ), 10 );
-				currentAttributes.custom_data = form.$form.data( 'custom_data' );
-				form.xhr                      = $.ajax( {
-					url: wc_add_to_cart_variation_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'get_variation' ),
+				form.$form.block( {
+					message: null,
+					overlayCSS: { background: '#fff', opacity: 0.6 },
+				} );
+				currentAttributes.product_id = parseInt(
+					form.$form.data( 'product_id' ),
+					10
+				);
+				currentAttributes.custom_data =
+					form.$form.data( 'custom_data' );
+				form.xhr = $.ajax( {
+					url: wc_add_to_cart_variation_params.wc_ajax_url
+						.toString()
+						.replace( '%%endpoint%%', 'get_variation' ),
 					type: 'POST',
 					data: currentAttributes,
-					success: function( variation ) {
+					success: function ( variation ) {
 						if ( variation ) {
-							form.$form.trigger( 'found_variation', [ variation ] );
+							form.$form.trigger( 'found_variation', [
+								variation,
+							] );
 						} else {
 							form.$form.trigger( 'reset_data' );
 							attributes.chosenCount = 0;
@@ -221,15 +308,18 @@
 							}
 						}
 					},
-					complete: function() {
+					complete: function () {
 						form.$form.unblock();
-					}
+					},
 				} );
 			} else {
 				form.$form.trigger( 'update_variation_values' );

-				var matching_variations = form.findMatchingVariations( form.variationData, currentAttributes ),
-					variation           = matching_variations.shift();
+				var matching_variations = form.findMatchingVariations(
+						form.variationData,
+						currentAttributes
+					),
+					variation = matching_variations.shift();

 				if ( variation ) {
 					form.$form.trigger( 'found_variation', [ variation ] );
@@ -254,20 +344,22 @@
 	/**
 	 * Triggered when a variation has been found which matches all attributes.
 	 */
-	VariationForm.prototype.onFoundVariation = function( event, variation ) {
-		var form           = event.data.variationForm,
-			$sku           = form.$product.find( '.product_meta' ).find( '.sku' ),
-			$weight        = form.$product.find(
+	VariationForm.prototype.onFoundVariation = function ( event, variation ) {
+		var form = event.data.variationForm,
+			$sku = form.$product.find( '.product_meta' ).find( '.sku' ),
+			$weight = form.$product.find(
 				'.product_weight, .woocommerce-product-attributes-item--weight .woocommerce-product-attributes-item__value'
 			),
-			$dimensions    = form.$product.find(
+			$dimensions = form.$product.find(
 				'.product_dimensions, .woocommerce-product-attributes-item--dimensions .woocommerce-product-attributes-item__value'
 			),
-			$qty_input     = form.$singleVariationWrap.find( '.quantity input.qty[name="quantity"]' ),
-			$qty           = $qty_input.closest( '.quantity' ),
-			purchasable    = true,
-			variation_id   = '',
-			template       = false,
+			$qty_input = form.$singleVariationWrap.find(
+				'.quantity input.qty[name="quantity"]'
+			),
+			$qty = $qty_input.closest( '.quantity' ),
+			purchasable = true,
+			variation_id = '',
+			template = false,
 			$template_html = '';

 		if ( variation.sku ) {
@@ -284,7 +376,9 @@

 		if ( variation.dimensions ) {
 			// Decode HTML entities.
-			$dimensions.wc_set_content( $.parseHTML( variation.dimensions_html )[0].data );
+			$dimensions.wc_set_content(
+				$.parseHTML( variation.dimensions_html )[ 0 ].data
+			);
 		} else {
 			$dimensions.wc_reset_content();
 		}
@@ -294,63 +388,89 @@
 		if ( ! variation.variation_is_visible ) {
 			template = wp_template( 'unavailable-variation-template' );
 		} else {
-			template     = wp_template( 'variation-template' );
+			template = wp_template( 'variation-template' );
 			variation_id = variation.variation_id;
 		}

 		$template_html = template( {
-			variation: variation
+			variation: variation,
 		} );
 		$template_html = $template_html.replace( '/*<![CDATA[*/', '' );
 		$template_html = $template_html.replace( '/*]]>*/', '' );

-		form.$form.find( 'input[name="variation_id"], input.variation_id' ).val( variation.variation_id ).trigger( 'change' );
+		form.$form
+			.find( 'input[name="variation_id"], input.variation_id' )
+			.val( variation.variation_id )
+			.trigger( 'change' );

 		// Hide or show qty input
 		if ( variation.is_sold_individually === 'yes' ) {
-			$qty_input.val( '1' ).attr( 'min', '1' ).attr( 'max', '' ).trigger( 'change' );
+			$qty_input
+				.val( '1' )
+				.attr( 'min', '1' )
+				.attr( 'max', '' )
+				.trigger( 'change' );
 			$qty.hide();
 		} else {
-
-			var qty_val    = parseFloat( $qty_input.val() );
+			var qty_val = parseFloat( $qty_input.val() );

 			if ( isNaN( qty_val ) ) {
 				qty_val = variation.min_qty;
 			} else {
-				qty_val = qty_val > parseFloat( variation.max_qty ) ? variation.max_qty : qty_val;
-				qty_val = qty_val < parseFloat( variation.min_qty ) ? variation.min_qty : qty_val;
+				qty_val =
+					qty_val > parseFloat( variation.max_qty )
+						? variation.max_qty
+						: qty_val;
+				qty_val =
+					qty_val < parseFloat( variation.min_qty )
+						? variation.min_qty
+						: qty_val;
 			}

-			$qty_input.attr( 'min', variation.min_qty ).attr( 'max', variation.max_qty ).val( qty_val ).trigger( 'change' );
+			$qty_input
+				.attr( 'min', variation.min_qty )
+				.attr( 'max', variation.max_qty )
+				.val( qty_val )
+				.trigger( 'change' );
 			$qty.show();
 		}

 		// Enable or disable the add to cart button
-		if ( ! variation.is_purchasable || ! variation.is_in_stock || ! variation.variation_is_visible ) {
+		if (
+			! variation.is_purchasable ||
+			! variation.is_in_stock ||
+			! variation.variation_is_visible
+		) {
 			purchasable = false;
 		}

 		// Add a delay before updating the live region to ensure screen readers pick up the content changes.
-		setTimeout( function() {
+		setTimeout( function () {
 			form.$singleVariation.html( $template_html );

 			// Reveal
 			if ( form.$singleVariation.text().trim() ) {
-				form.$singleVariation.slideDown( 200 ).trigger( 'show_variation', [ variation, purchasable ] );
+				form.$singleVariation
+					.slideDown( 200 )
+					.trigger( 'show_variation', [ variation, purchasable ] );
 			} else {
-				form.$singleVariation.show().trigger( 'show_variation', [ variation, purchasable ] );
+				form.$singleVariation
+					.show()
+					.trigger( 'show_variation', [ variation, purchasable ] );
 			}
-		}, 300);
-
+		}, 300 );
 	};

 	/**
 	 * Triggered when an attribute field changes.
 	 */
-	VariationForm.prototype.onChange = function( event ) {
+	VariationForm.prototype.onChange = function ( event ) {
 		var form = event.data.variationForm;

-		form.$form.find( 'input[name="variation_id"], input.variation_id' ).val( '' ).trigger( 'change' );
+		form.$form
+			.find( 'input[name="variation_id"], input.variation_id' )
+			.val( '' )
+			.trigger( 'change' );
 		form.$form.trigger( 'clear_reset_announcement' );
 		form.$form.find( '.wc-no-matching-variations' ).parent().remove();

@@ -370,18 +490,18 @@
 	 * @param {string} string
 	 * @return {string}
 	 */
-	VariationForm.prototype.addSlashes = function( string ) {
-		string = string.replace( /'/g, '\\\'' );
-		string = string.replace( /"/g, '\\\"' );
+	VariationForm.prototype.addSlashes = function ( string ) {
+		string = string.replace( /'/g, "\\'" );
+		string = string.replace( /"/g, '\\"' );
 		return string;
 	};

 	/**
 	 * Updates attributes in the DOM to show valid values.
 	 */
-	VariationForm.prototype.onUpdateAttributes = function( event ) {
-		var form              = event.data.variationForm,
-			attributes        = form.getChosenAttributes(),
+	VariationForm.prototype.onUpdateAttributes = function ( event ) {
+		var form = event.data.variationForm,
+			attributes = form.getChosenAttributes(),
 			currentAttributes = attributes.data;

 		if ( form.useAjax ) {
@@ -389,21 +509,27 @@
 		}

 		// Loop through selects and disable/enable options based on selections.
-		form.$attributeFields.each( function( index, el ) {
-			var current_attr_select     = $( el ),
-				current_attr_name       = current_attr_select.data( 'attribute_name' ) || current_attr_select.attr( 'name' ),
-				show_option_none        = $( el ).data( 'show_option_none' ),
-				option_gt_filter        = ':gt(0)',
-				attached_options_count  = 0,
-				new_attr_select         = $( '<select/>' ),
-				selected_attr_val       = current_attr_select.val() || '',
+		form.$attributeFields.each( function ( index, el ) {
+			var current_attr_select = $( el ),
+				current_attr_name =
+					current_attr_select.data( 'attribute_name' ) ||
+					current_attr_select.attr( 'name' ),
+				show_option_none = $( el ).data( 'show_option_none' ),
+				option_gt_filter = ':gt(0)',
+				attached_options_count = 0,
+				new_attr_select = $( '<select/>' ),
+				selected_attr_val = current_attr_select.val() || '',
 				selected_attr_val_valid = true;

 			// Reference options set at first.
 			if ( ! current_attr_select.data( 'attribute_html' ) ) {
 				var refSelect = current_attr_select.clone();

-				refSelect.find( 'option' ).removeAttr( 'attached' ).prop( 'disabled', false ).prop( 'selected', false );
+				refSelect
+					.find( 'option' )
+					.removeAttr( 'attached' )
+					.prop( 'disabled', false )
+					.prop( 'selected', false );

 				// Legacy data attribute.
 				current_attr_select.data(
@@ -413,7 +539,9 @@
 				current_attr_select.data( 'attribute_html', refSelect.html() );
 			}

-			new_attr_select.html( current_attr_select.data( 'attribute_html' ) );
+			new_attr_select.html(
+				current_attr_select.data( 'attribute_html' )
+			);

 			// The attribute of this select field should not be taken into account when calculating its matching variations:
 			// The constraints of this attribute are shaped by the values of the other attributes.
@@ -421,16 +549,19 @@

 			checkAttributes[ current_attr_name ] = '';

-			var variations = form.findMatchingVariations( form.variationData, checkAttributes );
+			var variations = form.findMatchingVariations(
+				form.variationData,
+				checkAttributes
+			);

 			// Loop through variations.
 			for ( var num in variations ) {
-				if ( typeof( variations[ num ] ) !== 'undefined' ) {
+				if ( typeof variations[ num ] !== 'undefined' ) {
 					var variationAttributes = variations[ num ].attributes;

 					for ( var attr_name in variationAttributes ) {
 						if ( variationAttributes.hasOwnProperty( attr_name ) ) {
-							var attr_val         = variationAttributes[ attr_name ],
+							var attr_val = variationAttributes[ attr_name ],
 								variation_active = '';

 							if ( attr_name === current_attr_name ) {
@@ -440,25 +571,43 @@

 								if ( attr_val ) {
 									// Decode entities.
-									attr_val = $( '<div/>' ).html( attr_val ).text();
+									attr_val = $( '<div/>' )
+										.html( attr_val )
+										.text();

 									// Attach to matching options by value. This is done to compare
 									// TEXT values rather than any HTML entities.
-									var $option_elements = new_attr_select.find( 'option' );
+									var $option_elements =
+										new_attr_select.find( 'option' );
 									if ( $option_elements.length ) {
-										for (var i = 0, len = $option_elements.length; i < len; i++) {
-											var $option_element = $( $option_elements[i] ),
-												option_value = $option_element.val();
+										for (
+											var i = 0,
+												len = $option_elements.length;
+											i < len;
+											i++
+										) {
+											var $option_element = $(
+													$option_elements[ i ]
+												),
+												option_value =
+													$option_element.val();

 											if ( attr_val === option_value ) {
-												$option_element.addClass( 'attached ' + variation_active );
+												$option_element.addClass(
+													'attached ' +
+														variation_active
+												);
 												break;
 											}
 										}
 									}
 								} else {
 									// Attach all apart from placeholder.
-									new_attr_select.find( 'option:gt(0)' ).addClass( 'attached ' + variation_active );
+									new_attr_select
+										.find( 'option:gt(0)' )
+										.addClass(
+											'attached ' + variation_active
+										);
 								}
 							}
 						}
@@ -467,21 +616,24 @@
 			}

 			// Count available options.
-			attached_options_count = new_attr_select.find( 'option.attached' ).length;
+			attached_options_count =
+				new_attr_select.find( 'option.attached' ).length;

 			// Check if current selection is in attached options.
 			if ( selected_attr_val ) {
 				selected_attr_val_valid = false;

 				if ( 0 !== attached_options_count ) {
-					new_attr_select.find( 'option.attached.enabled' ).each( function() {
-						var option_value = $( this ).val();
-
-						if ( selected_attr_val === option_value ) {
-							selected_attr_val_valid = true;
-							return false; // break.
-						}
-					});
+					new_attr_select
+						.find( 'option.attached.enabled' )
+						.each( function () {
+							var option_value = $( this ).val();
+
+							if ( selected_attr_val === option_value ) {
+								selected_attr_val_valid = true;
+								return false; // break.
+							}
+						} );
 				}
 			}

@@ -490,17 +642,26 @@
 			// - The current selection is non-empty.
 			// - The current selection is valid.
 			// - Placeholders are not set to be permanently visible.
-			if ( attached_options_count > 0 && selected_attr_val && selected_attr_val_valid && ( 'no' === show_option_none ) ) {
+			if (
+				attached_options_count > 0 &&
+				selected_attr_val &&
+				selected_attr_val_valid &&
+				'no' === show_option_none
+			) {
 				new_attr_select.find( 'option:first' ).remove();
 				option_gt_filter = '';
 			}

 			// Detach unattached.
-			new_attr_select.find( 'option' + option_gt_filter + ':not(.attached)' ).remove();
+			new_attr_select
+				.find( 'option' + option_gt_filter + ':not(.attached)' )
+				.remove();

 			// Finally, copy to DOM and set value.
 			current_attr_select.html( new_attr_select.html() );
-			current_attr_select.find( 'option' + option_gt_filter + ':not(.enabled)' ).prop( 'disabled', true );
+			current_attr_select
+				.find( 'option' + option_gt_filter + ':not(.enabled)' )
+				.prop( 'disabled', true );

 			// Choose selected value.
 			if ( selected_attr_val ) {
@@ -513,7 +674,7 @@
 			} else {
 				current_attr_select.val( '' ); // No change event to prevent infinite loop.
 			}
-		});
+		} );

 		// Custom event for when variations have been updated.
 		form.$form.trigger( 'woocommerce_update_variation_values' );
@@ -523,37 +684,41 @@
 	 * Get chosen attributes from form.
 	 * @return array
 	 */
-	VariationForm.prototype.getChosenAttributes = function() {
-		var data   = {};
-		var count  = 0;
+	VariationForm.prototype.getChosenAttributes = function () {
+		var data = {};
+		var count = 0;
 		var chosen = 0;

-		this.$attributeFields.each( function() {
-			var attribute_name = $( this ).data( 'attribute_name' ) || $( this ).attr( 'name' );
-			var value          = $( this ).val() || '';
+		this.$attributeFields.each( function () {
+			var attribute_name =
+				$( this ).data( 'attribute_name' ) || $( this ).attr( 'name' );
+			var value = $( this ).val() || '';

 			if ( value.length > 0 ) {
-				chosen ++;
+				chosen++;
 			}

-			count ++;
+			count++;
 			data[ attribute_name ] = value;
-		});
+		} );

 		return {
-			'count'      : count,
-			'chosenCount': chosen,
-			'data'       : data
+			count: count,
+			chosenCount: chosen,
+			data: data,
 		};
 	};

 	/**
 	 * Find matching variations for attributes.
 	 */
-	VariationForm.prototype.findMatchingVariations = function( variations, attributes ) {
+	VariationForm.prototype.findMatchingVariations = function (
+		variations,
+		attributes
+	) {
 		var matching = [];
 		for ( var i = 0; i < variations.length; i++ ) {
-			var variation = variations[i];
+			var variation = variations[ i ];

 			if ( this.isMatch( variation.attributes, attributes ) ) {
 				matching.push( variation );
@@ -566,13 +731,22 @@
 	 * See if attributes match.
 	 * @return {Boolean}
 	 */
-	VariationForm.prototype.isMatch = function( variation_attributes, attributes ) {
+	VariationForm.prototype.isMatch = function (
+		variation_attributes,
+		attributes
+	) {
 		var match = true;
 		for ( var attr_name in variation_attributes ) {
 			if ( variation_attributes.hasOwnProperty( attr_name ) ) {
 				var val1 = variation_attributes[ attr_name ];
 				var val2 = attributes[ attr_name ];
-				if ( val1 !== undefined && val2 !== undefined && val1.length !== 0 && val2.length !== 0 && val1 !== val2 ) {
+				if (
+					val1 !== undefined &&
+					val2 !== undefined &&
+					val1.length !== 0 &&
+					val2.length !== 0 &&
+					val1 !== val2
+				) {
 					match = false;
 				}
 			}
@@ -583,10 +757,13 @@
 	/**
 	 * Show or hide the reset link.
 	 */
-	VariationForm.prototype.toggleResetLink = function( on ) {
+	VariationForm.prototype.toggleResetLink = function ( on ) {
 		if ( on ) {
 			if ( this.$resetVariations.css( 'visibility' ) === 'hidden' ) {
-				this.$resetVariations.css( 'visibility', 'visible' ).hide().fadeIn();
+				this.$resetVariations
+					.css( 'visibility', 'visible' )
+					.hide()
+					.fadeIn();
 			}
 		} else {
 			this.$resetVariations.css( 'visibility', 'hidden' );
@@ -596,15 +773,15 @@
 	/**
 	 * Show no matching variation message.
 	 */
-	VariationForm.prototype.showNoMatchingVariationsMsg = function() {
+	VariationForm.prototype.showNoMatchingVariationsMsg = function () {
 		this.$form
 			.find( '.single_variation' )
 			.after(
 				'<div role="alert">' +
 					'<p class="wc-no-matching-variations woocommerce-info">' +
-						wc_add_to_cart_variation_params.i18n_no_matching_variations_text +
+					wc_add_to_cart_variation_params.i18n_no_matching_variations_text +
 					'</p>' +
-				'</div>'
+					'</div>'
 			)
 			.next( 'div' )
 			.find( '.wc-no-matching-variations' )
@@ -625,7 +802,7 @@
 	/**
 	 * Function to call wc_variation_form on jquery selector.
 	 */
-	$.fn.wc_variation_form = function() {
+	$.fn.wc_variation_form = function () {
 		new VariationForm( this );
 		return this;
 	};
@@ -633,7 +810,7 @@
 	/**
 	 * Stores the default text for an element so it can be reset later
 	 */
-	$.fn.wc_set_content = function( content ) {
+	$.fn.wc_set_content = function ( content ) {
 		if ( undefined === this.attr( 'data-o_content' ) ) {
 			this.attr( 'data-o_content', this.text() );
 		}
@@ -643,7 +820,7 @@
 	/**
 	 * Stores the default text for an element so it can be reset later
 	 */
-	$.fn.wc_reset_content = function() {
+	$.fn.wc_reset_content = function () {
 		if ( undefined !== this.attr( 'data-o_content' ) ) {
 			this.text( this.attr( 'data-o_content' ) );
 		}
@@ -652,9 +829,12 @@
 	/**
 	 * Stores a default attribute for an element so it can be reset later
 	 */
-	$.fn.wc_set_variation_attr = function( attr, value ) {
+	$.fn.wc_set_variation_attr = function ( attr, value ) {
 		if ( undefined === this.attr( 'data-o_' + attr ) ) {
-			this.attr( 'data-o_' + attr, ( ! this.attr( attr ) ) ? '' : this.attr( attr ) );
+			this.attr(
+				'data-o_' + attr,
+				! this.attr( attr ) ? '' : this.attr( attr )
+			);
 		}
 		if ( false === value ) {
 			this.removeAttr( attr );
@@ -666,7 +846,7 @@
 	/**
 	 * Reset a default attribute for an element so it can be reset later
 	 */
-	$.fn.wc_reset_variation_attr = function( attr ) {
+	$.fn.wc_reset_variation_attr = function ( attr ) {
 		if ( undefined !== this.attr( 'data-o_' + attr ) ) {
 			this.attr( attr, this.attr( 'data-o_' + attr ) );
 		}
@@ -675,12 +855,13 @@
 	/**
 	 * Reset the slide position if the variation has a different image than the current one
 	 */
-	$.fn.wc_maybe_trigger_slide_position_reset = function( variation ) {
-		var $form                = $( this ),
-			$product             = $form.closest( '.product' ),
-			$product_gallery     = $product.find( '.images' ),
+	$.fn.wc_maybe_trigger_slide_position_reset = function ( variation ) {
+		var $form = $( this ),
+			$product = $form.closest( '.product' ),
+			$product_gallery = $product.find( '.images' ),
 			reset_slide_position = false,
-			new_image_id         = ( variation && variation.image_id ) ? variation.image_id : '';
+			new_image_id =
+				variation && variation.image_id ? variation.image_id : '';

 		if ( $form.attr( 'current-image' ) !== new_image_id ) {
 			reset_slide_position = true;
@@ -689,28 +870,42 @@
 		$form.attr( 'current-image', new_image_id );

 		if ( reset_slide_position ) {
-			$product_gallery.trigger( 'woocommerce_gallery_reset_slide_position' );
+			$product_gallery.trigger(
+				'woocommerce_gallery_reset_slide_position'
+			);
 		}
 	};

 	/**
 	 * Sets product images for the chosen variation
 	 */
-	$.fn.wc_variations_image_update = function( variation ) {
-		var $form             = this,
-			$product          = $form.closest( '.product' ),
-			$product_gallery  = $product.find( '.images' ),
-			$gallery_nav      = $product.find( '.flex-control-nav' ),
-			$gallery_img      = $gallery_nav.find( 'li:eq(0) img' ),
+	$.fn.wc_variations_image_update = function ( variation ) {
+		var $form = this,
+			$product = $form.closest( '.product' ),
+			$product_gallery = $product.find( '.images' ),
+			$gallery_nav = $product.find( '.flex-control-nav' ),
+			$gallery_img = $gallery_nav.find( 'li:eq(0) img' ),
 			$product_img_wrap = $product_gallery
-				.find( '.woocommerce-product-gallery__image, .woocommerce-product-gallery__image--placeholder' )
+				.find(
+					'.woocommerce-product-gallery__image, .woocommerce-product-gallery__image--placeholder'
+				)
 				.eq( 0 ),
-			$product_img      = $product_img_wrap.find( '.wp-post-image' ),
-			$product_link     = $product_img_wrap.find( 'a' ).eq( 0 );
-
-		if ( variation && variation.image && variation.image.src && variation.image.src.length > 1 ) {
+			$product_img = $product_img_wrap.find( '.wp-post-image' ),
+			$product_link = $product_img_wrap.find( 'a' ).eq( 0 );
+
+		if (
+			variation &&
+			variation.image &&
+			variation.image.src &&
+			variation.image.src.length > 1
+		) {
 			// See if the gallery has an image with the same original src as the image we want to switch to.
-			var galleryHasImage = $gallery_nav.find( 'li img[data-o_src="' + variation.image.gallery_thumbnail_src + '"]' ).length > 0;
+			var galleryHasImage =
+				$gallery_nav.find(
+					'li img[data-o_src="' +
+						variation.image.gallery_thumbnail_src +
+						'"]'
+				).length > 0;

 			// If the gallery has the image, reset the images. We'll scroll to the correct one.
 			if ( galleryHasImage ) {
@@ -718,12 +913,14 @@
 			}

 			// See if gallery has a matching image we can slide to.
-			var slideToImage = $gallery_nav.find( 'li img[src="' + variation.image.gallery_thumbnail_src + '"]' );
+			var slideToImage = $gallery_nav.find(
+				'li img[src="' + variation.image.gallery_thumbnail_src + '"]'
+			);

 			if ( slideToImage.length > 0 ) {
 				slideToImage.trigger( 'flexslider-click' );
 				$form.attr( 'current-image', variation.image_id );
-				window.setTimeout( function() {
+				window.setTimeout( function () {
 					$( window ).trigger( 'resize' );
 					$product_gallery.trigger( 'woocommerce_gallery_init_zoom' );
 				}, 20 );
@@ -731,25 +928,64 @@
 			}

 			$product_img.wc_set_variation_attr( 'src', variation.image.src );
-			$product_img.wc_set_variation_attr( 'height', variation.image.src_h );
-			$product_img.wc_set_variation_attr( 'width', variation.image.src_w );
-			$product_img.wc_set_variation_attr( 'srcset', variation.image.srcset );
-			$product_img.wc_set_variation_attr( 'sizes', variation.image.sizes );
-			$product_img.wc_set_variation_attr( 'title', variation.image.title );
-			$product_img.wc_set_variation_attr( 'data-caption', variation.image.caption );
+			$product_img.wc_set_variation_attr(
+				'height',
+				variation.image.src_h
+			);
+			$product_img.wc_set_variation_attr(
+				'width',
+				variation.image.src_w
+			);
+			$product_img.wc_set_variation_attr(
+				'srcset',
+				variation.image.srcset
+			);
+			$product_img.wc_set_variation_attr(
+				'sizes',
+				variation.image.sizes
+			);
+			$product_img.wc_set_variation_attr(
+				'title',
+				variation.image.title
+			);
+			$product_img.wc_set_variation_attr(
+				'data-caption',
+				variation.image.caption
+			);
 			$product_img.wc_set_variation_attr( 'alt', variation.image.alt );
-			$product_img.wc_set_variation_attr( 'data-src', variation.image.full_src );
-			$product_img.wc_set_variation_attr( 'data-large_image', variation.image.full_src );
-			$product_img.wc_set_variation_attr( 'data-large_image_width', variation.image.full_src_w );
-			$product_img.wc_set_variation_attr( 'data-large_image_height', variation.image.full_src_h );
-			$product_img_wrap.wc_set_variation_attr( 'data-thumb', variation.image.src );
-			$gallery_img.wc_set_variation_attr( 'src', variation.image.gallery_thumbnail_src );
-			$product_link.wc_set_variation_attr( 'href', variation.image.full_src );
+			$product_img.wc_set_variation_attr(
+				'data-src',
+				variation.image.full_src
+			);
+			$product_img.wc_set_variation_attr(
+				'data-large_image',
+				variation.image.full_src
+			);
+			$product_img.wc_set_variation_attr(
+				'data-large_image_width',
+				variation.image.full_src_w
+			);
+			$product_img.wc_set_variation_attr(
+				'data-large_image_height',
+				variation.image.full_src_h
+			);
+			$product_img_wrap.wc_set_variation_attr(
+				'data-thumb',
+				variation.image.src
+			);
+			$gallery_img.wc_set_variation_attr(
+				'src',
+				variation.image.gallery_thumbnail_src
+			);
+			$product_link.wc_set_variation_attr(
+				'href',
+				variation.image.full_src
+			);
 		} else {
 			$form.wc_variations_image_reset();
 		}

-		window.setTimeout( function() {
+		window.setTimeout( function () {
 			$( window ).trigger( 'resize' );
 			$form.wc_maybe_trigger_slide_position_reset( variation );
 			$product_gallery.trigger( 'woocommerce_gallery_init_zoom' );
@@ -759,17 +995,19 @@
 	/**
 	 * Reset main image to defaults.
 	 */
-	$.fn.wc_variations_image_reset = function() {
-		var $form             = this,
-			$product          = $form.closest( '.product' ),
-			$product_gallery  = $product.find( '.images' ),
-			$gallery_nav      = $product.find( '.flex-control-nav' ),
-			$gallery_img      = $gallery_nav.find( 'li:eq(0) img' ),
+	$.fn.wc_variations_image_reset = function () {
+		var $form = this,
+			$product = $form.closest( '.product' ),
+			$product_gallery = $product.find( '.images' ),
+			$gallery_nav = $product.find( '.flex-control-nav' ),
+			$gallery_img = $gallery_nav.find( 'li:eq(0) img' ),
 			$product_img_wrap = $product_gallery
-				.find( '.woocommerce-product-gallery__image, .woocommerce-product-gallery__image--placeholder' )
+				.find(
+					'.woocommerce-product-gallery__image, .woocommerce-product-gallery__image--placeholder'
+				)
 				.eq( 0 ),
-			$product_img      = $product_img_wrap.find( '.wp-post-image' ),
-			$product_link     = $product_img_wrap.find( 'a' ).eq( 0 );
+			$product_img = $product_img_wrap.find( '.wp-post-image' ),
+			$product_link = $product_img_wrap.find( 'a' ).eq( 0 );

 		$product_img.wc_reset_variation_attr( 'src' );
 		$product_img.wc_reset_variation_attr( 'width' );
@@ -788,13 +1026,13 @@
 		$product_link.wc_reset_variation_attr( 'href' );
 	};

-	$(function() {
+	$( function () {
 		if ( typeof wc_add_to_cart_variation_params !== 'undefined' ) {
-			$( '.variations_form' ).each( function() {
+			$( '.variations_form' ).each( function () {
 				$( this ).wc_variation_form();
-			});
+			} );
 		}
-	});
+	} );

 	/**
 	 * Matches inline variation objects to chosen attributes
@@ -802,30 +1040,41 @@
 	 * @type {Object}
 	 */
 	var wc_variation_form_matcher = {
-		find_matching_variations: function( product_variations, settings ) {
+		find_matching_variations: function ( product_variations, settings ) {
 			var matching = [];
 			for ( var i = 0; i < product_variations.length; i++ ) {
-				var variation    = product_variations[i];
-
-				if ( wc_variation_form_matcher.variations_match( variation.attributes, settings ) ) {
+				var variation = product_variations[ i ];
+
+				if (
+					wc_variation_form_matcher.variations_match(
+						variation.attributes,
+						settings
+					)
+				) {
 					matching.push( variation );
 				}
 			}
 			return matching;
 		},
-		variations_match: function( attrs1, attrs2 ) {
+		variations_match: function ( attrs1, attrs2 ) {
 			var match = true;
 			for ( var attr_name in attrs1 ) {
 				if ( attrs1.hasOwnProperty( attr_name ) ) {
 					var val1 = attrs1[ attr_name ];
 					var val2 = attrs2[ attr_name ];
-					if ( val1 !== undefined && val2 !== undefined && val1.length !== 0 && val2.length !== 0 && val1 !== val2 ) {
+					if (
+						val1 !== undefined &&
+						val2 !== undefined &&
+						val1.length !== 0 &&
+						val2.length !== 0 &&
+						val1 !== val2
+					) {
 						match = false;
 					}
 				}
 			}
 			return match;
-		}
+		},
 	};

 	/**
@@ -834,7 +1083,7 @@
 	 * @param {string} templateId
 	 * @return {Function}
 	 */
-	var wp_template = function( templateId ) {
+	var wp_template = function ( templateId ) {
 		var html = document.getElementById( 'tmpl-' + templateId ).textContent;
 		var hard = false;
 		// any <# #> interpolate (evaluate).
@@ -845,27 +1094,29 @@
 		// data.variation.object.item
 		// data.variation.object['item']
 		// data.variation.array[0]
-		hard = hard || /{{{?\s?data\.variation\.[\w-]*[^\s}]/.test ( html );
+		hard = hard || /{{{?\s?data\.variation\.[\w-]*[^\s}]/.test( html );
 		if ( hard ) {
 			return wp.template( templateId );
 		}
-		return function template ( data ) {
+		return function template( data ) {
 			var variation = data.variation || {};
-			return html.replace( /({{{?)\s?data\.variation\.([\w-]*)\s?(}}}?)/g, function( _, open, key, close ) {
-				// Error in the format, ignore.
-				if ( open.length !== close.length ) {
-					return '';
-				}
-				var replacement = variation[ key ] || '';
-				// {{{ }}} => interpolate (unescaped).
-				// {{  }}  => interpolate (escaped).
-				// https://codex.wordpress.org/Javascript_Reference/wp.template
-				if ( open.length === 2 ) {
-					return window.escape( replacement );
+			return html.replace(
+				/({{{?)\s?data\.variation\.([\w-]*)\s?(}}}?)/g,
+				function ( _, open, key, close ) {
+					// Error in the format, ignore.
+					if ( open.length !== close.length ) {
+						return '';
+					}
+					var replacement = variation[ key ] || '';
+					// {{{ }}} => interpolate (unescaped).
+					// {{  }}  => interpolate (escaped).
+					// https://codex.wordpress.org/Javascript_Reference/wp.template
+					if ( open.length === 2 ) {
+						return window.escape( replacement );
+					}
+					return replacement;
 				}
-				return replacement;
-			});
+			);
 		};
 	};
-
-})( jQuery, window, document );
+} )( jQuery, window, document );
diff --git a/plugins/woocommerce/templates/single-product/add-to-cart/variation-add-to-cart-button.php b/plugins/woocommerce/templates/single-product/add-to-cart/variation-add-to-cart-button.php
index 162c6fad6c..6161e1104f 100644
--- a/plugins/woocommerce/templates/single-product/add-to-cart/variation-add-to-cart-button.php
+++ b/plugins/woocommerce/templates/single-product/add-to-cart/variation-add-to-cart-button.php
@@ -4,7 +4,7 @@
  *
  * @see https://woocommerce.com/document/template-structure/
  * @package WooCommerce\Templates
- * @version 10.2.0
+ * @version 10.5.0
  */

 defined( 'ABSPATH' ) || exit;
@@ -28,7 +28,7 @@ global $product;
 	do_action( 'woocommerce_after_add_to_cart_quantity' );
 	?>

-	<button type="submit" class="single_add_to_cart_button button alt<?php echo esc_attr( wc_wp_theme_get_element_class_name( 'button' ) ? ' ' . wc_wp_theme_get_element_class_name( 'button' ) : '' ); ?>"><?php echo esc_html( $product->single_add_to_cart_text() ); ?></button>
+	<button type="submit" class="single_add_to_cart_button button alt<?php echo esc_attr( wc_wp_theme_get_element_class_name( 'button' ) ? ' ' . wc_wp_theme_get_element_class_name( 'button' ) : '' ); ?>" disabled><?php echo esc_html( $product->single_add_to_cart_text() ); ?></button>

 	<?php do_action( 'woocommerce_after_add_to_cart_button' ); ?>