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