1/* 2 * One Page Checkout JS: add/remove items from a One Page Checkout page via Ajax 3 */ 4jQuery( document ).ready( 5 function($){ 6 7 var response_messages = ''; 8 var timeout; 9 var delay = 700; 10 11 var $checkout = $( '.checkout' ); 12 var $body = $( 'body' ); 13 /** 14 * Review Order Template Item Management (Removal & Quantity Adjustment) 15 */ 16 17 // Quantity buttons 18 $checkout.on( 19 'input', 20 '#order_review .opc_cart_item div.quantity input.qty', 21 function(e) { 22 23 var input = $( this ), 24 selectors = '.checkout #order_review .opc_cart_item div.quantity input.qty'; 25 26 const quantity = input.val(); 27 28 clearTimeout( timeout ); 29 30 if ( quantity === '' ) { 31 return; 32 } 33 34 timeout = setTimeout( 35 function() { 36 37 var data = { 38 quantity, 39 add_to_cart: parseInt( input.closest( '.opc_cart_item' ).data( 'add_to_cart' ) ), 40 update_key: input.closest( '.opc_cart_item' ).data( 'update_key' ), 41 nonce: wcopc.wcopc_nonce, 42 }; 43 44 if ( data['quantity'] == 0 ) { 45 data['action'] = 'pp_remove_from_cart'; 46 } else { 47 data['action'] = 'pp_update_add_in_cart'; 48 } 49 50 input.ajax_add_remove_product( data, e, selectors ); 51 52 }, 53 delay 54 ); 55 56 e.preventDefault(); 57 58 } 59 ); 60 61 // handle blur with empty value 62 $checkout.on( 63 'blur', 64 '#order_review .opc_cart_item div.quantity input.qty', 65 function () { 66 const input = $( this ); 67 const quantity = input.val(); 68 // the previous value is still stored in value attribute on input 69 if ( quantity === '' ) { 70 input.val( input.attr( 'value' ) ); 71 } 72 } 73 ); 74 75 // Remove buttons 76 $checkout.on( 77 'click', 78 '#order_review .opc_cart_item a.remove', 79 function(e) { 80 81 var data = { 82 action: 'pp_remove_from_cart', 83 add_to_cart: parseInt( $( this ).closest( '.opc_cart_item' ).data( 'add_to_cart' ) ), 84 update_key: $( this ).closest( '.opc_cart_item' ).data( 'update_key' ), 85 nonce: wcopc.wcopc_nonce, 86 }, 87 selectors = '.checkout #order_review .opc_cart_item a.remove'; 88 89 $( this ).ajax_add_remove_product( data, e, selectors ); 90 91 e.preventDefault(); 92 93 } 94 ); 95 96 /** 97 * Single Product Template 98 */ 99 100 /* Add/remove products with button element or a tags */ 101 $( '#opc-product-selection button.single_add_to_cart_button, .wcopc-product-single button.single_add_to_cart_button' ).on( 102 'click', 103 function(e) { 104 105 e.preventDefault(); 106 107 var is_variable = $( this ).closest( '.variations_form' ).find( 'input[name="variation_id"]' ).length === 1, 108 is_grouped = $( this ).siblings( '.group_table' ).length >= 1, 109 add_to_cart_id = $( this ).closest( '.cart' ).find( '[name="add-to-cart"]' ).val(), 110 has_quantity = $( this ).closest( '.cart' ).find( 'input[name="quantity"]' ).length === 1, 111 data = { 112 action: 'pp_add_to_cart', 113 nonce: wcopc.wcopc_nonce, 114 input_data: $( this ).closest( '.product-quantity, .wcopc-product-single form' ).find( 'input[name!="variation_id"][name!="product_id"][name!="add-to-cart"][name!="quantity"], select, textarea' ).serialize(), 115 }, 116 selectors = '#opc-product-selection button.single_add_to_cart_button, .wcopc-product-single button.single_add_to_cart_button'; 117 118 // Do not attempt the ajax call if the add to cart product ID is undefined. 119 if ( 'undefined' === typeof add_to_cart_id ) { 120 return; 121 } 122 123 if ( is_grouped ) { 124 125 data.add_to_cart = []; 126 127 $( this ).siblings( '.group_table' ).find( 'input[name^="quantity"]' ).each( 128 function( index, value ) { 129 130 if ( ( ! $( this ).is( ':checkbox' ) && $( this ).val() > 0 ) || ( $( this ).is( ':checkbox' ) && $( this ).is( ':checked' ) ) ) { 131 132 var product_id = parseInt( $( this ).attr( 'name' ).match( /quantity\[(\d+)\]/ )[1] ), 133 quantity = parseInt( $( this ).val() ); 134 135 data.add_to_cart.push( 136 { 137 'product_id': product_id, 138 'quantity': quantity, 139 } 140 ); 141 } 142 } 143 ); 144 145 data.add_to_cart.forEach( 146 function(add_to_cart_data) { 147 148 data.add_to_cart = parseInt( add_to_cart_data.product_id ); 149 data.quantity = parseInt( add_to_cart_data.quantity ); 150 151 $( this ).ajax_add_remove_product( data, e, selectors, false ); 152 } 153 ); 154 155 } else { 156 157 data.add_to_cart = parseInt( add_to_cart_id ); 158 159 if ( is_variable ) { 160 data.variation_id = parseInt( $( this ).closest( '.variations_form' ).find( 'input[name="variation_id"]' ).val() ); 161 } 162 163 // The quantity input field might be missing if a product is sold individually or has only 1 unit of stock remaining 164 if ( has_quantity ) { 165 data.quantity = parseInt( $( this ).closest( '.cart' ).find( 'input[name="quantity"]' ).val() ); 166 } 167 168 $( this ).ajax_add_remove_product( data, e, selectors ); 169 } 170 171 e.preventDefault(); 172 } 173 ); 174 175 /** 176 * Other Templates 177 */ 178 179 /* Add/remove products with number input type */ 180 $( '#opc-product-selection input[type="number"][data-add_to_cart]' ).on( 181 'input', 182 function(e) { 183 var input = $( this ), 184 selectors = '#opc-product-selection input[type="number"][data-add_to_cart]'; 185 186 const quantity = input.val(); 187 188 clearTimeout( timeout ); 189 190 if ( quantity === '' ) { 191 return; 192 } 193 194 timeout = setTimeout( 195 function() { 196 197 var data = { 198 quantity, 199 add_to_cart: parseInt( input.data( 'add_to_cart' ) ), 200 input_data: input.closest( '.product-quantity' ).find( 'input[name!="product_id"], select, textarea' ).serialize(), 201 nonce: wcopc.wcopc_nonce, 202 }; 203 204 if ( data['quantity'] == 0 ) { 205 data['action'] = 'pp_remove_from_cart'; 206 } else { 207 data['action'] = 'pp_update_add_in_cart'; 208 } 209 210 input.ajax_add_remove_product( data, e, selectors ); 211 212 }, 213 delay 214 ); 215 216 e.preventDefault(); 217 218 } 219 ); 220 221 // handle blur with empty value for the input above 222 $( '#opc-product-selection input[type="number"][data-add_to_cart]' ).on( 223 'blur', 224 function () { 225 const input = $( this ); 226 const quantity = input.val(); 227 // the previous value is still stored in value attribute on input 228 if ( quantity === '' ) { 229 input.val( input.attr( 'value' ) ); 230 } 231 } 232 ); 233 234 /* Add/remove products with radio or checkbox inputs */ 235 $( '#opc-product-selection input[type="radio"][data-add_to_cart], #opc-product-selection input[type="checkbox"][data-add_to_cart]' ).on( 236 'change', 237 function(e) { 238 239 var input = $( this ), 240 selectors = '#opc-product-selection input[type="radio"][data-add_to_cart], #opc-product-selection input[type="checkbox"][data-add_to_cart]'; 241 242 clearTimeout( timeout ); 243 244 timeout = setTimeout( 245 function() { 246 247 var data = { 248 add_to_cart: parseInt( input.data( 'add_to_cart' ) ), 249 nonce: wcopc.wcopc_nonce 250 }; 251 252 if ( input.is( ':checked' ) ) { 253 254 if ( input.prop( 'type' ) == 'radio' ) { 255 256 data.empty_cart = 'true'; 257 $( 'input[data-add_to_cart]' ).prop( 'checked', false ); 258 input.prop( 'checked', true ); 259 $( '.selected' ).removeClass( 'selected' ); 260 } 261 262 data.action = 'pp_add_to_cart'; 263 input.parents( '.product-item' ).addClass( 'selected' ); 264 265 } else { 266 267 data.action = 'pp_remove_from_cart'; 268 input.parents( '.product-item' ).removeClass( 'selected' ); 269 270 } 271 272 input.ajax_add_remove_product( data, e, selectors ); 273 274 }, 275 delay 276 ); 277 278 } 279 ); 280 281 /* Add/remove products with button element or a tags */ 282 $( '#opc-product-selection a[data-add_to_cart], #opc-product-selection button[data-add_to_cart]' ).on( 283 'click', 284 function(e) { 285 286 var data = { 287 add_to_cart: parseInt( $( this ).data( 'add_to_cart' ) ), 288 nonce: wcopc.wcopc_nonce, 289 input_data: $( this ).closest( '.product-quantity' ).find( 'input[name!="product_id"], select, textarea' ).serialize(), 290 }, 291 selectors = '#opc-product-selection a[data-add_to_cart], #opc-product-selection button[data-add_to_cart]'; 292 293 // Toggle button on or off 294 if ( ! $( this ).parents( '.product-item' ).hasClass( 'selected' ) ) { 295 data.action = 'pp_add_to_cart'; 296 $( this ).parents( '.product-item' ).addClass( 'selected' ); 297 } else { 298 data.action = 'pp_remove_from_cart'; 299 $( this ).parents( '.product-item' ).removeClass( 'selected' ); 300 } 301 302 $( this ).ajax_add_remove_product( data, e, selectors ); 303 } 304 ); 305 306 /* Add products from any Easy Pricing Table template */ 307 $( 'a.ptp-button, a.ptp-dg5-button, a.ptp-dg6-button, a.ptp-dg7-button, a.ptp-fancy-button, a.btn.sign-up, .ptp-stylish-pricing_button a, .ptp-design4-col > a' ).on( 308 'click', 309 function(e) { 310
311 var productParams = getUrlsParams( $( this )[0].search.substring( 1 ) ), 312 selectors = 'a.ptp-button, a.ptp-dg5-button, a.ptp-dg6-button, a.ptp-dg7-button, a.ptp-fancy-button, a.btn.sign-up, .ptp-stylish-pricing_button a, .ptp-design4-col > a'; 313 314 var data = { 315 action: 'pp_add_to_cart', 316 add_to_cart: productParams['add-to-cart'], 317 empty_cart: 'true', 318 nonce: wcopc.wcopc_nonce 319 }; 320 321 delete productParams['add-to-cart']; 322 data.input_data = $.param( productParams ); 323 324 $( this ).ajax_add_remove_product( data, e, selectors ); 325 } 326 ); 327 328 // Set response messages when the checkout is fully updated (because it would remove them if we set them before that) 329 $body.on( 330 'updated_checkout', 331 function(){ 332 if ( response_messages.length > 0 ) { 333 var $opc_messages = $( '#opc-messages' ); 334 335 $opc_messages.prepend( response_messages ); 336 337 if ( 'yes' === wcopc.autoscroll && ! $opc_messages.visible() ) { 338 $( 'html, body' ).animate( 339 { 340 scrollTop: ($opc_messages.offset().top - 150 ) 341 }, 342 500 343 ); 344 } 345 346 response_messages = ''; 347 } 348 } 349 ); 350 351 /* Function to add or remove product from cart via an ajax call */ 352 $.fn.ajax_add_remove_product = function( data, e, selectors, async_ajax ) { 353 354 // Default to synchonus Ajax, but for Grouped products, use async 355 async_ajax = ( typeof async_ajax !== 'undefined' ) ? async_ajax : true; 356 357 // Guard against race conditions by disabling the inputs 358 $( selectors ).attr( 'disabled', 'disabled' ); 359 360 // Custom event for devs to hook into before posting of products for processing 361 $( 'body' ).trigger( 'opc_add_remove_product', [ data, e, selectors ] ); 362 363 // Read from opc_add_remove_product trigger above and maybe avoid the AJAX add to cart call. 364 if ( data.invalid === true ) { 365 $( selectors ).removeAttr( 'disabled' ); 366 return; 367 } 368 369 $.ajax( 370 { 371 type: 'POST', 372 url: wcopc.ajax_url, 373 data: data, 374 async: async_ajax, 375 dataType: 'json', 376 success: function( response, status, xhr ) { 377 378 var fragments; 379 380 try { 381 382 if ( response === '-1' ) { 383 throw 'invalid response'; 384 } 385 386 if (xhr.getResponseHeader( 'Content-Type' ) == 'application/json') { 387 response = $.parseJSON( response ); 388 } 389 390 // Get fragments 391 fragments = response.fragments; 392 393 var inputs = $( '#opc-product-selection [data-add_to_cart]' ); 394 395 inputs.each( 396 function( index, value ) { 397 398 var product_id = $( this ).data( 'add_to_cart' ), 399 in_cart = false; 400 401 $.each( 402 response.products_in_cart, 403 function( cart_item_id, cart_item_data ) { 404 if ( ( product_id == cart_item_id || product_id == cart_item_data.product_id ) ) { 405 in_cart = true; 406 } 407 } 408 ); 409 410 if ( $( this ).prop( 'type' ) == 'number' ) { 411 412 if ( in_cart ) { 413 $( this ).val( response.products_in_cart[ product_id ].quantity ).data( 'cart_quantity', response.products_in_cart[ product_id ].quantity ); 414 } else { 415 $( this ).val( 0 ).data( 'cart_quantity', 0 ); 416 } 417 418 } else if ( $( this ).is( 'a, button' ) ) { 419 420 if ( in_cart ) { 421 $( this ).parents( '.product-item' ).addClass( 'selected' ); 422 } else { 423 $( this ).parents( '.product-item' ).removeClass( 'selected' ); 424 } 425 426 } else { 427 428 if ( in_cart ) { 429 $( this ).prop( 'checked', true ); 430 } else { 431 $( this ).prop( 'checked', false ); 432 $( this ).parents( '.product-item' ).removeClass( 'selected' ); 433 } 434 } 435 436 } 437 ); 438 439 // Store messages for use when checkout has finished updating 440 response_messages = response.messages; 441 442 } catch ( err ) { 443 444 if ( 'undefined' == typeof response.messages ) { 445 response_messages = wcopc.ajax_error_notice; 446 } else { 447 response_messages = response.messages; 448 } 449 450 var $opc_messages = $( '#opc-messages' ); 451 $opc_messages.prepend( response.messages ); 452 453 $( 'html, body' ).animate( 454 { 455 scrollTop: ( $opc_messages.offset().top - 50 ) 456 }, 457 500 458 ); 459 } 460 461 $( '#opc-messages .woocommerce-error, #opc-messages .woocommerce-message, #opc-messages .woocommerce-info' ).remove(); 462 463 // remove 'loading' class that Flatsome theme adds 464 $( 'button.single_add_to_cart_button' ).removeClass( 'loading' ); 465 466 // Custom event for devs to hook into after products have been processed 467 $body.trigger( 'after_opc_add_remove_product', [ data, response ] ); 468 469 // Tell WooCommerce to update totals 470 $body.trigger( 'update_checkout' ); 471 472 // Block fragments class 473 if ( fragments ) { 474 $.each( 475 fragments, 476 function( key, value ) { 477 $( key ).addClass( 'updating' ); 478 } 479 ); 480 } 481 482 // Block fragments 483 var $updating = $( '.updating' ); 484 $updating.fadeTo( '400', '0.6' ).block( 485 { 486 message: null, 487 overlayCSS: { 488 opacity: 0.6 489 } 490 } 491 ); 492 493 // Replace fragments 494 if ( fragments ) { 495 $.each( 496 fragments, 497 function( key, value ) { 498 $( key ).replaceWith( value ); 499 } 500 ); 501 } 502 503 // Unblock 504 $updating.stop( true ).css( 'opacity', '1' ).unblock(); 505 506 // It is now safe to change the cart again 507 $( selectors ).removeAttr( 'disabled' ); 508 509 } } 510 ); 511 512 e.preventDefault(); 513 }; 514
515 /* Only display the place order button when a product has been selected */ 516 showHidePlaceOrder(); 517 518 /* Append "Complete Order" anchor and "data-add_to_cart" attribute to single-product template buttons */ 519 initSingleProductTemplateButtons(); 520 521 /* Init custom order-review template quantity buttons */ 522 initOrderReviewQtyButtons(); 523 524 $body.on( 525 'updated_checkout', 526 function() { 527 showHidePlaceOrder(); 528 529 /* Init custom order-review template quantity buttons */ 530 initOrderReviewQtyButtons(); 531 532 /* Check the create account fields are correctly displayed when subscriptions are part of the cart/order after ajax update */ 533 create_account_toggle(); 534 } 535 ); 536 537 function initOrderReviewQtyButtons() { 538 539 $( '#order_review.opc_order_review div.quantity:not(.buttons_added)' ).addClass( 'buttons_added' ).append( '<input type="button" value="+" class="plus" />' ).prepend( '<input type="button" value="-" class="minus" />' ); 540 } 541 542 function showHidePlaceOrder() { 543 544 if ( $( '#order_review tbody' ).children().length > 0 ) { 545 /* Stop animations from other plugins and show immediately.*/ 546 $( '#place_order' ).stop(); 547 $( '#place_order' ).animate( { opacity: 'show', height: 'show', padding: 'show' }, 0 ); 548 $( '#payment .payment_methods' ).show(); 549 } else { 550 /* Stop animations from other plugins and hide immediately*/ 551 $( '#place_order' ).stop(); 552 $( '#place_order' ).animate( { opacity: 'hide', height: 'hide', padding: 'hide' }, 0 ); 553 $( '#payment .payment_methods' ).hide(); 554 } 555 } 556 557 function initSingleProductTemplateButtons() { 558 559 $( '#opc-product-selection button.single_add_to_cart_button' ).each( 560 function() { 561 562 $( this ).after( wcopc.wcopc_complete_order_prompt ); 563 $( this ).attr( 'data-add_to_cart', $( this ).closest( '.cart' ).find( '[name="add-to-cart"]' ).val() ); 564 $( this ).data( 'add_to_cart', $( this ).closest( '.cart' ).find( '[name="add-to-cart"]' ).val() ); 565 } 566 ); 567 568 } 569 570 function getUrlsParams( queryString ){ 571 572 var match, 573 pl = /\+/g, // Regex for replacing addition symbol with a space 574 search = /([^&=]+)=?([^&]*)/g, 575 decode = function (s) { return decodeURIComponent( s.replace( pl, ' ' ) ); }; 576 577 urlParams = {}; 578 579 while ( match = search.exec( queryString ) ) { 580 urlParams[ decode( match[1] ) ] = decode( match[2] ); 581 } 582 583 return urlParams; 584 } 585 586 function create_account_toggle() { 587 588 if ( $( '.opc_order_review' ).length ) { 589 590 var $p_create_account = $( 'p.create-account' ); 591 var $dif_create_account = $( 'div.create-account' ); 592 var create_account_p_original_visibility = $p_create_account.is( ":visible" ); 593 var create_account_div_original_visibility = $dif_create_account.is( ":visible" ); 594 595 if ( $( '.opc_order_review tfoot .recurring-totals' ).children().length > 0 ) { 596 597 // Hide create account option checkbox container if it was visible 598 if ( create_account_p_original_visibility ) { 599 $p_create_account.hide(); 600 } 601 602 // And to be safe ensure the checkbox is checked 603 $( '#createaccount' ).prop( 'checked', true ); 604 605 // If the fields weren't visible to begin with lets show them 606 if ( ! create_account_div_original_visibility ) { 607 $dif_create_account.show(); // show div 608 } 609 } else { 610 611 // If visible originally - lets make sure its visible once again 612 if ( 'no' === wc_checkout_params.wcopc_option_guest_checkout ) { 613 $p_create_account.hide(); 614 $dif_create_account.show(); 615 $( '#createaccount' ).prop( 'checked', true ); 616 } else { 617 $p_create_account.show(); 618 $dif_create_account.hide(); 619 $( '#createaccount' ).prop( 'checked', false ); 620 } 621 } 622 } 623 } 624 625 } 626); 627 628/*! jQuery visible 1.1.0 teamdf.com/jquery-plugins | teamdf.com/jquery-plugins/license */ 629(function(d){d.fn.visible = function(e,i){var a = d( this ).eq( 0 ),f = a.get( 0 ),c = d( window ),g = c.scrollTop();c = g + c.height();var b = a.offset().top,h = b + a.height();a = e === true ? h : b;b = e === true ? b : h;return ! ! (i === true ? f.offsetWidth * f.offsetHeight : true) && b <= c && a >= g}})( 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.