1(function( $ ) { 2 'use strict'; 3 4 //============================================================================= 5 // GLOBAL VARIABLES 6 //============================================================================= 7 var ccWindow = $( '.cc-window' ); 8 var cc_quanity_update_send = true; 9 var expectingCartUpdate = false; 10 11 12 //============================================================================= 13 // UTILITY FUNCTIONS 14 //============================================================================= 15 16 /** 17 * Generate skeleton HTML for cart items loading state 18 * 19 * @param {number|null} customCount - Optional custom number of skeleton items to display 20 * @return {string} HTML string containing skeleton loaders 21 */ 22 function getSkeletonHTML(customCount) { 23 const cartCount = customCount || parseInt($('.cc-compass-count').text()) || 1; 24 25 const skeletonItem = ` 26 <div class="cc-skeleton-item"> 27 <div class="cc-skeleton cc-skeleton-thumb"></div> 28 <div class="cc-skeleton-content"> 29 <div class="cc-skeleton cc-skeleton-line medium"></div> 30 <div class="cc-skeleton cc-skeleton-line short"></div> 31 </div> 32 </div> 33 `; 34 35 return skeletonItem.repeat(cartCount); 36 } 37 38 /** 39 * Get WooCommerce AJAX URL for an endpoint 40 * 41 * @param {string} endpoint - The endpoint to get URL for 42 * @return {string} The complete AJAX URL 43 */ 44 function getWcAjaxUrl(endpoint) { 45 if (cc_ajax_script.wc_ajax_url) { 46 return cc_ajax_script.wc_ajax_url.replace('%%endpoint%%', endpoint); 47 } 48 return '/?wc-ajax=' + endpoint; 49 } 50 51 /** 52 * Handle common AJAX response for cart operations 53 * 54 * @param {Object} response - The AJAX response 55 */ 56 function handleCartResponse(response) { 57 if (response && response.fragments) { 58 $.each(response.fragments, function(key, value) { 59 $(key).replaceWith(value); 60 }); 61 $(document.body).trigger('wc_fragments_refreshed'); 62 } 63 } 64 65 //============================================================================= 66 // CART MANAGEMENT FUNCTIONS 67 //============================================================================= 68 69 /** 70 * Load and display the cart screen with updated fragments 71 * 72 * @param {string} productAdded - Optional parameter indicating if a product was added ('yes' or 'move_to_cart') 73 */ 74 function cc_cart_screen(productAdded = '') { 75 if (window.cc_cart_ajax && window.cc_cart_ajax.readyState !== 4) { 76 window.cc_cart_ajax.abort(); 77 } 78 79 window.cc_cart_ajax = $.ajax({ 80 type: 'POST', 81 url: cc_ajax_script.ajaxurl, 82 data: { 83 action: 'caddy_get_cart_fragments', 84 _: new Date().getTime() 85 }, 86 cache: false, 87 beforeSend: function() { 88 $('.cc-cart-items').html(getSkeletonHTML()); 89 }, 90 error: function(xhr, status, error) { 91 if (status !== 'abort') { 92 $('.cc-cart-items').html('<div class="cc-cart-error">Unable to load cart. <a href="' + cc_ajax_script.cart_url + '">View cart page</a>.</div>'); 93 } 94 }, 95 success: function(response) { 96 handleCartResponse(response); 97 98 var tabs = new Tabby('[data-tabs]'); 99 tabs.toggle('#cc-cart'); 100 101 if ('yes' == productAdded) { 102 $('.cc-window-wrapper').show(); 103 } else if ('move_to_cart' === productAdded) { 104 $('.cc_cart_from_sfl').removeClass('cc_hide_btn'); 105 $('.cc_cart_from_sfl').parent().find('.cc-loader').hide(); 106 $('.cc-coupon .woocommerce-notices-wrapper').remove(); 107 $('.cc-cart').removeAttr('hidden'); 108 } 109 } 110 }); 111 } 112 113 /** 114 * Update cart item quantity 115 * @param {string} cartKey - The cart item key 116 * @param {number} newQty - The new quantity 117 */ 118 function cc_update_cart_quantity(cartKey, newQty) { 119 $('.cc-cart-items').html(getSkeletonHTML()); 120 121 $.ajax({ 122 type: 'POST', 123 url: getWcAjaxUrl('cc_update_item_quantity'), 124 data: { 125 cart_key: cartKey, 126 qty: newQty, 127 nonce: cc_ajax_script.nonce 128 }, 129 success: handleCartResponse, 130 error: function() { 131 cc_refresh_cart(); 132 }, 133 complete: function() { 134 $('.cc-window-wrapper').show(); 135 } 136 }); 137 } 138 139 /** 140 * Update item quantity in the cart 141 * Handles quantity increment and decrement buttons 142 * 143 * @param {Object} el - The button element that was clicked 144 */ 145 function cc_quantity_update_buttons(el) { 146 var wrap = $(el).parents('.cc-cart-product-list'); 147 var input = $(wrap).find('.cc_item_quantity'); 148 var key = $(input).data('key'); 149 var number = parseInt($(input).val()); 150 var type = $(el).data('type'); 151
152 if ('minus' == type) { 153 number--; 154 } else { 155 number++; 156 } 157 158 if (number < 1) { 159 // If quantity would be less than 1, trigger remove item instead 160 var removeButton = $(el).closest('.cc-cart-product').find('a.remove_from_cart_button'); 161 removeButton.trigger('click'); 162 return; 163 } 164 165 cc_update_cart_quantity(key, number); 166 } 167 168 /** 169 * Remove an item from the cart 170 * 171 * @param {Object} button - The remove button that was clicked 172 */ 173 function cc_remove_item_from_cart(button) { 174 var cartItemKey = button.data('cart_item_key'); 175 176 $('.cc-cart-items').html(getSkeletonHTML()); 177 $('.cc-compass .ccicon-cart').hide(); 178 $('.cc-compass .cc-loader').show(); 179 180 $.ajax({ 181 type: 'POST', 182 url: getWcAjaxUrl('cc_remove_item_from_cart'), 183 data: { 184 cart_item_key: cartItemKey, 185 nonce: cc_ajax_script.nonce 186 }, 187 success: handleCartResponse, 188 error: function() { 189 cc_refresh_cart(); 190 }, 191 complete: function() { 192 $('.cc-compass .ccicon-cart').show(); 193 $('.cc-compass .cc-loader').hide(); 194 $('.cc-window-wrapper').show(); 195 } 196 }); 197 } 198 199 /** 200 * Display the cart items list in the cart window 201 */ 202 function cc_cart_item_list() { 203 if ( !ccWindow.hasClass( 'visible' ) ) { 204 $( '.cc-compass' ).trigger( 'click' ); 205 } 206 } 207 208 /** 209 * Refresh the cart contents via AJAX 210 * Gets updated fragments and refreshes the cart display 211 */ 212 function cc_refresh_cart() { 213 if (window.cc_refresh_ajax && window.cc_refresh_ajax.readyState !== 4) { 214 window.cc_refresh_ajax.abort(); 215 } 216 217 window.cc_refresh_ajax = $.ajax({ 218 type: 'POST', 219 url: cc_ajax_script.ajaxurl, 220 data: { 221 action: 'caddy_get_cart_fragments', 222 _: new Date().getTime() 223 }, 224 cache: false, 225 success: handleCartResponse, 226 error: function(xhr, status, error) { 227 if (status !== 'abort') { 228 $('.cc-cart-items').html('<div class="cc-cart-error">Unable to refresh cart. <a href="javascript:void(0)" onclick="cc_refresh_cart()">Try again</a>.</div>'); 229 } 230 } 231 }); 232 } 233 234 //============================================================================= 235 // COUPON MANAGEMENT FUNCTIONS 236 //============================================================================= 237 238 /** 239 * Apply a coupon code from the cart screen 240 */ 241 function cc_coupon_code_applied_from_cart_screen() { 242 var coupon_code = $('.cc-coupon-form #cc_coupon_code').val(); 243 244 $('#cc-cart').css('opacity', '0.3'); 245 246 $.ajax({ 247 type: 'POST', 248 url: getWcAjaxUrl('cc_apply_coupon_to_cart'), 249 data: { 250 nonce: cc_ajax_script.nonce, 251 coupon_code: coupon_code 252 }, 253 success: handleCartResponse, 254 complete: function() { 255 $('#cc-cart').css('opacity', '1'); 256 var tabs = new Tabby('[data-tabs]'); 257 tabs.toggle('#cc-cart'); 258 } 259 }); 260 } 261 262 /** 263 * Remove a coupon code from the cart screen 264 * 265 * @param {Object} $remove_code - The remove coupon button that was clicked 266 */ 267 function cc_coupon_code_removed_from_cart_screen($remove_code) { 268 var coupon_code_to_remove = $remove_code.parent('.cc-applied-coupon').find('.cc_applied_code').text(); 269 270 $('#cc-cart').css('opacity', '0.3'); 271 272 $.ajax({ 273 type: 'POST', 274 url: getWcAjaxUrl('cc_remove_coupon_code'), 275 data: { 276 nonce: cc_ajax_script.nonce, 277 coupon_code_to_remove: coupon_code_to_remove 278 }, 279 success: handleCartResponse, 280 complete: function() { 281 $('#cc-cart').css('opacity', '1'); 282 var tabs = new Tabby('[data-tabs]'); 283 tabs.toggle('#cc-cart'); 284 } 285 }); 286 } 287 288 //============================================================================= 289 // SAVE FOR LATER FUNCTIONALITY 290 //============================================================================= 291 292 /** 293 * Save an item for later 294 * Moves an item from the cart to the save-for-later list 295 * 296 * @param {Object} $button - The save for later button that was clicked 297 */ 298 function cc_save_for_later($button) { 299 var product_id = $button.data('product_id'); 300 var cart_item_key = $button.data('cart_item_key'); 301 302 $('#cc-cart').css('opacity', '0.3'); 303 $button.addClass('cc_hide_btn'); 304 $button.parent().find('.cc-loader').show(); 305 306 $.ajax({ 307 type: 'POST', 308 url: getWcAjaxUrl('cc_save_for_later'), 309 data: { 310 nonce: cc_ajax_script.nonce, 311 product_id: product_id, 312 cart_item_key: cart_item_key 313 }, 314 success: function(response) { 315 handleCartResponse(response); 316 var tabs = new Tabby('[data-tabs]'); 317 tabs.toggle('#cc-saves'); 318 },
319 complete: function() { 320 $button.removeClass('cc_hide_btn'); 321 $button.parent().find('.cc-loader').hide(); 322 $('#cc-cart').css('opacity', '1'); 323 } 324 }); 325 } 326 327 /** 328 * Move an item from save-for-later to cart 329 * 330 * @param {Object} $button - The move to cart button that was clicked 331 */ 332 function cc_move_to_cart($button) { 333 var product_id = $button.data('product_id'); 334 335 $button.addClass('cc_hide_btn'); 336 $button.parent().find('.cc-loader').show(); 337 338 // First, add to cart using WooCommerce's standard method 339 $.ajax({ 340 type: 'POST', 341 url: getWcAjaxUrl('add_to_cart'), 342 data: { 343 product_id: product_id, 344 quantity: 1 345 }, 346 success: function(response) { 347 if (response.error) { 348 $button.removeClass('cc_hide_btn'); 349 $button.parent().find('.cc-loader').hide(); 350 // Handle error - don't remove from SFL 351 } else { 352 // Cart addition successful, now remove from SFL using existing method 353 $.ajax({ 354 type: 'POST', 355 url: getWcAjaxUrl('cc_remove_item_from_sfl'), 356 data: { 357 nonce: cc_ajax_script.nonce, 358 product_id: product_id 359 }, 360 success: function(sfl_response) { 361 cc_cart_screen('move_to_cart'); 362 }, 363 error: function() { 364 cc_cart_screen('move_to_cart'); 365 } 366 }); 367 } 368 }, 369 error: function() { 370 $button.removeClass('cc_hide_btn'); 371 $button.parent().find('.cc-loader').hide(); 372 cc_refresh_cart(); 373 } 374 }); 375 } 376 377 /** 378 * Remove an item from the save-for-later list 379 * 380 * @param {Object} button - The remove button that was clicked 381 */ 382 function cc_remove_item_from_save_for_later(button) { 383 var productID = button.data('product_id'); 384 385 $('#cc-saves').css('opacity', '0.3'); 386 387 $.ajax({ 388 type: 'POST', 389 url: getWcAjaxUrl('cc_remove_item_from_sfl'), 390 data: { 391 nonce: cc_ajax_script.nonce, 392 product_id: productID 393 }, 394 success: function(response) { 395 handleCartResponse(response); 396 var tabs = new Tabby('[data-tabs]'); 397 tabs.toggle('#cc-saves'); 398 }, 399 complete: function() { 400 $('#cc-saves').css('opacity', '1'); 401 } 402 }); 403 } 404 405 /** 406 * Display the saved items list in the cart window 407 */ 408 function cc_saved_item_list() { 409 $( '.cc-compass' ).toggleClass( 'cc-compass-open' ); 410 $( 'body' ).toggleClass( 'cc-window-open' ); 411 412 $( '.cc-pl-info-container' ).hide(); 413 $( '.cc-window-wrapper' ).show(); 414 415 $( '.cc-overlay' ).show(); 416 417 var tabs = new Tabby( '[data-tabs]' ); 418 tabs.toggle( '#cc-saves' ); 419 420 ccWindow.animate( { 'right': '0' }, 'slow' ).addClass( 'visible' ); 421 } 422 423 /** 424 * Navigate back to cart from product info view 425 */ 426 function cc_back_to_cart() { 427 $( '.cc-pl-info-container' ).hide(); 428 $( '.cc-window-wrapper' ).show(); 429 } 430 431 //============================================================================= 432 // DOCUMENT READY - EVENT HANDLERS & INITIALIZATION 433 //============================================================================= 434 435 jQuery( document ).ready( function( $ ) { 436 437 // Initialize cart screen on page load 438 setTimeout( function() { 439 cc_cart_screen(); 440 }, 200 ); 441 442 //------------------------------------------------------------------------- 443 // ACCESSIBILITY & NAVIGATION 444 //------------------------------------------------------------------------- 445 446 // Tab usability 447 $( '.cc-nav ul li a' ).mousedown( function() { 448 $( this ).addClass( 'using-mouse' ); 449 } ); 450 451 $( 'body' ).keydown( function() { 452 $( '.cc-nav ul li a' ).removeClass( 'using-mouse' ); 453 } ); 454 455 // cc-window tabbing 456 var tabs = new Tabby( '[data-tabs]' ); 457 458 // Tab navigation events 459 $( document ).on( 'click', '.cc-nav ul li a', function() { 460 var current_tab = $( this ).attr( 'data-id' ); 461 if ( 'cc-cart' === current_tab ) { 462 $( '.cc-pl-upsells-slider' ).resize(); 463 } 464 } ); 465 466 //------------------------------------------------------------------------- 467 // CART WINDOW INTERACTIONS 468 //------------------------------------------------------------------------- 469 470 // Clicking outside of mini cart 471 $( document ).mouseup( function( e ) { 472 var container = $( '.cc-window.visible, .cc-compass, #toast-container' ); 473 474 if ( !container.is( e.target ) && container.has( e.target ).length === 0 ) { 475 if ( ccWindow.hasClass( 'visible' ) ) { 476 477 $( '.cc-compass' ).toggleClass( 'cc-compass-open' ); 478 $( 'body' ).toggleClass( 'cc-window-open' ); 479 480 $( '.cc-overlay' ).hide(); 481 ccWindow.animate( { 'right': '-1000px' }, 'slow' ).removeClass( 'visible' ); 482 483 if ( $( '#toast-container' ).length > 0 ) { 484 $( '#toast-container' ).animate( { 'right': '25px' }, 'fast' ).toggleClass( 'cc-toast-open' ); 485 } 486 } 487 } 488 } ); 489 490 // Compass click handler (toggle cart window) 491 $(document).on('click', '.cc-compass', function() { 492 $(this).toggleClass('cc-compass-open'); 493 $('body').toggleClass('cc-window-open'); 494 495 if (ccWindow.hasClass('visible')) { 496 $('.cc-overlay').hide(); 497 $('.cc-window-wrapper').hide(); 498 ccWindow.animate({'right': '-1000px'}, 'slow').removeClass('visible'); 499 } else { 500 $('.cc-overlay').show(); 501 $('.cc-window-wrapper').show(); 502 503 $('.cc-cart-items').html(getSkeletonHTML()); 504 505 tabs.toggle('#cc-cart'); 506 507 ccWindow.animate({'right': '0'}, 'slow').addClass('visible'); 508 509 cc_refresh_cart(); 510 } 511 }); 512 513 // Close button for cart window 514 $( document ).on( 'click', '.ccicon-x', function() { 515 $( '.cc-overlay' ).hide(); 516 ccWindow.animate( { 'right': '-1000px' }, 'slow' ).removeClass( 'visible' ); 517 $( '.cc-compass' ).toggleClass( 'cc-compass-open' ); 518 $( 'body' ).toggleClass( 'cc-window-open' ); 519 } ); 520 521 //------------------------------------------------------------------------- 522 // CART ITEM MANAGEMENT 523 //------------------------------------------------------------------------- 524 525 // Remove cart item 526 $( document ).on( 'click', '.cc-cart-product-list .cc-cart-product a.remove_from_cart_button', function() { 527 var button = $( this ); 528 cc_remove_item_from_cart( button ); 529 } ); 530 531 // Item quantity update 532 $( document ).on( 'click', '.cc_item_quantity_update', function() { 533 var $this = $(this); 534 var quantityInput = $this.siblings('.cc_item_quantity'); 535 var currentQuantity = parseInt(quantityInput.val(), 10); 536 537 if ($this.hasClass('cc_item_quantity_minus') && currentQuantity === 1) { 538 // When quantity is 1 and user clicks minus, remove the item instead 539 var removeButton = $this.closest('.cc-cart-product').find('a.remove_from_cart_button'); 540 removeButton.trigger('click'); 541 return false; // Prevent any further processing 542 } else { 543 cc_quantity_update_buttons($this); 544 } 545 } ); 546
547 // Cart items list button clicked 548 $( document ).on( 'click', '.cc_cart_items_list', function() { 549 cc_cart_item_list(); 550 } ); 551 552 // View cart button clicked 553 $( document ).on( 'click', '.added_to_cart.wc-forward, .woocommerce-error .button.wc-forward', function( e ) { 554 e.preventDefault(); 555 cc_cart_item_list(); 556 } ); 557 558 // Product added view cart button 559 $( document ).on( 'click', '.cc-pl-info .cc-pl-actions .cc-view-cart', function() { 560 tabs.toggle( '#cc-cart' ); 561 } ); 562 563 //------------------------------------------------------------------------- 564 // SAVE FOR LATER FUNCTIONALITY 565 //------------------------------------------------------------------------- 566 567 // Remove from save for later 568 $( document ).on( 'click', 'a.remove_from_sfl_button', function() { 569 var button = $( this ); 570 cc_remove_item_from_save_for_later( button ); 571 } ); 572 573 // Save for later button click from the Caddy cart screen 574 $( document ).on( 'click', '.save_for_later_btn', function() { 575 cc_save_for_later( $( this ) ); 576 } ); 577 578 // Move to cart button clicked 579 $( document ).on( 'click', '.cc_cart_from_sfl', function() { 580 cc_move_to_cart( $( this ) ); 581 } ); 582 583 // Move to cart button 584 $( document ).on( 'click', '.cc_back_to_cart', function() { 585 cc_back_to_cart(); 586 } ); 587 588 // Saved items list button clicked 589 $( document ).on( 'click', '.cc_saved_items_list', function() { 590 cc_saved_item_list(); 591 } ); 592 593 // Clicks on a view saved items 594 $( document ).on( 'click', '.cc-view-saved-items', function() { 595 var tabs = new Tabby( '[data-tabs]' ); 596 tabs.toggle( '#cc-saves' ); 597 } ); 598 599 // Handle variations with save for later 600 if ( $( '.variations_form' ).length > 0 ) { 601 $( '.cc_add_product_to_sfl' ).addClass( 'disabled' ); 602 $( this ).each( function() { 603 $( this ).on( 'found_variation', function( event, variation ) { 604 $( '.cc_add_product_to_sfl' ).removeClass( 'disabled' ); 605 } ); 606 607 $( this ).on( 'reset_data', function() { 608 $( '.cc_add_product_to_sfl' ).addClass( 'disabled' ); 609 } ); 610 } ); 611 } 612 613 //------------------------------------------------------------------------- 614 // COUPON HANDLING 615 //------------------------------------------------------------------------- 616 617 // Apply coupon form submission 618 $( document ).on( 'submit', '#apply_coupon_form', function( e ) { 619 e.preventDefault(); 620 cc_coupon_code_applied_from_cart_screen(); 621 } ); 622 623 // Remove coupon 624 $( document ).on( 'click', '.cc-applied-coupon .cc-remove-coupon', function() { 625 cc_coupon_code_removed_from_cart_screen( $( this ) ); 626 } ); 627 628 // Coupon form toggle 629 $(document).on('click', '.cc-coupon-title', function() { 630 var $couponForm = $('.cc-coupon-form'); 631 var $couponWrapper = $('.cc-coupon'); 632 633 if ($couponForm.is(':hidden')) { 634 $couponWrapper.addClass('cc-coupon-open'); 635 $couponForm.slideDown(300); 636 } else { 637 $couponForm.slideUp(300, function() { 638 $couponWrapper.removeClass('cc-coupon-open'); 639 }); 640 } 641 }); 642 643 // Update the error notice click handler 644 $(document).on('click', '.cc-coupon .woocommerce-error', function(e) { 645 var $error = $(this); 646 var clickX = e.pageX - $error.offset().left; 647 648 if (clickX > $error.width() - 40) { 649 $(this).closest('.woocommerce-notices-wrapper').fadeOut(200); 650 } 651 }); 652 653 //------------------------------------------------------------------------- 654 // ADD TO CART HANDLING 655 //------------------------------------------------------------------------- 656 657 // Add a flag to track the source of the event 658 var handlingOurAjaxResponse = false; 659 660 // Add a flag to prevent double handling 661 var handlingCartUpdate = false; 662 663 // Handle add to cart button clicks for guest users 664 $(document).on('click', '.add_to_cart_button, .single_add_to_cart_button', function() { 665 expectingCartUpdate = true; 666 }); 667 668 // Handle WooCommerce cart updates for guest users 669 $('body').on('added_to_cart wc_fragments_refreshed', function(event) { 670 // If this is a fragments refresh after add-to-cart, open cart 671 if (event.type === 'wc_fragments_refreshed' && expectingCartUpdate) { 672 expectingCartUpdate = false;
673 if (!ccWindow.hasClass('visible')) { 674 $('.cc-compass').trigger('click'); 675 } 676 } 677 }); 678 679 /** 680 * Handle WooCommerce 'added_to_cart' event 681 * 682 * Triggered when products are successfully added to the cart. 683 * Manages cart window display based on: 684 * - Device type (mobile/desktop) 685 * - User preferences for notifications 686 * - Whether the product was added from recommendations 687 * 688 * Prevents duplicate event handling with the handlingCartUpdate flag. 689 * 690 * @param {Event} e - The event object 691 * @param {Object} fragments - Cart fragments returned from WooCommerce 692 * @param {string} cart_hash - The cart hash 693 * @param {Object} this_button - The button that triggered the add to cart action 694 */ 695 $('body').on('added_to_cart', function(e, fragments, cart_hash, this_button) { 696 697 if (handlingCartUpdate) { 698 return; 699 } 700 701 handlingCartUpdate = true; 702 703 var cpDeskNotice = $('.cc-compass-desk-notice').val(), 704 cpMobNotice = $('.cc-compass-mobile-notice').val(); 705 706 var isRecommendationButton = $(this_button).closest('.cc-pl-recommendations').length > 0; 707 708 if (isRecommendationButton) { 709 cc_cart_screen('', {fragments: fragments, cart_hash: cart_hash}); 710 } 711 712 if (cc_ajax_script.is_mobile && !ccWindow.hasClass('visible') && 'mob_disable_notices' === cpMobNotice) { 713 setTimeout(function() { 714 $('.cc-compass').trigger('click'); 715 handlingCartUpdate = false; 716 }, 20); 717 } else if (!cc_ajax_script.is_mobile && !ccWindow.hasClass('visible') 718 && ('desk_disable_notices' === cpDeskNotice || 'desk_notices_caddy_window' === cpDeskNotice || '' === cpDeskNotice)) { 719 setTimeout(function() { 720 $('.cc-compass').trigger('click'); 721 handlingCartUpdate = false; 722 }, 20); 723 } else { 724 handlingCartUpdate = false; 725 } 726 }); 727 728 // Listen for add to cart form submissions 729 $(document.body).on('submit', 'form.cart', function(e) { 730 e.preventDefault(); 731 732 var $form = $(this); 733 var $button = e.originalEvent && e.originalEvent.submitter 734 ? $(e.originalEvent.submitter) 735 : $form.find('button[type="submit"]'); 736 737 // Create FormData object 738 var formData = new FormData($form[0]); 739 740 // If product ID isn't in form, try to get it from URL 741 if(!formData.has('add-to-cart') && $form.attr('action')){ 742 var matches = $form.attr('action').match(/add-to-cart=([0-9]+)/); 743 if(matches){ 744 formData.append('add-to-cart', matches[1]); 745 } 746 } 747 748 // Add button data if it has name/value 749 if($button.attr('name') && $button.attr('value')){ 750 formData.append($button.attr('name'), $button.attr('value')); 751 } 752 753 // Add caddy_ajax parameter 754 formData.append('caddy_ajax', true); 755 756 // Show loading state 757 $button.addClass('loading'); 758 759 // Add to cart via Ajax 760 $.ajax({ 761 type: 'POST', 762 url: wc_add_to_cart_params.wc_ajax_url.replace('%%endpoint%%', 'add_to_cart'), 763 data: formData, 764 processData: false, 765 contentType: false, 766 success: function(response) { 767 if (response.error) { 768 // Let WooCommerce handle the error display 769 return true; 770 } 771 772 // Update fragments 773 if (response.fragments) { 774 $.each(response.fragments, function(key, value) { 775 $(key).replaceWith(value); 776 }); 777 $(document.body).trigger('wc_fragments_refreshed'); 778 } 779 780 // Open the cart drawer 781 $('.cc-compass').addClass('cc-compass-open'); 782 $('body').addClass('cc-window-open'); 783 $('.cc-overlay').show(); 784 $('.cc-window-wrapper').show(); 785 $('.cc-cart-items').html(getSkeletonHTML()); 786 ccWindow.animate({'right': '0'}, 'slow').addClass('visible'); 787 788 // Load cart screen with productAdded flag 789 cc_cart_screen('yes'); 790 791 // Ensure wrapper stays visible after fragments refresh 792 $(document.body).one('wc_fragments_refreshed', function() { 793 $('.cc-window-wrapper').show(); 794 }); 795 }, 796 complete: function() { 797 $button.removeClass('loading'); 798 } 799 }); 800 }); 801 802 }); 803 804})( 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.