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' 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 $( '.woocommerce-cart-form' ).replaceWith( $new_form ); 140 $( '.woocommerce-cart-form' ) 141 .find( ':input[name="update_cart"]' ) 142 .prop( 'disabled', true ); 143 144 if ( $notices.length > 0 ) { 145 show_notice( $notices ); 146 } 147 148 update_cart_totals_div( $new_totals ); 149 } 150 151 $( document.body ).trigger( 'updated_wc_div' ); 152 }; 153 154 /** 155 * Update the .cart_totals div with a string of html. 156 * 157 * @param {String} html_str The HTML string with which to replace the div. 158 */ 159 var update_cart_totals_div = function ( html_str ) { 160 $( '.cart_totals' ).replaceWith( html_str ); 161 $( document.body ).trigger( 'updated_cart_totals' ); 162 }; 163 164 /** 165 * Shows new notices on the page. 166 * 167 * @param {Object} The Notice HTML Element in string or object form. 168 */ 169 var show_notice = function ( html_element, $target ) { 170 if ( ! $target ) { 171 $target = 172 $( '.woocommerce-notices-wrapper:first' ) || 173 $( '.wc-empty-cart-message' ).closest( '.woocommerce' ) || 174 $( '.woocommerce-cart-form' ); 175 } 176 $target.prepend( html_element ); 177 }; 178 179 /** 180 * Object to handle AJAX calls for cart shipping changes. 181 */ 182 var cart_shipping = { 183 /** 184 * Initialize event handlers and UI state. 185 */ 186 init: function ( cart ) { 187 this.cart = cart; 188 this.toggle_shipping = this.toggle_shipping.bind( this ); 189 this.shipping_method_selected = 190 this.shipping_method_selected.bind( this ); 191 this.shipping_calculator_submit = 192 this.shipping_calculator_submit.bind( this ); 193 194 $( document ).on( 195 'click', 196 '.shipping-calculator-button', 197 this.toggle_shipping 198 ); 199 $( document ).on( 200 'change', 201 'select.shipping_method, :input[name^=shipping_method]', 202 this.shipping_method_selected 203 ); 204 $( document ).on( 205 'submit', 206 'form.woocommerce-shipping-calculator', 207 this.shipping_calculator_submit 208 ); 209 210 $( '.shipping-calculator-form' ).hide(); 211 }, 212 213 /** 214 * Toggle Shipping Calculator panel 215 */ 216 toggle_shipping: function () { 217 $( '.shipping-calculator-form' ).slideToggle( 'slow' ); 218 $( 'select.country_to_state, input.country_to_state' ).trigger( 219 'change' 220 ); 221 $( document.body ).trigger( 'country_to_state_changed' ); // Trigger select2 to load. 222 return false;
223 }, 224 225 /** 226 * Handles when a shipping method is selected. 227 */ 228 shipping_method_selected: function () { 229 var shipping_methods = {}; 230 231 // eslint-disable-next-line max-len 232 $( 233 'select.shipping_method, :input[name^=shipping_method][type=radio]:checked, :input[name^=shipping_method][type=hidden]' 234 ).each( function () { 235 shipping_methods[ $( this ).data( 'index' ) ] = $( this ).val(); 236 } ); 237 238 block( $( 'div.cart_totals' ) ); 239 240 var data = { 241 security: wc_cart_params.update_shipping_method_nonce, 242 shipping_method: shipping_methods, 243 }; 244 245 $.ajax( { 246 type: 'post', 247 url: get_url( 'update_shipping_method' ), 248 data: data, 249 dataType: 'html', 250 success: function ( response ) { 251 update_cart_totals_div( response ); 252 }, 253 complete: function () { 254 unblock( $( 'div.cart_totals' ) ); 255 $( document.body ).trigger( 'updated_shipping_method' ); 256 }, 257 } ); 258 }, 259 260 /** 261 * Handles a shipping calculator form submit. 262 * 263 * @param {Object} evt The JQuery event. 264 */ 265 shipping_calculator_submit: function ( evt ) { 266 evt.preventDefault(); 267 268 var $form = $( evt.currentTarget ); 269 270 block( $( 'div.cart_totals' ) ); 271 block( $form ); 272 273 // Provide the submit button value because wc-form-handler expects it. 274 $( '<input />' ) 275 .attr( 'type', 'hidden' ) 276 .attr( 'name', 'calc_shipping' ) 277 .attr( 'value', 'x' ) 278 .appendTo( $form ); 279 280 // Make call to actual form post URL. 281 $.ajax( { 282 type: $form.attr( 'method' ), 283 url: $form.attr( 'action' ), 284 data: $form.serialize(), 285 dataType: 'html', 286 success: function ( response ) { 287 update_wc_div( response ); 288 }, 289 complete: function () { 290 unblock( $form ); 291 unblock( $( 'div.cart_totals' ) ); 292 }, 293 } ); 294 }, 295 }; 296 297 /** 298 * Object to handle cart UI. 299 */ 300 var cart = { 301 /** 302 * Initialize cart UI events. 303 */ 304 init: function () { 305 this.update_cart_totals = this.update_cart_totals.bind( this ); 306 this.input_keypress = this.input_keypress.bind( this ); 307 this.cart_submit = this.cart_submit.bind( this ); 308 this.submit_click = this.submit_click.bind( this ); 309 this.apply_coupon = this.apply_coupon.bind( this ); 310 this.remove_coupon_clicked = 311 this.remove_coupon_clicked.bind( this ); 312 this.quantity_update = this.quantity_update.bind( this ); 313 this.item_remove_clicked = this.item_remove_clicked.bind( this ); 314 this.item_restore_clicked = this.item_restore_clicked.bind( this ); 315 this.update_cart = this.update_cart.bind( this ); 316 317 $( document ).on( 'wc_update_cart added_to_cart', function () { 318 cart.update_cart.apply( cart, [].slice.call( arguments, 1 ) ); 319 } ); 320 $( document ).on( 321 'click', 322 '.woocommerce-cart-form :input[type=submit]', 323 this.submit_click 324 ); 325 $( document ).on( 326 'keypress', 327 '.woocommerce-cart-form :input[type=number]', 328 this.input_keypress 329 ); 330 $( document ).on( 331 'submit', 332 '.woocommerce-cart-form', 333 this.cart_submit 334 ); 335 $( document ).on( 336 'click', 337 'a.woocommerce-remove-coupon', 338 this.remove_coupon_clicked 339 ); 340 $( document ).on( 341 'click', 342 '.woocommerce-cart-form .product-remove > a', 343 this.item_remove_clicked 344 ); 345 $( document ).on( 346 'click', 347 '.woocommerce-cart .restore-item', 348 this.item_restore_clicked 349 ); 350 $( document ).on( 351 'change input', 352 '.woocommerce-cart-form .cart_item :input', 353 this.input_changed 354 ); 355 356 $( '.woocommerce-cart-form :input[name="update_cart"]' ).prop( 357 'disabled', 358 true 359 ); 360 }, 361 362 /** 363 * After an input is changed, enable the update cart button. 364 */ 365 input_changed: function () { 366 $( '.woocommerce-cart-form :input[name="update_cart"]' ).prop( 367 'disabled', 368 false 369 ); 370 }, 371 372 /** 373 * Update entire cart via ajax. 374 */ 375 update_cart: function ( preserve_notices ) { 376 var $form = $( '.woocommerce-cart-form' ); 377 378 block( $form ); 379 block( $( 'div.cart_totals' ) ); 380 381 // Make call to actual form post URL. 382 $.ajax( { 383 type: $form.attr( 'method' ), 384 url: $form.attr( 'action' ), 385 data: $form.serialize(), 386 dataType: 'html', 387 success: function ( response ) { 388 update_wc_div( response, preserve_notices ); 389 }, 390 complete: function () { 391 unblock( $form ); 392 unblock( $( 'div.cart_totals' ) ); 393 $.scroll_to_notices( $( '[role="alert"]' ) ); 394 }, 395 } ); 396 }, 397 398 /** 399 * Update the cart after something has changed. 400 */ 401 update_cart_totals: function () { 402 block( $( 'div.cart_totals' ) ); 403 404 $.ajax( { 405 url: get_url( 'get_cart_totals' ), 406 dataType: 'html', 407 success: function ( response ) { 408 update_cart_totals_div( response ); 409 }, 410 complete: function () { 411 unblock( $( 'div.cart_totals' ) ); 412 }, 413 } ); 414 }, 415 416 /** 417 * Handle the <ENTER> key for quantity fields. 418 * 419 * @param {Object} evt The JQuery event 420 * 421 * For IE, if you hit enter on a quantity field, it makes the 422 * document.activeElement the first submit button it finds. 423 * For us, that is the Apply Coupon button. This is required 424 * to catch the event before that happens. 425 */ 426 input_keypress: function ( evt ) { 427 // Catch the enter key and don't let it submit the form. 428 if ( 13 === evt.keyCode ) { 429 var $form = $( evt.currentTarget ).parents( 'form' ); 430 431 try { 432 // If there are no validation errors, handle the submit. 433 if ( $form[ 0 ].checkValidity() ) { 434 evt.preventDefault(); 435 this.cart_submit( evt ); 436 } 437 } catch ( err ) { 438 evt.preventDefault(); 439 this.cart_submit( evt ); 440 } 441 } 442 }, 443 444 /** 445 * Handle cart form submit and route to correct logic. 446 * 447 * @param {Object} evt The JQuery event 448 */ 449 cart_submit: function ( evt ) { 450 var $submit = $( document.activeElement ), 451 $clicked = $( ':input[type=submit][clicked=true]' ), 452 $form = $( evt.currentTarget ); 453 454 // For submit events, currentTarget is form.
455 // For keypress events, currentTarget is input. 456 if ( ! $form.is( 'form' ) ) { 457 $form = $( evt.currentTarget ).parents( 'form' ); 458 } 459 460 if ( 461 0 === $form.find( '.woocommerce-cart-form__contents' ).length 462 ) { 463 return; 464 } 465 466 if ( is_blocked( $form ) ) { 467 return false; 468 } 469 470 if ( 471 $clicked.is( ':input[name="update_cart"]' ) || 472 $submit.is( 'input.qty' ) 473 ) { 474 evt.preventDefault(); 475 this.quantity_update( $form ); 476 } else if ( 477 $clicked.is( ':input[name="apply_coupon"]' ) || 478 $submit.is( '#coupon_code' ) 479 ) { 480 evt.preventDefault(); 481 this.apply_coupon( $form ); 482 } 483 }, 484 485 /** 486 * Special handling to identify which submit button was clicked. 487 * 488 * @param {Object} evt The JQuery event 489 */ 490 submit_click: function ( evt ) { 491 $( 492 ':input[type=submit]', 493 $( evt.target ).parents( 'form' ) 494 ).removeAttr( 'clicked' ); 495 $( evt.target ).attr( 'clicked', 'true' ); 496 }, 497 498 /** 499 * Apply Coupon code 500 * 501 * @param {JQuery Object} $form The cart form. 502 */ 503 apply_coupon: function ( $form ) { 504 block( $form ); 505 506 var cart = this; 507 var $text_field = $( '#coupon_code' ); 508 var coupon_code = $text_field.val(); 509 510 var data = { 511 security: wc_cart_params.apply_coupon_nonce, 512 coupon_code: coupon_code, 513 }; 514 515 $.ajax( { 516 type: 'POST', 517 url: get_url( 'apply_coupon' ), 518 data: data, 519 dataType: 'html', 520 success: function ( response ) { 521 $( 522 '.woocommerce-error, .woocommerce-message, .woocommerce-info, .is-error, .is-info, .is-success' 523 ).remove(); 524 show_notice( response ); 525 $( document.body ).trigger( 'applied_coupon', [ 526 coupon_code, 527 ] ); 528 }, 529 complete: function () { 530 unblock( $form ); 531 $text_field.val( '' ); 532 cart.update_cart( true ); 533 }, 534 } ); 535 }, 536 537 /** 538 * Handle when a remove coupon link is clicked. 539 * 540 * @param {Object} evt The JQuery event 541 */ 542 remove_coupon_clicked: function ( evt ) { 543 evt.preventDefault(); 544 545 var cart = this; 546 var $wrapper = $( evt.currentTarget ).closest( '.cart_totals' ); 547 var coupon = $( evt.currentTarget ).attr( 'data-coupon' ); 548 549 block( $wrapper ); 550 551 var data = { 552 security: wc_cart_params.remove_coupon_nonce, 553 coupon: coupon, 554 }; 555 556 $.ajax( { 557 type: 'POST', 558 url: get_url( 'remove_coupon' ), 559 data: data, 560 dataType: 'html', 561 success: function ( response ) { 562 $( 563 '.woocommerce-error, .woocommerce-message, .woocommerce-info, .is-error, .is-info, .is-success' 564 ).remove(); 565 show_notice( response ); 566 $( document.body ).trigger( 'removed_coupon', [ coupon ] ); 567 unblock( $wrapper ); 568 }, 569 complete: function () { 570 cart.update_cart( true ); 571 }, 572 } ); 573 }, 574 575 /** 576 * Handle a cart Quantity Update 577 * 578 * @param {JQuery Object} $form The cart form. 579 */ 580 quantity_update: function ( $form ) { 581 block( $form ); 582 block( $( 'div.cart_totals' ) ); 583 584 // Provide the submit button value because wc-form-handler expects it. 585 $( '<input />' ) 586 .attr( 'type', 'hidden' ) 587 .attr( 'name', 'update_cart' ) 588 .attr( 'value', 'Update Cart' ) 589 .appendTo( $form ); 590 591 // Make call to actual form post URL. 592 $.ajax( { 593 type: $form.attr( 'method' ), 594 url: $form.attr( 'action' ), 595 data: $form.serialize(), 596 dataType: 'html', 597 success: function ( response ) { 598 update_wc_div( response ); 599 }, 600 complete: function () { 601 unblock( $form ); 602 unblock( $( 'div.cart_totals' ) ); 603 $.scroll_to_notices( $( '[role="alert"]' ) ); 604 }, 605 } ); 606 }, 607 608 /** 609 * Handle when a remove item link is clicked. 610 * 611 * @param {Object} evt The JQuery event 612 */ 613 item_remove_clicked: function ( evt ) { 614 evt.preventDefault(); 615 616 var $a = $( evt.currentTarget ); 617 var $form = $a.parents( 'form' ); 618 619 block( $form ); 620 block( $( 'div.cart_totals' ) ); 621 622 $.ajax( { 623 type: 'GET', 624 url: $a.attr( 'href' ), 625 dataType: 'html', 626 success: function ( response ) { 627 update_wc_div( response ); 628 }, 629 complete: function () { 630 unblock( $form ); 631 unblock( $( 'div.cart_totals' ) ); 632 $.scroll_to_notices( $( '[role="alert"]' ) ); 633 }, 634 } ); 635 }, 636 637 /** 638 * Handle when a restore item link is clicked. 639 * 640 * @param {Object} evt The JQuery event 641 */ 642 item_restore_clicked: function ( evt ) { 643 evt.preventDefault(); 644 645 var $a = $( evt.currentTarget ); 646 var $form = $( 'form.woocommerce-cart-form' ); 647 648 block( $form ); 649 block( $( 'div.cart_totals' ) ); 650 651 $.ajax( { 652 type: 'GET', 653 url: $a.attr( 'href' ), 654 dataType: 'html', 655 success: function ( response ) { 656 update_wc_div( response ); 657 }, 658 complete: function () { 659 unblock( $form ); 660 unblock( $( 'div.cart_totals' ) ); 661 }, 662 } ); 663 }, 664 }; 665 666 cart_shipping.init( cart ); 667 cart.init(); 668} );
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.