PageSourceSearch

https://360branded.com/wp-content/plugins/woocommerce/assets/js/frontend/cart.js?ver=11.1.2

js 360branded.com collected 2026-10-01 16:02:52 UTC 20,580 bytes, 813 lines download raw bytes

1/* global wc_cart_params */
2jQuery( function ( $ ) {
3	// wc_cart_params is required to continue, ensure the object exists
4	if ( typeof wc_cart_params === 'undefined' ) {
5		return false;
6	}
7
8	// Utility functions for the file.
9
10	/**
11	 * Gets a url for a given AJAX endpoint.
12	 *
13	 * @param {String} endpoint The AJAX Endpoint
14	 * @return {String} The URL to use for the request
15	 */
16	var get_url = function ( endpoint ) {
17		return wc_cart_params.wc_ajax_url
18			.toString()
19			.replace( '%%endpoint%%', endpoint );
20	};
21
22	/**
23	 * Check if a node is blocked for processing.
24	 *
25	 * @param {JQuery Object} $node
26	 * @return {bool} True if the DOM Element is UI Blocked, false if not.
27	 */
28	var is_blocked = function ( $node ) {
29		return (
30			$node.is( '.processing' ) || $node.parents( '.processing' ).length
31		);
32	};
33
34	/**
35	 * Block a node visually for processing.
36	 *
37	 * @param {JQuery Object} $node
38	 */
39	var block = function ( $node ) {
40		if ( ! is_blocked( $node ) ) {
41			$node.addClass( 'processing' ).block( {
42				message: null,
43				overlayCSS: {
44					background: '#fff',
45					opacity: 0.6,
46				},
47			} );
48		}
49	};
50
51	/**
52	 * Unblock a node after processing is complete.
53	 *
54	 * @param {JQuery Object} $node
55	 */
56	var unblock = function ( $node ) {
57		$node.removeClass( 'processing' ).unblock();
58	};
59
60	/**
61	 * Removes duplicate notices.
62	 *
63	 * @param {JQuery Object} $notices
64	 */
65	var remove_duplicate_notices = function ( $notices ) {
66		var seen = new Set();
67		var deduplicated_notices = [];
68
69		$notices.each( function () {
70			const text = $( this ).text();
71
72			if ( ! seen.has( text ) ) {
73				seen.add( text );
74				deduplicated_notices.push( this );
75			}
76		} );
77
78		return $( deduplicated_notices );
79	};
80
81	/**
82	 * Update the .woocommerce div with a string of html.
83	 *
84	 * @param {String} html_str The HTML string with which to replace the div.
85	 * @param {bool} preserve_notices Should notices be kept? False by default.
86	 */
87	var update_wc_div = function ( html_str, preserve_notices ) {
88		var $html = $.parseHTML( html_str );
89		var $new_form = $( '.woocommerce-cart-form', $html );
90		var $new_totals = $( '.cart_totals', $html );
91		var $notices = remove_duplicate_notices(
92			$(
93				'.woocommerce-error, .woocommerce-message, .woocommerce-info, .is-error, .is-info, .is-success',
94				$html
95			)
96		);
97
98		// No form, cannot do this.
99		if ( $( '.woocommerce-cart-form' ).length === 0 ) {
100			window.location.reload();
101			return;
102		}
103
104		// Remove errors
105		if ( ! preserve_notices ) {
106			$(
107				'.woocommerce-error, .woocommerce-message, .woocommerce-info, .is-error, .is-info, .is-success, .coupon-error-notice'
108			).remove();
109		}
110
111		if ( $new_form.length === 0 ) {
112			// If the checkout is also displayed on this page, trigger reload instead.
113			if ( $( '.woocommerce-checkout' ).length ) {
114				window.location.reload();
115				return;
116			}
117
118			// No items to display now! Replace all cart content.
119			var $cart_html = $( '.wc-empty-cart-message', $html ).closest(
120				'.woocommerce'
121			);
122			$( '.woocommerce-cart-form__contents' )
123				.closest( '.woocommerce' )
124				.replaceWith( $cart_html );
125
126			// Display errors
127			if ( $notices.length > 0 ) {
128				show_notice( $notices );
129			}
130
131			// Notify plugins that the cart was emptied.
132			$( document.body ).trigger( 'wc_cart_emptied' );
133		} else {
134			// If the checkout is also displayed on this page, trigger update event.
135			if ( $( '.woocommerce-checkout' ).length ) {
136				$( document.body ).trigger( 'update_checkout' );
137			}
138
139			// Store the old coupon error message and value before the
140			// .woocommerce-cart-form is replaced with the new form.
141			var $old_coupon_field_val = $( '#coupon_code' ).val();
142			var $old_coupon_error_msg = $( '#coupon_code' )
143					.closest( '.coupon' )
144					.find( '.coupon-error-notice' );
145
146			$( '.woocommerce-cart-form' ).replaceWith( $new_form );
147			$( '.woocommerce-cart-form' )
148				.find( ':input[name="update_cart"]' )
149				.prop( 'disabled', true );
150
151			if ( preserve_notices && $old_coupon_error_msg.length > 0 ) {
152				var $new_coupon_field = $( '.woocommerce-cart-form' ).find( '#coupon_code' );
153				var $new_coupon_field_wrapper = $new_coupon_field.closest( '.coupon' );
154
155				$new_coupon_field.val( $old_coupon_field_val );
156				// The coupon input with error needs to be focused before adding the live region
157				// with the error message, otherwise the screen reader won't read it.
158				$new_coupon_field.trigger( 'focus' );
159				show_coupon_error( $old_coupon_error_msg, $new_coupon_field_wrapper, true );
160			}
161
162			if ( $notices.length > 0 ) {
163				show_notice( $notices );
164			}
165
166			update_cart_totals_div( $new_totals );
167		}
168
169		$( document.body ).trigger( 'updated_wc_div' );
170	};
171
172	/**
173	 * Update the .cart_totals div with a string of html.
174	 *
175	 * @param {String} html_str The HTML string with which to replace the div.
176	 */
177	var update_cart_totals_div = function ( html_str ) {
178		$( '.cart_totals' ).replaceWith( html_str );
179		$( document.body ).trigger( 'updated_cart_totals' );
180	};
181
182	/**
183	 * Shows new notices on the page.
184	 *
185	 * @param {Object} The Notice HTML Element in string or object form.
186	 */
187	var show_notice = function ( html_element, $target ) {
188		if ( ! $target ) {
189			$target =
190				$( '.woocommerce-notices-wrapper:first' ) ||
191				$( '.wc-empty-cart-message' ).closest( '.woocommerce' ) ||
192				$( '.woocommerce-cart-form' );
193		}
194		$target.prepend( html_element );
195	};
196
197	/**
198	 * Shows coupon form errors.
199	 *
200	 * @param {string|object}
200 html_element The HTML string response after applying an invalid coupon or a jQuery element.
201	 * @param {Object} $target Coupon field wrapper jQuery element.
202	 * @param {boolean} is_live_region Whether role="alert" should be added or not.
203	 */
204	var show_coupon_error = function ( html_element, $target, is_live_region ) {
205		if ( $target.length === 0 ) {
206			return;
207		}
208
209		var $coupon_error_el = html_element;
210
211		if ( typeof html_element === 'string' ) {
212			var msg = $( $.parseHTML( html_element ) ).text().trim();
213
214			if ( msg === '' ) {
215				return;
216			}
217
218			$coupon_error_el = $('<p>', {
219				class: 'coupon-error-notice',
220				id: 'coupon-error-notice',
221				text: msg
222			});
223		}
224
225		if ( is_live_region ) {
226			$coupon_error_el.attr( 'role', 'alert' );
227		}
228
229		$target.find( '#coupon_code' )
230			.addClass( 'has-error' )
231			.attr( 'aria-invalid', 'true' )
232			.attr( 'aria-describedby', 'coupon-error-notice' );
233		$target.append( $coupon_error_el );
234	};
235
236	/**
237	 * Handle when pressing the Space key on a link with the button role.
238	 *
239	 * @param {Object} event The jQuery event.
240	 */
241	var on_keydown_button = function ( event ) {
242		if ( event.key === ' ' ) {
243			event.preventDefault();
244
245			if ( ! event.originalEvent || ! event.originalEvent.repeat ) {
246				$( event.currentTarget ).trigger( 'click' );
247			}
248		}
249	};
250
251	/**
252	 * Object to handle AJAX calls for cart shipping changes.
253	 */
254	var cart_shipping = {
255		/**
256		 * Initialize event handlers and UI state.
257		 */
258		init: function ( cart ) {
259			this.cart = cart;
260			this.toggle_shipping = this.toggle_shipping.bind( this );
261			this.shipping_method_selected =
262				this.shipping_method_selected.bind( this );
263			this.shipping_calculator_submit =
264				this.shipping_calculator_submit.bind( this );
265
266			$( document ).on(
267				'click',
268				'.shipping-calculator-button',
269				this.toggle_shipping
270			);
271			$( document ).on(
272				'keydown',
273				'.shipping-calculator-button',
274				on_keydown_button
275			);
276			$( document ).on(
277				'change',
278				'select.shipping_method, :input[name^=shipping_method]',
279				this.shipping_method_selected
280			);
281			$( document ).on(
282				'submit',
283				'form.woocommerce-shipping-calculator',
284				this.shipping_calculator_submit
285			);
286
287			$( '.shipping-calculator-form' ).hide();
288		},
289
290		/**
291		 * Toggle Shipping Calculator panel
292		 */
293		toggle_shipping: function ( event ) {
294			var $target = $( event.currentTarget );
295
296			$( '.shipping-calculator-form' ).slideToggle( 'slow', function () {
297				var self = this;
298
299				setTimeout( function () {
300					var $form = $( self );
301
302					$target.attr( 'aria-expanded', $form.is( ':visible' ) ? 'true' : 'false' );
303				}, 0 );
304			} );
305
306			$( 'select.country_to_state, input.country_to_state' ).trigger(
307				'change'
308			);
309			$( document.body ).trigger( 'country_to_state_changed' ); // Trigger select2 to load.
310			return false;
311		},
312
313		/**
314		 * Handles when a shipping method is selected.
315		 */
316		shipping_method_selected: function ( event ) {
317			var shipping_methods = {};
318
319			$(
320				'select.shipping_method, :input[name^=shipping_method][type=radio]:checked, :input[name^=shipping_method][type=hidden]'
321			).each( function () {
322				shipping_methods[ $( this ).data( 'index' ) ] = $( this ).val();
323			} );
324
325			block( $( 'div.cart_totals' ) );
326
327			var data = {
328				security: wc_cart_params.update_shipping_method_nonce,
329				shipping_method: shipping_methods,
330			};
331
332			$.ajax( {
333				type: 'post',
334				url: get_url( 'update_shipping_method' ),
335				data: data,
336				dataType: 'html',
337				success: function ( response ) {
338					update_cart_totals_div( response );
339
340					var newCurrentTarget = document.getElementById( event.currentTarget.id );
341
342					if ( newCurrentTarget ) {
343						newCurrentTarget.focus();
344					}
345				},
346				complete: function () {
347					unblock( $( 'div.cart_totals' ) );
348					$( document.body ).trigger( 'updated_shipping_method' );
349				},
350			} );
351		},
352
353		/**
354		 * Handles a shipping calculator form submit.
355		 *
356		 * @param {Object} evt The JQuery event.
357		 */
358		shipping_calculator_submit: function ( evt ) {
359			evt.preventDefault();
360
361			var $form = $( evt.currentTarget );
362
363			block( $( 'div.cart_totals' ) );
364			block( $form );
365
366			// Provide the submit button value because wc-form-handler expects it.
367			$( '<input />' )
368				.attr( 'type', 'hidden' )
369				.attr( 'name', 'calc_shipping' )
370				.attr( 'value', 'x' )
371				.appendTo( $form );
372
373			// Make call to actual form post URL.
374			$.ajax( {
375				type: $form.attr( 'method' ),
376				url: $form.attr( 'action' ),
377				data: $form.serialize(),
378				dataType: 'html',
379				success: function ( response ) {
380					update_wc_div( response );
381				},
382				complete: function () {
383					unblock( $form );
384					unblock( $( 'div.cart_totals' ) );
385				},
386			} );
387		},
388	};
389
390	/**
391	 * Object to handle cart UI.
392	 */
393	var cart = {
394		/**
395		 * Initialize cart UI events.
396		 */
397		init: function () {
398			this.update_cart_totals = this.update_cart_totals.bind( this );
399			this.input_keypress = this.input_keypress.bind( this );
400			this.cart_submit = this.cart_submit.bind( this );
401			this.submit_click = this.submit_click.bind( this );
402			this.apply_coupon = this.apply_coupon.bind( this );
403			this.remove_coupon_clicked =
404				this.remove_coupon_clicked.bind( this );
405			this.remove_coupon_error = this.remove_coupon_error.bind( this );
406			this.quantity_update = this.quantity_update.bind( this );
407			this.item_remove_clicked = this.item_remove_clicked.bind( this );
408			this.item_restore_clicked = this.item_restore_clicked.bind( this );
409			this.update_cart = this.update_cart.bind( this );
410
411			$( document ).on( 'wc_update_cart added_to_cart', function () {
412				cart.update_cart.apply( cart, [].slice.call( arguments, 1 ) );
413			} );
414			$( document ).on(
415				'click',
416				'.woocommerce-cart-form :input[type=submit]',
417				this.submit_click
418			);
419			$( document ).on(
420				'keypress',
421				'.woocommerce-cart-form :input[type=number]',
422				this.input_keypress
423			);
424			$( document ).on(
425				'submit',
426				'.woocommerce-cart-form',
427				this.cart_submit
428			);
429			$( document ).on(
430				'click',
431				'a.woocommerce-remove-coupon',
432				this.remove_coupon_clicked
433			);
434			$( document ).on(
435				'keydown',
436				'a.woocommerce-remove-coupon',
437				on_keydown_button
438			);
439			$( document ).on(
440				'click',
441				'.woocommerce-cart-form .product-remove > a',
442				this.item_remove_clicked
443			);
444			$( document ).on(
445				'keydown',
446				'.woocommerce-cart-form .product-remove > a',
447				on_keydown_button
448			);
449			$( document ).on(
450				'click',
451				'.woocommerce-cart .restore-item',
452				this.item_restore_clicked
453			);
454			$( document ).on(
455				'change input',
456				'.woocommerce-cart-form .cart_item :input',
457				this.input_changed
458			);
459			$( document ).on(
460				'change input',
461				'#coupon_code',
462				this.remove_coupon_error
463			);
464
465			$( '.woocommerce-cart-form :input[name="update_cart"]' ).prop(
466				'disabled',
467				true
468			);
469		},
470
471		/**
472		 * After an input is changed, enable the update cart button.
473		 */
474		input_changed: function () {
475			$( '.woocommerce-cart-form :input[name="update_cart"]' ).prop(
476				'disabled',
477				false
478			);
479		},
480
481		/**
482		 * Update entire cart via ajax.
483		 */
484		update_cart: function ( preserve_notices ) {
485			var $form = $( '.woocommerce-cart-form' );
486
487			block( $form );
488			block( $( 'div.cart_totals' ) );
489
490			// Make call to actual form post URL.
491			$.ajax( {
492				type: $form.attr( 'method' ),
493				url: $form.attr( 'action' ),
494				data: $form.serialize(),
495				dataType: 'html',
496				success: function ( response ) {
497					update_wc_div( response, preserve_notices );
498				},
499				complete: function () {
500					unblock( $form );
501					unblock( $( 'div.cart_totals' ) );
502					$.scroll_to_notices( $( '[role="alert"]' ) );
503				},
504			} );
505		},
506
507		/**
508		 * Update the cart after something has changed.
509		 */
510		update_cart_totals: function () {
511			block( $( 'div.cart_totals' ) );
512
513			$.ajax( {
514				url: get_url( 'get_cart_totals' ),
515				dataType: 'html',
516				success: function ( response ) {
517					update_cart_totals_div( response );
518				},
519				complete: function () {
520					unblock( $( 'div.cart_totals' ) );
521				},
522			} );
523		},
524
525		/**
526		 * Handle the <ENTER> key for quantity fields.
527		 *
528		 * @param {Object} evt The JQuery event
529		 *
530		 * For IE, if you hit enter on a quantity field, it makes the
531		 * document.activeElement the first submit button it finds.
532		 * For us, that is the Apply Coupon button. This is required
533		 * to catch the event before that happens.
534		 */
535		input_keypress: function ( evt ) {
536			// Catch the enter key and don't let it submit the form.
537			if ( 13 === evt.keyCode ) {
538				var $form = $( evt.currentTarget ).parents( 'form' );
539
540				try {
541					// If there are no validation errors, handle the submit.
542					if ( $form[ 0 ].checkValidity() ) {
543						evt.preventDefault();
544						this.cart_submit( evt );
545					}
546				} catch ( err ) {
547					evt.preventDefault();
548					this.cart_submit( evt );
549				}
550			}
551		},
552
553		/**
554		 * Handle cart form submit and route to correct logic.
555		 *
556		 * @param {Object} evt The JQuery event
557		 */
558		cart_submit: function ( evt ) {
559			var $submit = $( document.activeElement ),
560				$clicked = $( ':input[type=submit][clicked=true]' ),
561				$form = $( evt.currentTarget );
562
563			// For submit events, currentTarget is form.
564			// For keypress events, currentTarget is input.
565			if ( ! $form.is( 'form' ) ) {
566				$form = $( evt.currentTarget ).parents( 'form' );
567			}
568
569			if (
570				0 === $form.find( '.woocommerce-cart-form__contents' ).length
571			) {
572				return;
573			}
574
575			if ( is_blocked( $form ) ) {
576				return false;
577			}
578
579			if (
580				$clicked.is( ':input[name="update_cart"]' ) ||
581				$submit.is( 'input.qty' )
582			) {
583				evt.preventDefault();
584				this.quantity_update( $form );
585			} else if (
586				$clicked.is( ':input[name="apply_coupon"]' ) ||
587				$submit.is( '#coupon_code' )
588			) {
589				evt.preventDefault();
590				this.apply_coupon( $form );
591			}
592		},
593
594		/**
595		 * Special handling to identify which submit button was clicked.
596		 *
597		 * @param {Object} evt The JQuery event
598		 */
599		submit_click: function ( evt ) {
600			$(
601				':input[type=submit]',
602				$( evt.target ).parents( 'form' )
603			).removeAttr( 'clicked' );
604			$( evt.target ).attr( 'clicked', 'true' );
605		},
606
607		/**
608		 * Apply Coupon code
609		 *
610		 * @param {JQuery Object} $form The cart form.
611		 */
612		apply_coupon: function ( $form ) {
613			block( $form );
614
615			var cart = this;
616			var $text_field = $( '#coupon_code' );
617			var coupon_code = $text_field.val();
618
619			var data = {
620				security: wc_cart_params.apply_coupon_nonce,
621				coupon_code: coupon_code,
622			};
623
624			$.ajax( {
625				type: 'POST',
626				url: get_url( 'apply_coupon' ),
627				data: data,
628				dataType: 'html',
629				success: function ( response ) {
630					$(
631						'.woocommerce-error, .woocommerce-message, .woocommerce-info, ' +
632						'.is-error, .is-info, .is-success, .coupon-error-notice'
633					).remove();
634
635					// We only want to show coupon notices if they are not errors.
636					// Coupon errors are shown under the input.
637					if ( response.indexOf( 'woocommerce-error' ) === -1 && response.indexOf( 'is-error' ) === -1 ) {
638						show_notice( response );
639					} else {
640						var $coupon_wrapper = $text_field.closest( '.coupon' );
641
642						if ( $coupon_wrapper.length > 0 ) {
643							show_coupon_error( response, $coupon_wrapper, false );
644						}
645					}
646
647					$( document.body ).trigger( 'applied_coupon', [
648						coupon_code,
649					] );
650				},
651				complete: function () {
652					unblock( $form );
653					cart.update_cart( true );
654				},
655			} );
656		},
657
658		/**
659		 * Handle when a remove coupon link is clicked.
660		 *
661		 * @param {Object} evt The JQuery event
662		 */
663		remove_coupon_clicked: function ( evt ) {
664			evt.preventDefault();
665
666			var cart = this;
667			var $wrapper = $( evt.currentTarget ).closest( '.cart_totals' );
668			var coupon = $( evt.currentTarget ).attr( 'data-coupon' );
669
670			block( $wrapper );
671
672			var data = {
673				security: wc_cart_params.remove_coupon_nonce,
674				coupon: coupon,
675			};
676
677			$.ajax( {
678				type: 'POST',
679				url: get_url( 'remove_coupon' ),
680				data: data,
681				dataType: 'html',
682				success: function ( response ) {
683					$(
684						'.woocommerce-error, .woocommerce-message, .woocommerce-info, .is-error, .is-info, .is-success'
685					).remove();
686					show_notice( response );
687					$( document.body ).trigger( 'removed_coupon', [ coupon ] );
688					$( '#coupon_code' )
689						.val('')
690						.removeClass('has-error')
691						.removeAttr('aria-invalid')
692						.removeAttr('aria-describedby')
693						.closest('.coupon')
694						.find('.coupon-error-notice')
695						.remove();
696					unblock( $wrapper );
697				},
698				complete: function () {
699					cart.update_cart( true );
700				},
701			} );
702		},
703
704		/**
705		 * Handle when the coupon input loses focus.
706		 *
707		 * @param {Object} evt The JQuery event
708		 */
709		remove_coupon_error: function ( evt ) {
710			$( evt.currentTarget )
711				.removeClass( 'has-error' )
712				.removeAttr( 'aria-invalid' )
713				.removeAttr( 'aria-describedby' )
714				.closest( '.coupon' )
715				.find( '.coupon-error-notice' )
716				.remove();
717		},
718
719		/**
720		 * Handle a cart Quantity Update
721		 *
722		 * @param {JQuery Object} $form The cart form.
723		 */
724		quantity_update: function ( $form ) {
725			block( $form );
726			block( $( 'div.cart_totals' ) );
727
728			// Provide the submit button value because wc-form-handler expects it.
729			$( '<input />' )
730				.attr( 'type', 'hidden' )
731				.attr( 'name', 'update_cart' )
732				.attr( 'value', 'Update Cart' )
733				.appendTo( $form );
734
735			// Make call to actual form post URL.
736			$.ajax( {
737				type: $form.attr( 'method' ),
738				url: $form.attr( 'action' ),
739				data: $form.serialize(),
740				dataType: 'html',
741				success: function ( response ) {
742					update_wc_div( response );
743				},
744				complete: function () {
745					unblock( $form );
746					unblock( $( 'div.cart_totals' ) );
747					$.scroll_to_notices( $( '[role="alert"]' ) );
748				},
749			} );
750		},
751
752		/**
753		 * Handle when a remove item link is clicked.
754		 *
755		 * @param {Object} evt The JQuery event
756		 */
757		item_remove_clicked: function ( evt ) {
758			evt.preventDefault();
759
760			var $a = $( evt.currentTarget );
761			var $form = $a.parents( 'form' );
762
763			block( $form );
764			block( $( 'div.cart_totals' ) );
765
766			$.ajax( {
767				type: 'GET',
768				url: $a.attr( 'href' ),
769				dataType: 'html',
770				success: function ( response ) {
771					update_wc_div( response );
772				},
773				complete: function () {
774					unblock( $form );
775					unblock( $( 'div.cart_totals' ) );
776					$.scroll_to_notices( $( '[role="alert"]' ) );
777					$( document.body ).trigger( 'item_removed_from_classic_cart');
778				},
779			} );
780		},
781
782		/**
783		 * Handle when a restore item link is clicked.
784		 *
785		 * @param {Object} evt The JQuery event
786		 */
787		item_restore_clicked: function ( evt ) {
788			evt.preventDefault();
789
790			var $a = $( evt.currentTarget );
791			var $form = $( 'form.woocommerce-cart-form' );
792
793			block( $form );
794			block( $( 'div.cart_totals' ) );
795
796			$.ajax( {
797				type: 'GET',
798				url: $a.attr( 'href' ),
799				dataType: 'html',
800				success: function ( response ) {
801					update_wc_div( response );
802				},
803				complete: function () {
804					unblock( $form );
805					unblock( $( 'div.cart_totals' ) );
806				},
807			} );
808		},
809	};
810
811	cart_shipping.init( cart );
812	cart.init();
813} );

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.