1/* global wc_add_to_cart_params */ 2jQuery( function( $ ) { 3 4 if ( typeof wc_add_to_cart_params === 'undefined' ) { 5 return false; 6 } 7 8 /** 9 * AddToCartHandler class. 10 */ 11 var AddToCartHandler = function() { 12 this.requests = []; 13 this.addRequest = this.addRequest.bind( this ); 14 this.run = this.run.bind( this ); 15 this.$liveRegion = this.createLiveRegion(); 16 17 $( document.body ) 18 .on( 'click', '.add_to_cart_button:not(.wc-interactive)', { addToCartHandler: this }, this.onAddToCart ) 19 .on( 'click', '.remove_from_cart_button', { addToCartHandler: this }, this.onRemoveFromCart ) 20 .on( 'added_to_cart', { addToCartHandler: this }, this.onAddedToCart ) 21 .on( 'removed_from_cart', { addToCartHandler: this }, this.onRemovedFromCart ) 22 .on( 'ajax_request_not_sent.adding_to_cart', this.updateButton ); 23 }; 24 25 /** 26 * Add add to cart event. 27 */ 28 AddToCartHandler.prototype.addRequest = function( request ) { 29 this.requests.push( request ); 30 31 if ( 1 === this.requests.length ) { 32 this.run(); 33 } 34 }; 35 36 /** 37 * Run add to cart events. 38 */ 39 AddToCartHandler.prototype.run = function() { 40 var requestManager = this, 41 originalCallback = requestManager.requests[0].complete; 42 43 requestManager.requests[0].complete = function() { 44 if ( typeof originalCallback === 'function' ) { 45 originalCallback(); 46 } 47 48 requestManager.requests.shift(); 49 50 if ( requestManager.requests.length > 0 ) { 51 requestManager.run(); 52 } 53 }; 54 55 $.ajax( this.requests[0] ); 56 }; 57 58 /** 59 * Handle the add to cart event. 60 */ 61 AddToCartHandler.prototype.onAddToCart = function( e ) { 62 var $thisbutton = $( this ); 63 64 if ( $thisbutton.is( '.ajax_add_to_cart' ) ) { 65 if ( ! $thisbutton.attr( 'data-product_id' ) ) { 66 return true; 67 } 68 69 // Clean existing text in mini cart live region and update aria-relevant attribute 70 // so screen readers can identify the next update if it's the same as the previous one. 71 e.data.addToCartHandler.$liveRegion 72 .text( '' ) 73 .removeAttr( 'aria-relevant' ); 74 75 e.preventDefault(); 76 77 $thisbutton.removeClass( 'added' ); 78 $thisbutton.addClass( 'loading' ); 79 80 // Allow 3rd parties to validate and quit early. 81 if ( false === $( document.body ).triggerHandler( 'should_send_ajax_request.adding_to_cart', [ $thisbutton ] ) ) { 82 $( document.body ).trigger( 'ajax_request_not_sent.adding_to_cart', [ false, false, $thisbutton ] ); 83 return true; 84 } 85 86 var data = {}; 87 88 // Fetch changes that are directly added by calling $thisbutton.data( key, value ) 89 $.each( $thisbutton.data(), function( key, value ) { 90 data[ key ] = value; 91 }); 92 93 // Fetch data attributes in $thisbutton. Give preference to data-attributes because they can be directly modified by javascript 94 // while `.data` are jquery specific memory stores. 95 $.each( $thisbutton[0].dataset, function( key, value ) { 96 data[ key ] = value; 97 }); 98 99 // Trigger event. 100 $( document.body ).trigger( 'adding_to_cart', [ $thisbutton, data ] ); 101 102 e.data.addToCartHandler.addRequest({ 103 type: 'POST', 104 url: wc_add_to_cart_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'add_to_cart' ), 105 data: data, 106 success: function( response ) { 107 if ( ! response ) { 108 return; 109 } 110 111 if ( response.error && response.product_url ) { 112 window.location = response.product_url; 113 return; 114 } 115 116 // Redirect to cart option 117 if ( wc_add_to_cart_params.cart_redirect_after_add === 'yes' ) { 118 window.location = wc_add_to_cart_params.cart_url; 119 return; 120 } 121 122 // Trigger event so themes can refresh other areas. 123 $( document.body ).trigger( 'added_to_cart', [ response.fragments, response.cart_hash, $thisbutton ] ); 124 }, 125 dataType: 'json' 126 }); 127 } 128 }; 129 130 /** 131 * Update fragments after remove from cart event in mini-cart. 132 */ 133 AddToCartHandler.prototype.onRemoveFromCart = function( e ) { 134 var $thisbutton = $( this ), 135 $row = $thisbutton.closest( '.woocommerce-mini-cart-item' ); 136 137 e.data.addToCartHandler.$liveRegion 138 .text( '' ) 139 .removeAttr( 'aria-relevant' ); 140 141 e.preventDefault(); 142 143 $row.block({ 144 message: null, 145 overlayCSS: { 146 opacity: 0.6 147 } 148 }); 149 150 e.data.addToCartHandler.addRequest({ 151 type: 'POST', 152 url: wc_add_to_cart_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'remove_from_cart' ), 153 data: { 154 cart_item_key : $thisbutton.data( 'cart_item_key' ) 155 }, 156 success: function( response ) { 157 if ( ! response || ! response.fragments ) { 158 window.location = $thisbutton.attr( 'href' ); 159 return; 160 } 161 162 $( document.body ).trigger( 'removed_from_cart', [ response.fragments, response.cart_hash, $thisbutton ] ); 163 }, 164 error: function() { 165 window.location = $thisbutton.attr( 'href' ); 166 return; 167 }, 168 dataType: 'json' 169 }); 170 }; 171 172 /** 173 * Update cart page elements after add to cart events. 174 */ 175 AddToCartHandler.prototype.updateButton = function( e, fragments, cart_hash, $button ) { 176 // Some themes and plugins manually trigger added_to_cart without passing a button element, which in turn calls this function. 177 // If there is no button we don't want to crash. 178 $button = typeof $button === 'undefined' ? false : $button; 179 180 if ( $button ) { 181 $button.removeClass( 'loading' ); 182 183 if ( fragments ) { 184 $button.addClass( 'added' ); 185 } 186 187 // View cart text. 188 if ( fragments && ! wc_add_to_cart_params.is_cart && $button.parent().find( '.added_to_cart' ).length === 0 ) { 189 var anchor = document.createElement( 'a' ); 190 anchor.href = wc_add_to_cart_params.cart_url; 191 anchor.className = 'added_to_cart wc-forward'; 192 anchor.title = wc_add_to_cart_params.i18n_view_cart; 193 anchor.textContent = wc_add_to_cart_params.i18n_view_cart; 194 $button.after( anchor ); 195 } 196 197 $( document.body ).trigger( 'wc_cart_button_updated', [ $button ] ); 198 } 199 }; 200 201 /** 202 * Update fragments after add to cart events. 203 */ 204 AddToCartHandler.prototype.updateFragments = function( e, fragments ) { 205 if ( fragments ) { 206 $.each( fragments, function( key ) { 207 $( key ) 208 .addClass( 'updating' ) 209 .fadeTo( '400', '0.6' ) 210 .block({ 211 message: null, 212 overlayCSS: { 213 opacity: 0.6 214 } 215 }); 216 }); 217 218 $.each( fragments, function( key, value ) { 219 $( key ).replaceWith( value ); 220 $( key ).stop( true ).css( 'opacity', '1' ).unblock(); 221 }); 222 223 $( document.body ).trigger( 'wc_fragments_loaded' ); 224 } 225 }; 226 227 /** 228 * Update cart live region message after add/remove cart events. 229 */ 230 AddToCartHandler.prototype.alertCartUpdated = function( e, fragments, cart_hash, $button ) { 231 // Some themes and plugins manually trigger added_to_cart without passing a button element, which in turn calls this function. 232 // If there is no button we don't want to crash. 233 $button = typeof $button === 'undefined' ? false : $button; 234 235 if ( $button ) { 236 var message = $button.data( 'success_message' ); 237 238 if ( !message ) { 239 return; 240 } 241 242 // If the response after adding/removing an item to/from the cart is really fast, 243 // screen readers may not have time to identify the changes in the live region element. 244 // So, we add a delay to ensure an interval between messages. 245 e.data.addToCartHandler.$liveRegion 246 .delay(1000) 247 .text( message ) 248 .attr( 'aria-relevant', 'all' ); 249 } 250 }; 251 252 /** 253 * Add live region into the body element. 254 */ 255 AddToCartHandler.prototype.createLiveRegion = function() { 256 var existingLiveRegion = $( '.widget_shopping_cart_live_region' ); 257 258 if ( existingLiveRegion.length ) { 259 return existingLiveRegion; 260 } 261 262 return $( '<div class="widget_shopping_cart_live_region screen-reader-text" role="status"></div>' ).appendTo( 'body' ); 263 }; 264 265 /** 266 * Callbacks after added to cart event. 267 */ 268 AddToCartHandler.prototype.onAddedToCart = function( e, fragments, cart_hash, $button ) { 269 e.data.addToCartHandler.updateButton( e, fragments, cart_hash, $button ); 270 e.data.addToCartHandler.updateFragments( e, fragments ); 271 e.data.addToCartHandler.alertCartUpdated( e, fragments, cart_hash, $button ); 272 }; 273 274 /** 275 * Callbacks after removed from cart event. 276 */ 277 AddToCartHandler.prototype.onRemovedFromCart = function( e, fragments, cart_hash, $button ) { 278 e.data.addToCartHandler.updateFragments( e, fragments ); 279 e.data.addToCartHandler.alertCartUpdated( e, fragments, cart_hash, $button ); 280 }; 281 282 /** 283 * Init AddToCartHandler. 284 */ 285 new AddToCartHandler(); 286});
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.