PageSourceSearch

https://almerecity.nl/wp-content/themes/Impreza/common/js/plugins/woocommerce.js?ver=8.46.1

js almerecity.nl collected 2026-10-02 11:43:59 UTC 42,738 bytes, 1,376 lines download raw bytes

1! ( function( $, _undefined ) {
2
3	const $ush = window.$ush || {};
4
5	// The last AJAX object request.
6	var lastUpdateCartRequest;
7
8	/**
9	 * UpSolution WooCommerce elements.
10	 * Note: All classes and key elements from WooCommerce are retained
11	 *
12	 * The code depends on:
13	 * 	- `../plugins/woocommerce/assets/js/frontend/cart.js`
14	 * 	- `../plugins/woocommerce/assets/js/frontend/checkout.js`
15	 *
16	 * @requires $us.$body
17	 * @requires $us.$canvas
18	 * @requires $ush.debounce
19	 * @requires $ush.timeout
20	 */
21	function WooCommerce() {
22		const self = this;
23
24		// Elements
25		self.$cart = $( '.w-cart' );
26		self.$cartCloser = $( '.w-cart-closer', self.$cart );
27		self.$cartLink = $( '.w-cart-link', self.$cart );
28		self.$pageContent = $( '#page-content' );
29		self.$notice = $( '.w-wc-notices.woocommerce-notices-wrapper:first', $us.$canvas );
30		self.$addToCart = $( '.w-post-elm.add_to_cart', $us.$canvas );
31
32		// Private "Variables"
33		self.isCartLayoutPanel = self.$cart.hasClass( 'layout_left_panel' ) || self.$cart.hasClass( 'layout_right_panel' );
34		self.isCartOpen = false;
35		self.restVars = {};
36
37		// Event handlers
38		self._events = {
39			ajaxAddToCart: self.ajaxAddToCart.bind( self ),
40			applyCouponCode: self.applyCouponCode.bind( self ),
41			changeCartQuantity: self.changeCartQuantity.bind( self ),
42			couponCodeChange: self.couponCodeChange.bind( self ),
43			couponDisplaySwitch: self.couponDisplaySwitch.bind( self ),
44			enterCouponCode: self.enterCouponCode.bind( self ),
45			showNotification: self.showNotification.bind( self ),
46			loginFieldKeydown: self.loginFieldKeydown.bind( self ),
47			minusCartQuantity: self.minusCartQuantity.bind( self ),
48			moveNotifications: self.moveNotifications.bind( self ),
49			clickOutsideCart: self.clickOutsideCart.bind( self ),
50			plusCartQuantity: self.plusCartQuantity.bind( self ),
51			removeCartItem: self.removeCartItem.bind( self ),
52			showLoginForm: self.showLoginForm.bind( self ),
53			submitLoginForm: self.submitLoginForm.bind( self ),
54			updateCart: self.updateCart.bind( self ),
55			updatedCartTotals: self.updatedCartTotals.bind( self ),
56			showCartOnClick: self.showCartOnClick.bind( self ),
57			showCartOnKeyup: self.showCartOnKeyup.bind( self ),
58			hideCartOnKeyup: self.hideCartOnKeyup.bind( self ),
59			redirectToCartOnMobile: self.redirectToCartOnMobile.bind( self ),
60		};
61
62		// Init cart elements
63		if ( self.isCart() ) {
64
65			self.$cartNotification = $( '.w-cart-notification', self.$cart );
66
67			// Events
68			self.$cartNotification.on( 'click', () => {
69				self.$cartNotification.fadeOutCSS();
70			} );
71
72			// Cart closer button click events for both mobiles and desktops
73			if ( self.isCartLayoutPanel ) {
74				self.$cartCloser.on( 'click', () => self._toggleCart( false ) );
75			}
76
77			// Redirect to cart page on mobile devices.
78			if ( $.isMobile ) {
79				self.$cart.on( 'click', '.w-cart-link', self._events.redirectToCartOnMobile );
80
81				// Handle click event on desktop devices with panel layouts
82			} else {
83				if ( self.$cart.hasClass( 'drop_on_click' ) || self.$cart.is( '[class*="_panel"]' ) ) {
84					// need to separate these events because keyup event also manages focus
85					self.$cartLink.on( 'keyup', self._events.showCartOnKeyup );
86					self.$cartLink.on( 'click', self._events.showCartOnClick );
87				}
88
89				$us.$body.on( 'keyup', self._events.hideCartOnKeyup );
90
91				// Cart closer button events for desktops only
92				if ( self.isCartLayoutPanel ) {
93					self.$cartCloser.on( 'keyup', ( e ) => {
94						if ( e.keyCode === $ush.ENTER_KEYCODE ) {
95							self._toggleCart( false, self.$cartLink );
96						}
97					} );
98				}
99			}
100
101			$us.$body
102				// Events of `../plugins/woocommerce/assets/js/frontend/add-to-cart.js`,
103				// `../plugins/woocommerce/assets/js/frontend/cart-fragments.js`
104				.on( 'wc_fragments_loaded wc_fragments_refreshed', self._events.updateCart )
105				// Events of `../plugins/woocommerce/assets/js/frontend/add-to-cart.js`
106				.on( 'added_to_cart', self._events.showNotification )
107				.on( 'removed_from_cart', self._events.updateCart );
108		}
109
110		if ( self.isCartPage() ) {
111
112			// REST API vars
113			self.restVars = $( '.w-cart-table.woocommerce-cart-form:visible', $us.$canvas )
114				.data( 'rest-vars' );
115
116			// Events
117			$us.$body
118				.on( 'change', '.w-wc-coupon-form input', self._events.couponCodeChange )
119				.on( 'keyup', '.w-wc-coupon-form input', self._events.enterCouponCode )
120				.on( 'click', '.w-wc-coupon-form button', self._events.applyCouponCode )
121				.on( 'click', 'a.remove', self._events.removeCartItem )
122				// Events of `../plugins/woocommerce/assets/js/frontend/cart.js`
123				.on( 'applied_coupon removed_coupon', self._events.couponDisplaySwitch )
124				.on( 'updated_cart_totals', self._events.updatedCartTotals );
125
126			// Initializing controls after the ready document
127			$( 'input.qty', $us.$canvas ).trigger( 'initControls' );
128
129			// Get the last active request for cart updates
130			$.ajaxPrefilter( ( _, originalOptions, jqXHR ) => {
131				const data = $ush.toString( originalOptions.data );
132				if ( data.indexOf( '&update_cart' ) > -1 ) {
133					lastUpdateCartRequest = jqXHR;
134				}
135				// Distance information updates in shortcode `[us_cart_shipping]`
136				if ( data.indexOf( '&us_calc_shipping' ) > -1 ) {
137					jqXHR.done( ( res ) => {
138						$( '.w-cart-shipping .woocommerce-shipping-destination' )
139							.html( $( '.w-cart-shipping:first .woocommerce-shipping-destination', res ).html() );
140					} );
141				}
142			} );
143
144			$( '.w-cart-shipping form.woocommerce-shipping-calculator', $us.$canvas )
145				.append( '<input type="hidden" name="us_calc_shipping">' );
146		}
147
148		if ( self.isCheckoutPage() ) {
149			// Events
150			$us.$body
151				.on( 'change', '.w-wc-coupon-form input', self._events.couponCodeChange )
152				.on( 'keyup', '.w-wc-coupon-form input', self._events.enterCouponCode )
153				.on( 'click', '.w-wc-coupon-form button', self._events.applyCouponCode )
154				// Events of `../plugins/woocommerce/assets/js/frontend/checkout.js`
155				.on( 'applied_coupon_in_checkout removed_coupon_in_checkout', self._events.couponDisplaySwitch )
156				.on( 'applied_coupon_in_checkout removed_coupon_in_checkout checkout_error', self._events.moveNotifications )
157				.on( 'click', '.w-checkout-login .showlogin', self._events.showLoginForm )
158				.on( 'click', '.w-checkout-login button', self._events.submitLoginForm )
159				.on( 'keydown', '.w-checkout-login input, .w-checkout-login button', self._events.loginFieldKeydown );
160
161			// Blocks the form from being submitted if the coupon field is in focus
162			// and the Enter key is pressed, this allows the coupon to be applied
163			// correctly, otherwise the form will simply be submitted.
164			const $couponField = $( '.w-wc-coupon-form input', $us.$canvas );
165			$us.$document.on( 'keypress', ( e ) => {
166				if ( e.keyCode === $ush.ENTER_KEYCODE && $couponField.is( ':focus' ) ) {
167					e.preventDefault();
168				}
169			} );
170		}
171
172		// "Add to cart" via Ajax
173		if( $us.$body.hasClass( 'us-ajax_add_to_cart' ) ) {
174			$us.$body.on( 'click', '.single_add_to_cart_button:not(.disabled)', self._events.ajaxAddToCart );
175		}
176
177		// Quantity Events
178		$us.$body
179			.on( 'click', '.quantity input.minus', self._events.minusCartQuantity )
180			.on( 'click', '.quantity input.plus', self._events.plusCartQuantity )
181			.on( 'change initControls', '.quantity input.qty', self._events.changeCartQuantity );
182
183		$us.$document.on( 'ajaxComplete', ( _, jqXHR, settings ) => {
184
185			if (
186				settings.url.includes( 'add_to_cart' )
187				&& self.$cart.hasClass( 'open_on_ajax' )
188				&& self.isCartLayoutPanel
189			) {
190				self._toggleCart( true );
191				// waiting for transition - cannot focus element with visibility:hidden
192				self.$cart.on( 'transitionend', () => {
193					self.$cartCloser[0].focus();
194					self.$cart.off( 'transitionend' );
195				} );
196			}
197
198			// Skip JSON string
199			if ( String( jqXHR.responseText ).charAt(0) === '{' ) {
200				return;
201			}
202
203			const $fragment = $( new DocumentFragment ).append( jqXHR.responseText );
204
205			// Updated only visible "Cart Totals" elements, bypassing WooCommerce core logic. See #3935 for details.
206			if ( self.isCartPage() && $( '.cart_totals', $fragment ).length > 1 ) {
207				const notices = self.$notice.html();
208				self.$pageContent.html( $( '.l-main', $fragment ) );
209				self.$notice = $( '.w-wc-notices.woocommerce-notices-wrapper:first', $us.$canvas );
210				// Restore notifications
211				if ( notices ) {
212					self.$notice.html( notices );
213				}
214			}
215
216			// Intercept messages after apply a coupon.
217			if ( String( settings.url ).includes( 'wc-ajax=apply_coupon' ) ) {
218				const $message = $( '.woocommerce-error, .woocommerce-message', $fragment );
219				if ( $message.length > 0 ) {
220					self.$notice.html( $message.clone() );
221				} else {
222					self.$notice.html( '' );
223				}
224			}
225		} );
226	}
227
228	// Checkout/Cart Page
229	$.extend( WooCommerce.prototype, {
230
231		/**
232		 * Determines if is cart.
233		 *
234		 * @return {boolean} True if cartesian, False otherwise.
235		 */
236		isCart: function() {
237			return this.$cart.length > 0;
238		},
239
240		/**
241		 * Determines if current cartesian page.
242		 *
243		 * @return {boolean} True if current cartesian page, False otherwise.
244		 */
245		isCartPage: function() {
246			return $us.$body.hasClass( 'woocommerce-cart' );
247		},
248
249		/**
250		 * Determines if current checkout page.
251		 *
252		 * @return {boolean} True if current checkout page, False otherwise
253		 */
254		isCheckoutPage: function() {
255			return $us.$body.hasClass( 'woocommerce-checkout' );
256		},
257
258		/**
259		 * Update cart elements.
260		 */
261		updateCart: function() {
262			const self = this;
263			$.each( self.$cart, ( _, cart ) => {
264				var $cart = $( cart ),
265					$cartQuantity = $( '.w-cart-quantity', $cart ),
266					miniCartAmount = $( '.us_mini_cart_amount:first', $cart ).text();
267
268				if (
269					$cart.hasClass( 'opened' )
270					&& ! $cart.hasClass( 'drop_on_click' )
271					&& ! $cart.hasClass( 'layout_left_panel' )
272					&& ! $cart.hasClass( 'layout_right_panel' )
273				) {
274					$cart.removeClass( 'opened' );
275				}
276
277				if ( miniCartAmount !== _undefined ) {
278					miniCartAmount = String( miniCartAmount ).match( /\d+/g );
279					$cartQuantity.html( miniCartAmount > 0 ? miniCartAmount : '0' );
280					$cart[ miniCartAmount > 0 ? 'removeClass' : 'addClass' ]( 'empty' );
281				} else {
282					// fallback in case our action wasn't fired somehow
283					var total = 0;
284					$( '.quantity', $cart ).each( ( _, quantity ) => {
285						var matches = String( quantity.innerText ).match( /\d+/g );
286
287						if ( matches ) {
288							total += parseInt( matches[0], 10 );
289						}
290					} );
291					$cartQuantity.html( total > 0 ? total : '0' );
292					$cart[ total > 0 ? 'removeClass' : 'addClass' ]( 'empty' );
293				}
294			} );
295		},
296
297		/**
298		 * Show notifications after adding product to cart.
299		 *
300		 * @event handler
301		 * @param {Event} e
302		 * @param {{} fragments
303		 * @param {node} $button
304		 */
305		showNotification: function( e, fragments, _, $button ) {
306			if ( $ush.isUndefined( e ) ) {
307				return;
308			}
309			const self = this;
310
311			// Update cart element
312			self.updateCart();
313
314			const $notice = self.$cartNotification;
315			// NOTE: Get product name in product page or popup when available.
316			const theProductName = $button
317				.closest( '.product, .w-popup-box-content' )
318				.find( '.woocommerce-loop-product__title, .w-post-elm.post_title' )
319				.first()
320				.text();
321
322			$( '.product-name', $notice ).html( `"${theProductName}"` );
323
324			// Skip show message
325			if ( $notice.hasClass( 'skip_message' ) ) {
326				$notice.removeClass( 'skip_message' );
327				return;
328			}
329
330			$notice.addClass( 'shown' );
331			$notice.on( 'mouseenter', () => $notice.removeClass( 'shown' ) );
332
333			$ush.timeout( () => {
334				$notice
335					.removeClass( 'shown' )
336					.off( 'mouseenter' );
337			}, 3000 );
338		},
339
340		/**
341		 * Handler when remove a item.
342		 *
343		 * @event handler
344		 * @param {Event} e
345		 */
346		removeCartItem: function( e ) {
347			var $item = $( e.target )
348				.closest( '.cart_item' )
349				.addClass( 'change_process' );
350			// If the element is the last, then delete the table for correct operation `cart.js:update_wc_div`
351			if ( ! $item.siblings( '.cart_item:not(.change_process)' ).length ) {
352				$( '.w-cart-table', $us.$canvas ).remove();
353			}
354		},
355
356		/**
357		 * Check and set quantity.
358		 *
359		 * @event handler
360		 * @param {Event} e
361		 */
362		changeCartQuantity: function( e ) {
363
364			if ( $us.usbPreview() ) {
365				return;
366			}
367
368			const self = this;
369
370			const $input = $( e.target );
371			const isGroupTable = $input.closest( '.cart' ).hasClass( 'grouped_form' );
372			const max = $ush.parseInt( $input.attr( 'max' ) ) || -1;
373			const min = $ush.parseInt( $input.attr( 'min' ) ) || ( isGroupTable ? 0 : 1 );
374
375			var value = $ush.parseInt( $input.val() );
376
377			if ( $input.is( ':disabled' ) ) {
378				return;
379			}
380			if ( min >= value ) {
381				value = min;
382			}
383			if ( max > 1 && value >= max ) {
384				value = max;
385			}
386			if ( value != $input.val() ) {
387				$input.val( value );
388			}
389
390			$input
391				.siblings( 'input.plus:first' )
392				.prop( 'disabled', ( max > 0 && value >= max ) );
393			$input
394				.siblings( 'input.minus:first' )
395				.prop( 'disabled', ( value <= min ) );
396
397			// If the event type is `initControls` then this is the
398			// first init when loading the document
399			if ( e.type == 'initControls' ) {
400				return;
401			}
402
403			// Add a flag that there was a change in the quantity to the cart elements
404			$( 'input[name=us_cart_quantity]', $us.$canvas ).val( true );
405
406			if ( self.isCartPage() ) {
407				self.restApiUpdateCartForm( $input, value );
408			}
409		},
410
411		/**
412		 * Update cart form (cart table, cart totals) via REST API
413		 *
414		 * If the REST API is allowed, it will be used
415		 * Otherwise the "update cart" button click will be used (updateCartForm)
416		 */
417		restApiUpdateCartForm: function( $input, value ) {
418			const self = this;
419
420			const productKey = $input.closest( '.product-quantity' ).data( 'product-key' );
421			const $cartItem = $input.closest( '.woocommerce-cart-form__cart-item.cart_item' );
422			const $cartTotals = $( '.w-cart-totals:visible', $us.$canvas ); // Define it here because cart totals may be overridden by other requests
423
424			// Abort previous cart update request
425			if ( typeof self.lastRestRequest?.abort === 'function' ) {
426				self.lastRestRequest.abort();
427			}
428
429			// Block cart totals and show spinner in WooCommerce way
430			if ( typeof $.fn.block === 'function' && $cartTotals.length ) {
431				$cartTotals.addClass( 'processing' ).block(
432					{
433						message: null,
434						overlayCSS: {
435							opacity: 0.6,
436						},
437					}
438				);
439			}
440
441			// Request to store API cart update
442			self.lastRestRequest = $.ajax( {
443				url: `${self.restVars.siteUrl}/wp-json/wc/store/v1/cart/update-item`,
444				method: 'POST',
445				dataType: 'json',
446				data: {
447					key: productKey,
448					quantity: value,
449				},
450				headers: {
451					'Nonce': self.restVars.nonce,
452				},
453				success: function( data ) {
454
455					// Find updated item in response
456					const updatedItem = data.items?.find( ( item ) => ( item.key === productKey ) );
457
458					// Update item subtotal using formatPrice
459					if ( updatedItem && updatedItem.totals ) {
460						const subtotalData = {
461							price: updatedItem.totals.line_subtotal || '',
462							precision: updatedItem.totals.currency_minor_unit || 2,
463							currency_code: updatedItem.totals.currency_code || '',
464							currency_symbol: updatedItem.totals.currency_symbol || '',
465							currency_prefix: updatedItem.totals.currency_prefix || '',
466							currency_suffix: updatedItem.totals.currency_suffix || ''
467						};
468						$cartItem.find( '.product-subtotal bdi' ).html( self.formatPrice( subtotalData ) );
469					}
470
471					// Update cart totals section
472					if ( data.totals ) {
473						const subtotalData = {
474							price: data.totals.total_items || '',
475							precision: data.totals.currency_minor_unit || 2,
476							currency_code: data.totals.currency_code || '',
477							currency_symbol: data.totals.currency_symbol || '',
478							currency_prefix: data.totals.currency_prefix || '',
479							currency_suffix: data.totals.currency_suffix || ''
480						};
481
482						if ( $cartTotals.length ) {
483							// Update subtotal price
484							$( '.cart-subtotal bdi', $cartTotals ).html( self.formatPrice( subtotalData ) );
485
486							// Update total price
487							const totalData = { ...subtotalData, price: data.totals.total_price };
488							$( '.order-total bdi', $cartTotals ).html( self.formatPrice( totalData ) );
489
490							// Update coupon total discount
491							const discountData = { ...subtotalData, price: data.totals.total_discount };
492							$( '.cart-discount .woocommerce-Price-amount', $cartTotals ).html( self.formatPrice( discountData ) );
493
494							$us.$body.trigger( 'updated_cart_totals' );
495						}
496					}
497
498					// Trigger WooCommerce updated event for compatibility
499					$us.$body.trigger( 'updated_wc_div' );
500				},
501				error: function() {
502					// Fallback to traditional form update on error (if REST API is blocked)
503					if ( ! $( '.w-cart-table', $us.$canvas ).hasClass( 'processing' ) ) {
504						self.updateCartForm_long( self.updateCartForm.bind( self ) );
505					} else {
506						self.updateCartForm();
507					}
508				},
509				complete: function( _, textStatus) {
510					// Unblock cart totals
511					if ( typeof $.fn.unblock === 'function' && textStatus === 'success' && $cartTotals.length ) {
512						$cartTotals.removeClass( 'processing' ).unblock();
513					}
514				}
515			} );
516		},
517
518		/**
519		 * Format price with priceData from REST API response
520		 * Analogous to "formatted-monetary-amount" func from WooCommerce blocks
521		 *
522		 * Variables: https://developer.woocommerce.com/docs/apis/store-api/extending-store-api/extend-store-api-formatters/#currencyformatter
523		 *
524		 * @param priceData - object containing price data to adjust format
525		*/
526		formatPrice: function( priceData ) {
527			if ( ! priceData.price ) {
528				return '';
529			}
530
531			const numericPrice = $ush.parseFloat( priceData.price ) / Math.pow( 10, priceData.precision );
532
533			// Format only the numeric part with locale aware separators
534			const formattedNumber = numericPrice.toLocaleString(
535				document.documentElement.lang,
536				{
537					minimumFractionDigits: priceData.precision,
538					maximumFractionDigits: priceData.precision,
539					useGrouping: true
540				}
541			);
542
543			var prefix = priceData.currency_prefix,
544				suffix = priceData.currency_suffix;
545
546			// Wrap currency symbol in WooCommerce compatible span
547			if ( priceData.currency_symbol ) {
548				if ( prefix.indexOf( priceData.currency_symbol ) !== -1 ) {
549					prefix = prefix.split( priceData.currency_symbol ).join(
550						`<span class="woocommerce-Price-currencySymbol">${priceData.currency_symbol}</span>`
551					);
552				}
553				if ( suffix.indexOf( priceData.currency_symbol ) !== -1 ) {
554					suffix = suffix.split( priceData.currency_symbol ).join(
555						`<span class="woocommerce-Price-currencySymbol">${priceData.currency_symbol}</span>`
556					);
557				}
558			}
559
560			return prefix + formattedNumber + suffix;
561		},
562
563		/**
564		 * Decreasing quantity item in cart.
565		 *
566		 * @event handler
567		 * @param {Event} e
568		 */
569		minusCartQuantity: function( e ) {
570			const self = this;
571
572			var $target = $( e.target ),
573				$input = $target.siblings( 'input.qty:first' );
574
575			if ( ! $input.length ) {
576				return;
577			}
578
579			const step = $ush.parseInt( $input.attr( 'step' ) || 1 );
580			$input // Update quantity
581				.val( $ush.parseInt( $input.val() ) - step )
582				.trigger( 'change' );
583		},
584
585		/**
586		 * Increasing quantity item in cart.
587		 *
588		 * @event handler
589		 * @param {Event} e
590		 */
591		plusCartQuantity: function( e ) {
592			const self = this;
593
594			var $target = $( e.target ),
595				$input = $target.siblings( 'input.qty:first' );
596
597			if ( ! $input.length ) {
598				return;
599			}
600
601			const step = $ush.parseInt( $input.attr( 'step' ) || 1 );
602			$input
603				.val( $ush.parseInt( $input.val() ) + step )
604				.trigger( 'change' );
605		},
606
607		/**
608		 * Update the cart form by means of WooCommerce
609		 * Note: The code is moved to a separate function since `debounced`
610		 * must be initialized before calling
611		 *
612		 * @param {function} fn The function to be executed
613		 * @type debounced
614		 */
615		updateCartForm_long: $ush.debounce( $ush.fn, /* wait */50 ),
616
617		/**
618		 * Update the cart form by means of WooCommerce.
619		 */
620		updateCartForm: function() {
621			const self = this;
622			// Abort previous cart update request
623			if ( typeof ( lastUpdateCartRequest || {} ).abort === 'function' ) {
624				lastUpdateCartRequest.abort();
625			}
626			// Initialize cart update
627			$( '.w-cart-table > button[name=update_cart]', $us.$canvas )
628				.removeAttr( 'disabled' )
629				.trigger( 'click' );
630		},
631
632		/**
633		 * Update cart totals.
634		 *
635		 * @event handler
636		 */
637		updatedCartTotals: function() {
638			const self = this;
639			// Reset last active request
640			if ( lastUpdateCartRequest ) {
641				lastUpdateCartRequest = _undefined;
642			}
643			// Removing animated class if any element had it
644			var wooElementClasses = [
645				'w-cart-shipping',
646				'w-cart-table',
647				'w-cart-totals',
648				'w-checkout-billing',
649				'w-checkout-order-review',
650				'w-checkout-payment',
651				'w-wc-coupon-form',
652			];
653			for ( const i in wooElementClasses ) {
654				$( `.${wooElementClasses[i]}.us_animate_this`, $us.$canvas ).removeClass( 'us_animate_this' );
655			}
656
657			// Shipping element sync after totals update
658			const $shipping = $( '.w-cart-shipping .shipping', $us.$canvas );
659			if ( ! $shipping.length ) {
660				return;
661			}
662			$shipping.html( $( '.w-cart-totals .shipping:first', $us.$canvas ).html() );
663		},
664
665		/**
666		 * Enter the coupon code in the field.
667		 *
668		 * @event handler
669		 * @param {Event} e
670		 */
671		couponCodeChange: function( e ) {
672			// Transit value to the cart form to add a coupon by WooCommerce logic
673			$( '.w-cart-table, form.checkout_coupon:first', $us.$canvas )
674				.find( 'input[name=coupon_code]' )
675				.val( e.target.value );
676		},
677
678		/**
679		 * Enter a coupon code.
680		 *
681		 * @event handler
682		 * @param {Event} e
683		 */
684		enterCouponCode: function( e ) {
685			if ( e.keyCode === $ush.ENTER_KEYCODE ) {
686				$( e.target )
687					.trigger( 'change' )
688					.siblings( 'button:first' )
689					.trigger( 'click' );
690			}
691		},
692
693		/**
694		 * Click on the "Apply Coupon" button.
695		 *
696		 * @event handler
697		 * @param {Event} e
698		 */
699		applyCouponCode: function( e ) {
700			// Stop event (Important on the checkout page)
701			e.stopPropagation();
702			e.preventDefault();
703			// Initialize coupon additions using WooCommerce logic
704			$( '.w-cart-table, form.checkout_coupon:first', $us.$canvas )
705				.find( 'button[name=apply_coupon]' )
706				.trigger( 'click' );
707			// Clear input field
708			$( e.target ).closest( '.w-wc-coupon-form' ).find( 'input:first' ).val( '' );
709		},
710
711		/**
712		 * Coupon form display switch.
713		 *
714		 * @param {Event} e
715		 */
716		couponDisplaySwitch: function( e ) {
717			const $coupon = $( '.w-wc-coupon-form', $us.$canvas );
718			if ( ! $coupon.length ) {
719				return;
720			}
721			// Add a class if the coupon is applied
722			if ( e.type.indexOf( 'applied_coupon' ) > -1 && ! $( '.woocommerce-error', $us.$canvas ).length ) {
723				$coupon.addClass( 'coupon_applied' );
724			}
725			// Remove a class if all coupons were removed
726			if ( e.type.indexOf( 'removed_coupon' ) > -1 && $( '.woocommerce-remove-coupon', $us.$canvas ).length <= 1 ) {
727				$coupon.removeClass( 'coupon_applied' );
728			}
729		},
730
731		/**
732		 * Move notifications to `[wc_notices...]`.
733		 *
734		 * @event handler
735		 * @param {Event} e
736		 * @param {String} err_html The value is needed for checkout_error.
737		 */
738		moveNotifications: function( e, err_html ) {
739			const self = this;
740
741			// Do not proceed with notices adjustment if there are no US Cart / Checkout elements on the page
742			if ( ! self.$notice.length ) {
743				var $cartTotals = $( '.w-cart-totals', $us.$canvas ),
744					$checkoutPayment = $( '.w-checkout-payment', $us.$canvas );
745				if ( ! $cartTotals.length && ! $checkoutPayment.length ) {
746					return;
747				}
748			}
749
750			// Get notice
751			var $message;
752			if ( e.type === 'checkout_error' && err_html ) {
753				$message = $( err_html );
754			} else {
755				$message = $( '.woocommerce-error, .woocommerce-message', $us.$canvas );
756			}
757
758			// Show notification
759			if ( $message.length > 0 ) {
760				self.$notice.html( $message.clone() );
761			}
762			$message.remove();
763
764			// Remove NoticeGroup
765			if ( e.type === 'checkout_error' ) {
766				$( '.woocommerce-NoticeGroup-checkout' ).remove();
767			}
768		},
769	} );
770
771	// Cart element
772	$.extend( WooCommerce.prototype, {
773
774		/**
775		 * "Add to cart" via Ajax.
776		 *
777		 * @event handler
778		 * @param {Event} e
779		 */
780		ajaxAddToCart: function( e ) {
781			const self = this;
782
783			const $button = $( e.currentTarget );
784			const $form = $button.closest( 'form.cart' );
785
786			/**
787			 * Exclude specific product types from ajax as they needed additional processing
788			 * Excluded product types:
789			 * - External/Affiliate
790			 * - Grouped
791			 * - Composite
792			 */
793			if (
794				$form.is( '[method=get]' )
795				|| self.$pageContent.hasClass( 'product-type-grouped' )
796				|| self.$pageContent.hasClass( 'product-type-composite' )
797				|| $form.hasClass( 'grouped_form' )
798				|| $form.hasClass( 'composite_form' )
799			) {
800				return;
801			}
802
803			e.preventDefault();
804
805			// Add text wrapper and preloader
806			if ( $( '.g-preloader', $button ).length === 0 ) {
807				$button.html( `<div class="g-preloader type_1"><div></div></div><span class="w-btn-label">${$button.html()}</span>` );
808			}
809
810			var data = {};
811
812			// Serialize Form
813			const formData = new FormData( $form[0], $button[0] );
814
815			formData.forEach( ( value, key ) => {
816				if ( key.includes( '[' ) ) {
817					const keys = key.split( /\[|\]/ ).filter( ( k ) => { return k; } );
818					keys.forEach( ( k, index ) => {
819						if ( index === keys.length -1 ) {
820							if ( Array.isArray( data[ k ] ) ) {
821								data[ k ].push( value );
822							} else if ( data[ k ] ) {
823								data[ k ] = [ data[ k ], value ];
824							} else {
825								data[ k ] = key.includes( '[]' ) ? [ value ] : value; // кey includes [] without a k, ie: field[].
826							}
827						} else {
828							if ( ! data[ k ] ) {
829								data[ k ] = {};
830							}
831							data = data[ k ];
832						}
833					} );
834				} else {
835					if ( data[ key ] ) {
836						if ( Array.isArray( data[ key ] ) )  {
837							data[ key ].push( value );
838						} else {
839							data[ key ] = [ data[ key ], value ];
840						}
841					} else {
842						data[ key ] = value;
843					}
844				}
845			} );
846
847			// Check if product_id exists, if not add it with the value of add-to-cart. Use variation_id for variable products.
848			if ( data['variation_id'] ) {
849				data['product_id'] = data['variation_id'];
850			} else if ( ! data['product_id'] && data['add-to-cart'] ) {
851				data['product_id'] = data['add-to-cart'];
852			}
853
854			delete data['add-to-cart']; // need to remove this so that the form handler doesn't try to add the product to the cart again.
855
856			// Events of `../plugins/woocommerce/assets/js/frontend/add-to-cart.js`
857			$us.$body.trigger( 'adding_to_cart', $button, data );
858
859			$.ajax( {
860				type: 'POST',
861				url: String( woocommerce_params.wc_ajax_url ).replace( '%%endpoint%%', 'add_to_cart' ),
862				data: data,
863				beforeSend: () => {
864					$button
865						.removeClass( 'added' )
866						.addClass( 'loading' );
867				},
868				complete: ( jqXHR ) => {
869					// Redirect to the cart page after successful addition
870					if ( String( jqXHR.responseText ).includes( 'us_redirect_to_cart' ) ) {
871						return;
872					}
873					$button
874						.addClass( 'added' )
875						.removeClass( 'loading' );
876				},
877				success: ( res ) => {
878					if ( res.error && res.product_url ) {
879						window.location = res.product_url;
880						return;
881					}
882
883					// Redirect to the cart page after successful addition
884					if ( res.fragments && res.fragments[ 'us_redirect_to_cart' ] ) {
885						window.location.replace( res.fragments[ 'us_redirect_to_cart' ] );
886						return;
887					}
888
889					// Skip default message
890					if ( self.isCart() ) {
891						self.$cartNotification.addClass( 'skip_message' );
892					}
893
894					$us.$body.trigger( 'added_to_cart', [ res.fragments, res.cart_hash, $button ] );
895
896					// Show Cart content after clicking "Add to Cart" button
897					if ( self.isCart() && self.$cart.hasClass( 'open_on_ajax' ) ) {
898						self._toggleCart( /* open */true );
899					}
900
901					var message = '';
902
903					if ( self.isCart() ) {
904						message = self.$cartNotification.text();
905					}
906
907					// Get "View cart"
908					var $viewCart = $button.next( '.added_to_cart.wc-forward' ).removeClass( 'added_to_cart' );
909					if ( $viewCart.length ) {
910						message += ' ' + $viewCart.prop( 'outerHTML' );
911						$viewCart.remove();
912					}
913
914					// Show notice
915					$form.next( '.woocommerce-notices-wrapper' ).remove();
916					$form.after( `
917						<div class="woocommerce-notices-wrapper">
918							<div class="woocommerce-message" role="alert" tabindex="-1">${message}</div>
919						</div>
920					` );
921				},
922			} );
923		},
924
925		/**
926		 * Handler for outside click events for both mobile and desktop devices.
927		 *
928		 * @event handler
929		 * @param {Event} e
930		 */
931		clickOutsideCart: function( e ) {
932			const self = this;
933			if ( $.contains( self.$cart[0], e.target ) ) {
934				return;
935			}
936			self._toggleCart( /* open */false );
937		},
938
939		/**
940		 * Redirect to cart page on mobile devices.
941		 *
942		 * @event handler
943		 * @param {Event} e
944		 */
945		redirectToCartOnMobile: function ( e ) {
946
947			e.preventDefault();
948
949			const cartUrl = String( ( window.wc_add_to_cart_params || {} ).cart_url );
950			if ( cartUrl.includes( window.location.host ) ) {
951				window.location.replace( cartUrl );
952			}
953		},
954
955		/**
956		 * Show the cartesian on click.
957		 *
958		 * @event handler
959		 *
960		 * @param {Event} e
961		 */
962		showCartOnClick: function( e ) {
963			const self = this;
964
965			e.preventDefault();
966
967			// Enter press generates 'click' event firing two events in a row so need to distinguish these events
968			if ( [ 'mouse', 'touch', 'pen' ].includes( e.pointerType ) ) {
969				self._toggleCart( ! self.isCartOpen );
970			}
971		},
972
973		/**
974		 * Show the cartesian on keyup.
975		 *
976		 * @event handler
977		 *
978		 * @param {Event} e
979		 */
980		showCartOnKeyup: function( e ) {
981			const self = this;
982
983			e.preventDefault();
984
985			if ( e.keyCode !== $ush.ENTER_KEYCODE ) {
986				return;
987			}
988
989			if ( ! self.isCartOpen ) {
990				if ( self.$cartCloser.length > 0 ) {
991					// waiting for transition - cannot focus element with visibility:hidden
992					self.$cart.on( 'transitionend', () => {
993						self.$cartCloser[0].focus();
994						self.$cart.off( 'transitionend' );
995					} );
996				}
997			}
998
999			self._toggleCart( ! self.isCartOpen );
1000		},
1001
1002		/**
1003		 * Hide the cart on keyup.
1004		 *
1005		 * @event handler
1006		 *
1007		 * @param {Event} e
1008		 */
1009		hideCartOnKeyup: function( e ) {
1010			const self = this;
1011			if ( e.keyCode === $ush.ESC_KEYCODE ) {
1012				self._toggleCart( false, self.$cartLink );
1013			}
1014		},
1015
1016		/**
1017		 * @param {Boolean} open
1018		 * @param {*} $elementToFocus
1019		 */
1020		_toggleCart: function( open = true, $elementToFocus = null ) {
1021			const self = this;
1022
1023			self.$cart.toggleClass( 'opened', open );
1024			self.$cartLink.attr( 'aria-expanded', open );
1025
1026			if ( self.isCartOpen && $elementToFocus !== null ) {
1027				$elementToFocus.focus();
1028			}
1029
1030			self.isCartOpen = open;
1031
1032			if ( self.$cart.hasClass( 'layout_dropdown' ) && self.$cart.hasClass( 'drop_on_click' ) ) {
1033				if ( open ) {
1034					$us.$body.on( 'mouseup touchend.noPreventDefault', self._events.clickOutsideCart );
1035				} else {
1036					$us.$body.off( 'mouseup touchend.noPreventDefault', self._events.clickOutsideCart );
1037				}
1038			}
1039		}
1040	} );
1041
1042	// Login Form
1043	$.extend( WooCommerce.prototype, {
1044
1045		/**
1046		 * Show the login form.
1047		 *
1048		 * @event handler
1049		 *
1050		 * @return {Boolean}
1051		 */
1052		showLoginForm: function() {
1053			$( '.woocommerce-form-login' ).toggleClass( 'hidden' );
1054			return false;
1055		},
1056
1057		/**
1058		 * Submit the login form.
1059		 *
1060		 * @event handler
1061		 *
1062		 * @return {Boolean}
1063		 */
1064		submitLoginForm: function() {
1065			const self = this;
1066			// Prevent double sending
1067			if ( self.isSubmittingLoginForm ) {
1068				return false;
1069			}
1070			self.isSubmittingLoginForm = true;
1071
1072			// Get the form substitute view and all its fields
1073			var $formView = $( '.w-checkout-login' ),
1074				$usernameField = $( '#us_checkout_login_username', $formView ),
1075				$passwordField = $( '#us_checkout_login_password', $formView ),
1076				$redirectField = $( '#us_checkout_login_redirect', $formView ),
1077				$nonceField = $( '#us_checkout_login_nonce', $formView );
1078
1079			// Make sure all fields are present
1080			if (
1081				$usernameField.length == 0
1082				|| $passwordField.length == 0
1083				|| $redirectField.length == 0
1084				|| $nonceField.length == 0
1085			) {
1086				return false;
1087			}
1088
1089			// Append a new form with needed fields to <body> and submit it
1090			var fields = {
1091					'login': 'Login',
1092					'rememberme': 'forever',
1093					'username': $usernameField.val(),
1094					'password': $passwordField.val(),
1095					'redirect': $redirectField.val(),
1096					'woocommerce-login-nonce': $nonceField.val(),
1097				},
1098				$form = $( '<form>', {
1099					method: 'post'
1100				} );
1101			$.each( fields, ( key, val ) => {
1102				$( '<input>' ).attr( {
1103					type: "hidden",
1104					name: key,
1105					value: val
1106				} ).appendTo( $form );
1107			} );
1108
1109			$form.appendTo( 'body' ).submit();
1110
1111			return false;
1112		},
1113
1114		/**
1115		 * @event handler
1116		 * @param {Event} e
1117		 */
1118		loginFieldKeydown: function( e ) {
1119			if ( e.keyCode === $ush.ENTER_KEYCODE ) {
1120				e.stopPropagation();
1121				e.preventDefault();
1122				this.submitLoginForm();
1123			}
1124		},
1125	} );
1126
1127	$us.woocommerce = new WooCommerce;
1128
1129	/**
1130	 * Image Slider element
1131	 * Set product images for the chosen variation.
1132	 * Note: Overriding a default function implemented in WooCommerce logic.
1133	 * https://github.com/woocommerce/woocommerce/blob/d4696f043710131d5bbf51455e070791eaa12cf9/plugins/woocommerce/client/legacy/js/frontend/add-to-cart-variation.js#L646
1134	 *
1135	 * @param {{}} variation The variation.
1136	 */
1137	function us_wc_variations_image_update( variation ) {
1138		var $slider = $( '.w-slider.for_product_image_gallery:not(.w-grid .w-slider)', $( this ).closest( '.product' ) ),
1139			royalSlider = ( $slider.data( 'usImageSlider' ) || {} ).royalSlider;
1140		if ( $ush.isUndefined( royalSlider ) ) {
1141			return;
1142		}
1143		royalSlider.goTo(0);
1144		var $image = $( '.rsImg', royalSlider.slidesJQ[0] ),
1145			$thumb = $( '.rsThumb:first img', $slider );
1146		if ( variation === false ) {
1147			if ( ! $slider.data( 'orig-img' ) ) {
1148				var src = $image.attr( 'src' );
1149				$slider.data( 'orig-img', {
1150					src: src,
1151					srcset: src,
1152					full_src: src,
1153					thumb_src: $thumb.attr( 'srcset' ),
1154					gallery_thumbnail_src: $thumb.attr( 'src' ),
1155				} );
1156				return;
1157			}
1158			variation = {
1159				image: $slider.data( 'orig-img' ),
1160			};
1161		}
1162		if ( $.isPlainObject( variation.image ) ) {
1163			$image
1164				.attr( 'src', $ush.toString( variation.image.src ) )
1165				.attr( 'srcset', $ush.toString( variation.image.srcset ) );
1166			$thumb
1167				.attr( 'src', $ush.toString( variation.image.gallery_thumbnail_src ) )
1168				.attr( 'srcset', $ush.toString( variation.image.thumb_src ) );
1169			// Set bigImage for Fullscreen
1170			$.extend( royalSlider.currSlide, {
1171				bigImage: $ush.toString( variation.image.full_src ),
1172				image: $ush.toString( variation.image.src ),
1173			} );
1174			if ( typeof royalSlider.updateSliderSize === 'function' ) {
1175				royalSlider.updateSliderSize( true );
1176			}
1177		}
1178	}
1179	$( () => {
1180		if ( $( '.w-slider.for_product_image_gallery:not(.w-grid .w-slider.for_product_image_gallery)' ).length > 0 ) {
1181			$ush.timeout( () => {
1182				$.fn.wc_variations_image_update = us_wc_variations_image_update;
1183			}, 1 );
1184		}
1185	} );
1186
1187	/**
1188	 * Extension for WC Product Gallery
1189	 * Thumbs Arrows functionality
1190	 */
1191	function usProductGallerySlider( $wcSlider ) {
1192		const self = this;
1193
1194		// Elements
1195		self.$wcSlider = $( $wcSlider );
1196		self.$thumbsNav = $( '.flex-control-thumbs', self.$wcSlider );
1197		self.$thumbsItems = $( 'li', self.$thumbsNav );
1198		self.$thumbsArrowsContainer = self.$wcSlider
1199			.closest( '.w-post-elm.product_gallery' )
1200			.find( '.us-thumbs-nav' );
1201		self.$thumbArrowNext = $( '.us-thumb-next', self.$thumbsArrowsContainer );
1202		self.$thumbArrowPrev = $( '.us-thumb-prev', self.$thumbsArrowsContainer );
1203
1204		// Vars
1205		self._isVertical = self.isVertical();
1206		self.scrollProp = self._isVertical ? 'scrollTop' : 'scrollLeft';
1207		const sizeProp = self._isVertical ? 'outerHeight' : 'outerWidth';
1208		self.clientSizeProp = self._isVertical ? 'clientHeight' : 'clientWidth';
1209		self.scrollSizeProp = self._isVertical ? 'scrollHeight' : 'scrollWidth';
1210		self.thumbItemSize = self.$thumbsItems.first()[ sizeProp ]( true );
1211
1212		// Bindable events
1213		self._events = {
1214			updateThumbsArrowsState: self.updateThumbsArrowsState.bind( self ),
1215			controlThumbsArrows: self.controlThumbsArrows.bind( self ),
1216			syncThumbsWithMainSlider: self.syncThumbsWithMainSlider.bind( self ),
1217		};
1218
1219		// Main Image Arrows
1220		if ( self.$wcSlider.hasClass( 'with_arrows' ) ) {
1221			// Change arrows position
1222			$( '.flex-direction-nav', self.$wcSlider ).appendTo( $( '.flex-viewport', self.$wcSlider ) );
1223
1224			// Events
1225			$( '.flex-direction-nav a', self.$wcSlider ).on( 'click', self._events.syncThumbsWithMainSlider );
1226		}
1227
1228		// Wrap Product Gallery thumbs and move arrows to it
1229		self.$thumbsNav.wrap( '<div class="us-thumbs-wrapper"></div>' );
1230		self.$thumbsArrowsContainer.appendTo( self.$thumbsNav.parent() );
1231
1232		// Init thumb arrows state
1233		self.updateThumbsArrowsState();
1234
1235		// Events
1236		$( 'button', self.$thumbsArrowsContainer ).on( 'click', self._events.controlThumbsArrows );
1237
1238		self.$thumbsNav.on( 'scroll.noPreventDefault', self._events.updateThumbsArrowsState );
1239
1240		// Sync thumbs scroll position with main slider on variation change
1241		$( 'form.variations_form' ).on( 'found_variation reset_data', () => {
1242			$ush.timeout( () => self.syncThumbsWithMainSlider(), 100 );
1243		} );
1244	}
1245
1246	$.extend( usProductGallerySlider.prototype, {
1247		/**
1248		 * Scroll thumbs on thumb arrows click
1249		 *
1250		 * @param {Event} e
1251		 */
1252		controlThumbsArrows: function ( e ) {
1253			e.preventDefault();
1254			const self = this;
1255			const scrollObj = {};
1256			const isNext = $( e.currentTarget ).hasClass( 'us-thumb-next' );
1257
1258			var direction;
1259
1260			if ( $ush.isRtl() && ! self._isVertical ) {
1261				direction = isNext ? '-=' : '+=';
1262			} else {
1263				direction = isNext ? '+=' : '-=';
1264			}
1265			scrollObj[ self.scrollProp ] = direction + self.thumbItemSize;
1266			self.$thumbsNav.animate( scrollObj, 50, self._events.updateThumbsArrowsState );
1267		},
1268		/**
1269		 * Update thumbs arrows state depending on scroll position
1270		 */
1271		updateThumbsArrowsState: function () {
1272			const self = this;
1273
1274			if ( ! self.$thumbsNav[0] ) {
1275				return;
1276			}
1277
1278			const scrollPos = self.$thumbsNav[0][ self.scrollProp ];
1279			const maxScroll = self.$thumbsNav[0][ self.scrollSizeProp ] - self.$thumbsNav[0][ self.clientSizeProp ];
1280
1281			var isPrevDisabled, isNextDisabled;
1282
1283			if ( $ush.isRtl() && ! self._isVertical ) {
1284				isPrevDisabled = scrollPos >= -1;
1285				isNextDisabled = scrollPos <= -maxScroll + 1;
1286			} else {
1287				isPrevDisabled = scrollPos <= 1;
1288				isNextDisabled = scrollPos >= maxScroll - 1;
1289			}
1290
1291			self.$thumbArrowPrev.toggleClass( 'hidden', isPrevDisabled );
1292			self.$thumbArrowNext.toggleClass( 'hidden', isNextDisabled );
1293		},
1294		/**
1295		 * Sync thumbs scroll position with main slider
1296		 */
1297		syncThumbsWithMainSlider: function() {
1298			const self = this;
1299			const $activeSlide = $( '.woocommerce-product-gallery__wrapper .flex-active-slide', self.$wcSlider );
1300			const scrollObj = {};
1301
1302			if ( ! $activeSlide.length || ! self.$thumbsItems.length ) {
1303				return;
1304			}
1305
1306			const index = $activeSlide.index();
1307
1308			if ( index < 0 || index >= self.$thumbsItems.length ) {
1309				return;
1310			}
1311
1312			var targetScroll = index * self.thumbItemSize;
1313			if ( $ush.isRtl() && ! self._isVertical ) {
1314				targetScroll = -targetScroll;
1315			}
1316
1317			scrollObj[ self.scrollProp ] = targetScroll;
1318
1319			self.$thumbsNav.stop().animate( scrollObj, 50, self._events.updateThumbsArrowsState );
1320		},
1321		/**
1322		 * Get isVertical flag
1323		 */
1324		isVertical: function() {
1325			const self = this;
1326
1327			var isVertical = self.$wcSlider.hasClass( 'thumbpos_left' );
1328
1329			// Responsive for thumbpos_left
1330			if ( window.matchMedia( '(max-width: 767px)' ).matches ) {
1331				self.$wcSlider.removeClass( 'thumbpos_left' ).addClass( 'thumbpos_bottom' );
1332				isVertical = false;
1333			}
1334
1335			return isVertical;
1336		}
1337	} );
1338
1339	$.fn.us_product_gallery = function() {
1340		return this.each( function() {
1341			$( this ).data( 'us_product_gallery', new usProductGallerySlider( this ) );
1342		} );
1343	};
1344
1345	$( () => {
1346		const $wcSlider = $( '.woocommerce-product-gallery.type_slider' );
1347		if ( $wcSlider.length ) {
1348			$ush.timeout( () => $wcSlider.us_product_gallery(), 200 );
1349		}
1350	} );
1351
1352	// Init the "Product Gallery" inside the popup content in loop elements.
1353	$us.$document.on( 'usPopup.itemContentLoaded', ( e, usPopup ) => {
1354		// Functions for single product.
1355		// Dependency on `../plugins/woocommerce/assets/js/frontend/single-product.js`
1356		if ( typeof $.fn.wc_product_gallery === 'function' ) {
1357			const $wcProductGallery = $( '.woocommerce-product-gallery', usPopup.$content );
1358			$wcProductGallery.wc_product_gallery();
1359			if ( $wcProductGallery.hasClass( 'type_slider' ) ) {
1360				$ush.timeout( () => $wcProductGallery.us_product_gallery(), 200 );
1361			}
1362		}
1363		// Functions for which handles variation forms and attributes
1364		// Dependency on `../plugins/woocommerce/assets/js/frontend/add-to-cart-variations.js`
1365		if ( typeof $.fn.wc_variation_form === 'function' ) {
1366			const $variationsForm = $( '.variations_form', usPopup.$content );
1367			if ( $variationsForm.length > 0 ) {
1368				if ( $( '.product', usPopup.$content ).length == 0 ) {
1369					usPopup.$content.addClass( 'product' );
1370				}
1371				$variationsForm.wc_variation_form();
1372			}
1373		}
1374	} );
1375
1376} )( jQuery );

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.