1/* global wc_cart_params */ 2jQuery( function( $ ) { 3 4 // wc_cart_params is required to continue, ensure the object exists 5 if ( typeof wc_cart_params === 'undefined' ) { 6 return false; 7 } 8 9 // Utility functions for the file. 10 11 /** 12 * Gets a url for a given AJAX endpoint. 13 * 14 * @param {String} endpoint The AJAX Endpoint 15 * @return {String} The URL to use for the request 16 */ 17 var get_url = function( endpoint ) { 18 return wc_cart_params.wc_ajax_url.toString().replace( 19 '%%endpoint%%', 20 endpoint 21 ); 22 }; 23 24 /** 25 * Check if a node is blocked for processing. 26 * 27 * @param {JQuery Object} $node 28 * @return {bool} True if the DOM Element is UI Blocked, false if not. 29 */ 30 var is_blocked = function( $node ) { 31 return $node.is( '.processing' ) || $node.parents( '.processing' ).length; 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 = []; 67 var new_notices = notices; 68 69 notices.each( function( index ) { 70 var text = $( this ).text(); 71 72 if ( 'undefined' === typeof seen[ text ] ) { 73 seen[ text ] = true; 74 } else { 75 new_notices.splice( index, 1 ); 76 } 77 } ); 78 79 return new_notices; 80 }; 81 82 /** 83 * Update the .woocommerce div with a string of html. 84 * 85 * @param {String} html_str The HTML string with which to replace the div. 86 * @param {bool} preserve_notices Should notices be kept? False by default. 87 */ 88 var update_wc_div = function( html_str, preserve_notices ) { 89 var $html = $.parseHTML( html_str ); 90 var $new_form = $( '.woocommerce-cart-form', $html ); 91 var $new_totals = $( '.cart_totals', $html ); 92 var $notices = remove_duplicate_notices( $( '.woocommerce-error, .woocommerce-message, .woocommerce-info', $html ) ); 93 94 // No form, cannot do this. 95 if ( $( '.woocommerce-cart-form' ).length === 0 ) { 96 window.location.reload(); 97 return; 98 } 99 100 // Remove errors 101 if ( ! preserve_notices ) { 102 $( '.woocommerce-error, .woocommerce-message, .woocommerce-info' ).remove(); 103 } 104 105 if ( $new_form.length === 0 ) { 106 // If the checkout is also displayed on this page, trigger reload instead. 107 if ( $( '.woocommerce-checkout' ).length ) { 108 window.location.reload(); 109 return; 110 } 111 112 // No items to display now! Replace all cart content. 113 var $cart_html = $( '.cart-empty', $html ).closest( '.woocommerce' ); 114 $( '.woocommerce-cart-form__contents' ).closest( '.woocommerce' ).replaceWith( $cart_html ); 115 116 // Display errors 117 if ( $notices.length > 0 ) { 118 show_notice( $notices ); 119 } 120 121 // Notify plugins that the cart was emptied. 122 $( document.body ).trigger( 'wc_cart_emptied' ); 123 } else { 124 // If the checkout is also displayed on this page, trigger update event. 125 if ( $( '.woocommerce-checkout' ).length ) { 126 $( document.body ).trigger( 'update_checkout' ); 127 } 128 129 $( '.woocommerce-cart-form' ).replaceWith( $new_form ); 130 $( '.woocommerce-cart-form' ).find( ':input[name="update_cart"]' ).prop( 'disabled', true ).attr( 'aria-disabled', true ); 131 132 if ( $notices.length > 0 ) { 133 show_notice( $notices ); 134 } 135 136 update_cart_totals_div( $new_totals ); 137 } 138 139 $( document.body ).trigger( 'updated_wc_div' ); 140 }; 141 142 /** 143 * Update the .cart_totals div with a string of html. 144 * 145 * @param {String} html_str The HTML string with which to replace the div. 146 */ 147 var update_cart_totals_div = function( html_str ) { 148 $( '.cart_totals' ).replaceWith( html_str ); 149 $( document.body ).trigger( 'updated_cart_totals' ); 150 }; 151 152 /** 153 * Shows new notices on the page. 154 * 155 * @param {Object} The Notice HTML Element in string or object form. 156 */ 157 var show_notice = function( html_element, $target ) { 158 if ( ! $target ) { 159 $target = $( '.woocommerce-notices-wrapper:first' ) || 160 $( '.cart-empty' ).closest( '.woocommerce' ) || 161 $( '.woocommerce-cart-form' ); 162 } 163 $target.prepend( html_element ); 164 }; 165 166 167 /** 168 * Object to handle AJAX calls for cart shipping changes. 169 */ 170 var cart_shipping = { 171 172 /** 173 * Initialize event handlers and UI state. 174 */ 175 init: function( cart ) { 176 this.cart = cart; 177 this.toggle_shipping = this.toggle_shipping.bind( this ); 178 this.shipping_method_selected = this.shipping_method_selected.bind( this ); 179 this.shipping_calculator_submit = this.shipping_calculator_submit.bind( this ); 180 181 $( document ).on( 182 'click', 183 '.shipping-calculator-button', 184 this.toggle_shipping 185 ); 186 $( document ).on( 187 'change', 188 'select.shipping_method, :input[name^=shipping_method]', 189 this.shipping_method_selected 190 ); 191 $( document ).on( 192 'submit', 193 'form.woocommerce-shipping-calculator', 194 this.shipping_calculator_submit 195 ); 196 197 $( '.shipping-calculator-form' ).hide(); 198 }, 199 200 /** 201 * Toggle Shipping Calculator panel 202 */ 203 toggle_shipping: function() { 204 $( '.shipping-calculator-form' ).slideToggle( 'slow' ); 205 $( 'select.country_to_state, input.country_to_state' ).trigger( 'change' ); 206 $( document.body ).trigger( 'country_to_state_changed' ); // Trigger select2 to load. 207 return false;
208 }, 209 210 /** 211 * Handles when a shipping method is selected. 212 */ 213 shipping_method_selected: function() { 214 var shipping_methods = {}; 215 216 // eslint-disable-next-line max-len 217 $( 'select.shipping_method, :input[name^=shipping_method][type=radio]:checked, :input[name^=shipping_method][type=hidden]' ).each( function() { 218 shipping_methods[ $( this ).data( 'index' ) ] = $( this ).val(); 219 } ); 220 221 block( $( 'div.cart_totals' ) ); 222 223 var data = { 224 security: wc_cart_params.update_shipping_method_nonce, 225 shipping_method: shipping_methods 226 }; 227 228 $.ajax( { 229 type: 'post', 230 url: get_url( 'update_shipping_method' ), 231 data: data, 232 dataType: 'html', 233 success: function( response ) { 234 update_cart_totals_div( response ); 235 }, 236 complete: function() { 237 unblock( $( 'div.cart_totals' ) ); 238 $( document.body ).trigger( 'updated_shipping_method' ); 239 } 240 } ); 241 }, 242 243 /** 244 * Handles a shipping calculator form submit. 245 * 246 * @param {Object} evt The JQuery event. 247 */ 248 shipping_calculator_submit: function( evt ) { 249 evt.preventDefault(); 250 251 var $form = $( evt.currentTarget ); 252 253 block( $( 'div.cart_totals' ) ); 254 block( $form ); 255 256 // Provide the submit button value because wc-form-handler expects it. 257 $( '<input />' ).attr( 'type', 'hidden' ) 258 .attr( 'name', 'calc_shipping' ) 259 .attr( 'value', 'x' ) 260 .appendTo( $form ); 261 262 // Make call to actual form post URL. 263 $.ajax( { 264 type: $form.attr( 'method' ), 265 url: $form.attr( 'action' ), 266 data: $form.serialize(), 267 dataType: 'html', 268 success: function( response ) { 269 update_wc_div( response ); 270 }, 271 complete: function() { 272 unblock( $form ); 273 unblock( $( 'div.cart_totals' ) ); 274 } 275 } ); 276 } 277 }; 278 279 /** 280 * Object to handle cart UI. 281 */ 282 var cart = { 283 /** 284 * Initialize cart UI events. 285 */ 286 init: function() { 287 this.update_cart_totals = this.update_cart_totals.bind( this ); 288 this.input_keypress = this.input_keypress.bind( this ); 289 this.cart_submit = this.cart_submit.bind( this ); 290 this.submit_click = this.submit_click.bind( this ); 291 this.apply_coupon = this.apply_coupon.bind( this ); 292 this.remove_coupon_clicked = this.remove_coupon_clicked.bind( this ); 293 this.quantity_update = this.quantity_update.bind( this ); 294 this.item_remove_clicked = this.item_remove_clicked.bind( this ); 295 this.item_restore_clicked = this.item_restore_clicked.bind( this ); 296 this.update_cart = this.update_cart.bind( this ); 297 298 $( document ).on( 299 'wc_update_cart added_to_cart', 300 function() { cart.update_cart.apply( cart, [].slice.call( arguments, 1 ) ); } ); 301 $( document ).on( 302 'click', 303 '.woocommerce-cart-form :input[type=submit]', 304 this.submit_click ); 305 $( document ).on( 306 'keypress', 307 '.woocommerce-cart-form :input[type=number]', 308 this.input_keypress ); 309 $( document ).on( 310 'submit', 311 '.woocommerce-cart-form', 312 this.cart_submit ); 313 $( document ).on( 314 'click', 315 'a.woocommerce-remove-coupon', 316 this.remove_coupon_clicked ); 317 $( document ).on( 318 'click', 319 '.woocommerce-cart-form .product-remove > a', 320 this.item_remove_clicked ); 321 $( document ).on( 322 'click', 323 '.woocommerce-cart .restore-item', 324 this.item_restore_clicked ); 325 $( document ).on( 326 'change input', 327 '.woocommerce-cart-form .cart_item :input', 328 this.input_changed ); 329 330 $( '.woocommerce-cart-form :input[name="update_cart"]' ).prop( 'disabled', true ).attr( 'aria-disabled', true ); 331 }, 332 333 /** 334 * After an input is changed, enable the update cart button. 335 */ 336 input_changed: function() { 337 $( '.woocommerce-cart-form :input[name="update_cart"]' ).prop( 'disabled', false ).attr( 'aria-disabled', false ); 338 }, 339 340 /** 341 * Update entire cart via ajax. 342 */ 343 update_cart: function( preserve_notices ) { 344 var $form = $( '.woocommerce-cart-form' ); 345 346 block( $form ); 347 block( $( 'div.cart_totals' ) ); 348 349 // Make call to actual form post URL. 350 $.ajax( { 351 type: $form.attr( 'method' ), 352 url: $form.attr( 'action' ), 353 data: $form.serialize(), 354 dataType: 'html', 355 success: function( response ) { 356 update_wc_div( response, preserve_notices ); 357 }, 358 complete: function() { 359 unblock( $form ); 360 unblock( $( 'div.cart_totals' ) ); 361 $.scroll_to_notices( $( '[role="alert"]' ) ); 362 } 363 } ); 364 }, 365 366 /** 367 * Update the cart after something has changed. 368 */ 369 update_cart_totals: function() { 370 block( $( 'div.cart_totals' ) ); 371 372 $.ajax( { 373 url: get_url( 'get_cart_totals' ), 374 dataType: 'html', 375 success: function( response ) { 376 update_cart_totals_div( response ); 377 }, 378 complete: function() { 379 unblock( $( 'div.cart_totals' ) ); 380 } 381 } ); 382 }, 383 384 /** 385 * Handle the <ENTER> key for quantity fields. 386 * 387 * @param {Object} evt The JQuery event 388 * 389 * For IE, if you hit enter on a quantity field, it makes the 390 * document.activeElement the first submit button it finds. 391 * For us, that is the Apply Coupon button. This is required 392 * to catch the event before that happens. 393 */ 394 input_keypress: function( evt ) { 395 396 // Catch the enter key and don't let it submit the form. 397 if ( 13 === evt.keyCode ) { 398 var $form = $( evt.currentTarget ).parents( 'form' ); 399 400 try { 401 // If there are no validation errors, handle the submit. 402 if ( $form[0].checkValidity() ) { 403 evt.preventDefault(); 404 this.cart_submit( evt ); 405 } 406 } catch( err ) { 407 evt.preventDefault(); 408 this.cart_submit( evt ); 409 } 410 } 411 }, 412 413 /** 414 * Handle cart form submit and route to correct logic. 415 * 416 * @param {Object} evt The JQuery event 417 */ 418 cart_submit: function( evt ) { 419 var $submit = $( document.activeElement ), 420 $clicked = $( ':input[type=submit][clicked=true]' ), 421 $form = $( evt.currentTarget ); 422 423 // For submit events, currentTarget is form.
424 // For keypress events, currentTarget is input. 425 if ( ! $form.is( 'form' ) ) { 426 $form = $( evt.currentTarget ).parents( 'form' ); 427 } 428 429 if ( 0 === $form.find( '.woocommerce-cart-form__contents' ).length ) { 430 return; 431 } 432 433 if ( is_blocked( $form ) ) { 434 return false; 435 } 436 437 if ( $clicked.is( ':input[name="update_cart"]' ) || $submit.is( 'input.qty' ) ) { 438 evt.preventDefault(); 439 this.quantity_update( $form ); 440 441 } else if ( $clicked.is( ':input[name="apply_coupon"]' ) || $submit.is( '#coupon_code' ) ) { 442 evt.preventDefault(); 443 this.apply_coupon( $form ); 444 } 445 }, 446 447 /** 448 * Special handling to identify which submit button was clicked. 449 * 450 * @param {Object} evt The JQuery event 451 */ 452 submit_click: function( evt ) { 453 $( ':input[type=submit]', $( evt.target ).parents( 'form' ) ).removeAttr( 'clicked' ); 454 $( evt.target ).attr( 'clicked', 'true' ); 455 }, 456 457 /** 458 * Apply Coupon code 459 * 460 * @param {JQuery Object} $form The cart form. 461 */ 462 apply_coupon: function( $form ) { 463 block( $form ); 464 465 var cart = this; 466 var $text_field = $( '#coupon_code' ); 467 var coupon_code = $text_field.val(); 468 469 var data = { 470 security: wc_cart_params.apply_coupon_nonce, 471 coupon_code: coupon_code 472 }; 473 474 $.ajax( { 475 type: 'POST', 476 url: get_url( 'apply_coupon' ), 477 data: data, 478 dataType: 'html', 479 success: function( response ) { 480 $( '.woocommerce-error, .woocommerce-message, .woocommerce-info' ).remove(); 481 show_notice( response ); 482 $( document.body ).trigger( 'applied_coupon', [ coupon_code ] ); 483 }, 484 complete: function() { 485 unblock( $form ); 486 $text_field.val( '' ); 487 cart.update_cart( true ); 488 } 489 } ); 490 }, 491 492 /** 493 * Handle when a remove coupon link is clicked. 494 * 495 * @param {Object} evt The JQuery event 496 */ 497 remove_coupon_clicked: function( evt ) { 498 evt.preventDefault(); 499 500 var cart = this; 501 var $wrapper = $( evt.currentTarget ).closest( '.cart_totals' ); 502 var coupon = $( evt.currentTarget ).attr( 'data-coupon' ); 503 504 block( $wrapper ); 505 506 var data = { 507 security: wc_cart_params.remove_coupon_nonce, 508 coupon: coupon 509 }; 510 511 $.ajax( { 512 type: 'POST', 513 url: get_url( 'remove_coupon' ), 514 data: data, 515 dataType: 'html', 516 success: function( response ) { 517 $( '.woocommerce-error, .woocommerce-message, .woocommerce-info' ).remove(); 518 show_notice( response ); 519 $( document.body ).trigger( 'removed_coupon', [ coupon ] ); 520 unblock( $wrapper ); 521 }, 522 complete: function() { 523 cart.update_cart( true ); 524 } 525 } ); 526 }, 527 528 /** 529 * Handle a cart Quantity Update 530 * 531 * @param {JQuery Object} $form The cart form. 532 */ 533 quantity_update: function( $form ) { 534 block( $form ); 535 block( $( 'div.cart_totals' ) ); 536 537 // Provide the submit button value because wc-form-handler expects it. 538 $( '<input />' ).attr( 'type', 'hidden' ) 539 .attr( 'name', 'update_cart' ) 540 .attr( 'value', 'Update Cart' ) 541 .appendTo( $form ); 542 543 // Make call to actual form post URL. 544 $.ajax( { 545 type: $form.attr( 'method' ), 546 url: $form.attr( 'action' ), 547 data: $form.serialize(), 548 dataType: 'html', 549 success: function( response ) { 550 update_wc_div( response ); 551 }, 552 complete: function() { 553 unblock( $form ); 554 unblock( $( 'div.cart_totals' ) ); 555 $.scroll_to_notices( $( '[role="alert"]' ) ); 556 } 557 } ); 558 }, 559 560 /** 561 * Handle when a remove item link is clicked. 562 * 563 * @param {Object} evt The JQuery event 564 */ 565 item_remove_clicked: function( evt ) { 566 evt.preventDefault(); 567 568 var $a = $( evt.currentTarget ); 569 var $form = $a.parents( 'form' ); 570 571 block( $form ); 572 block( $( 'div.cart_totals' ) ); 573 574 $.ajax( { 575 type: 'GET', 576 url: $a.attr( 'href' ), 577 dataType: 'html', 578 success: function( response ) { 579 update_wc_div( response ); 580 }, 581 complete: function() { 582 unblock( $form ); 583 unblock( $( 'div.cart_totals' ) ); 584 $.scroll_to_notices( $( '[role="alert"]' ) ); 585 } 586 } ); 587 }, 588 589 /** 590 * Handle when a restore item link is clicked. 591 * 592 * @param {Object} evt The JQuery event 593 */ 594 item_restore_clicked: function( evt ) { 595 evt.preventDefault(); 596 597 var $a = $( evt.currentTarget ); 598 var $form = $( 'form.woocommerce-cart-form' ); 599 600 block( $form ); 601 block( $( 'div.cart_totals' ) ); 602 603 $.ajax( { 604 type: 'GET', 605 url: $a.attr( 'href' ), 606 dataType: 'html', 607 success: function( response ) { 608 update_wc_div( response ); 609 }, 610 complete: function() { 611 unblock( $form ); 612 unblock( $( 'div.cart_totals' ) ); 613 } 614 } ); 615 } 616 }; 617 618 cart_shipping.init( cart ); 619 cart.init(); 620} );
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.