暫無描述

checkout.js 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728
  1. /* global wc_checkout_params */
  2. jQuery( function( $ ) {
  3. // wc_checkout_params is required to continue, ensure the object exists
  4. if ( typeof wc_checkout_params === 'undefined' ) {
  5. return false;
  6. }
  7. $.blockUI.defaults.overlayCSS.cursor = 'default';
  8. var wc_checkout_form = {
  9. updateTimer: false,
  10. dirtyInput: false,
  11. selectedPaymentMethod: false,
  12. xhr: false,
  13. $order_review: $( '#order_review' ),
  14. $checkout_form: $( 'form.checkout' ),
  15. init: function() {
  16. $( document.body ).on( 'update_checkout', this.update_checkout );
  17. $( document.body ).on( 'init_checkout', this.init_checkout );
  18. // Payment methods
  19. this.$checkout_form.on( 'click', 'input[name="payment_method"]', this.payment_method_selected );
  20. if ( $( document.body ).hasClass( 'woocommerce-order-pay' ) ) {
  21. this.$order_review.on( 'click', 'input[name="payment_method"]', this.payment_method_selected );
  22. this.$order_review.on( 'submit', this.submitOrder );
  23. this.$order_review.attr( 'novalidate', 'novalidate' );
  24. }
  25. // Prevent HTML5 validation which can conflict.
  26. this.$checkout_form.attr( 'novalidate', 'novalidate' );
  27. // Form submission
  28. this.$checkout_form.on( 'submit', this.submit );
  29. // Inline validation
  30. this.$checkout_form.on( 'input validate change', '.input-text, select, input:checkbox', this.validate_field );
  31. // Manual trigger
  32. this.$checkout_form.on( 'update', this.trigger_update_checkout );
  33. // Inputs/selects which update totals
  34. this.$checkout_form.on( 'change', 'select.shipping_method, input[name^="shipping_method"], #ship-to-different-address input, .update_totals_on_change select, .update_totals_on_change input[type="radio"], .update_totals_on_change input[type="checkbox"]', this.trigger_update_checkout ); // eslint-disable-line max-len
  35. this.$checkout_form.on( 'change', '.address-field select', this.input_changed );
  36. this.$checkout_form.on( 'change', '.address-field input.input-text, .update_totals_on_change input.input-text', this.maybe_input_changed ); // eslint-disable-line max-len
  37. this.$checkout_form.on( 'keydown', '.address-field input.input-text, .update_totals_on_change input.input-text', this.queue_update_checkout ); // eslint-disable-line max-len
  38. // Address fields
  39. this.$checkout_form.on( 'change', '#ship-to-different-address input', this.ship_to_different_address );
  40. // Trigger events
  41. this.$checkout_form.find( '#ship-to-different-address input' ).trigger( 'change' );
  42. this.init_payment_methods();
  43. // Update on page load
  44. if ( wc_checkout_params.is_checkout === '1' ) {
  45. $( document.body ).trigger( 'init_checkout' );
  46. }
  47. if ( wc_checkout_params.option_guest_checkout === 'yes' ) {
  48. $( 'input#createaccount' ).on( 'change', this.toggle_create_account ).trigger( 'change' );
  49. }
  50. },
  51. init_payment_methods: function() {
  52. var $payment_methods = $( '.woocommerce-checkout' ).find( 'input[name="payment_method"]' );
  53. // If there is one method, we can hide the radio input
  54. if ( 1 === $payment_methods.length ) {
  55. $payment_methods.eq(0).hide();
  56. }
  57. // If there was a previously selected method, check that one.
  58. if ( wc_checkout_form.selectedPaymentMethod ) {
  59. $( '#' + wc_checkout_form.selectedPaymentMethod ).prop( 'checked', true );
  60. }
  61. // If there are none selected, select the first.
  62. if ( 0 === $payment_methods.filter( ':checked' ).length ) {
  63. $payment_methods.eq(0).prop( 'checked', true );
  64. }
  65. // Get name of new selected method.
  66. var checkedPaymentMethod = $payment_methods.filter( ':checked' ).eq(0).prop( 'id' );
  67. if ( $payment_methods.length > 1 ) {
  68. // Hide open descriptions.
  69. $( 'div.payment_box:not(".' + checkedPaymentMethod + '")' ).filter( ':visible' ).slideUp( 0 );
  70. }
  71. // Trigger click event for selected method
  72. $payment_methods.filter( ':checked' ).eq(0).trigger( 'click' );
  73. },
  74. get_payment_method: function() {
  75. return wc_checkout_form.$checkout_form.find( 'input[name="payment_method"]:checked' ).val();
  76. },
  77. payment_method_selected: function( e ) {
  78. e.stopPropagation();
  79. if ( $( '.payment_methods input.input-radio' ).length > 1 ) {
  80. var target_payment_box = $( 'div.payment_box.' + $( this ).attr( 'ID' ) ),
  81. is_checked = $( this ).is( ':checked' );
  82. if ( is_checked && ! target_payment_box.is( ':visible' ) ) {
  83. $( 'div.payment_box' ).filter( ':visible' ).slideUp( 230 );
  84. if ( is_checked ) {
  85. target_payment_box.slideDown( 230 );
  86. }
  87. }
  88. } else {
  89. $( 'div.payment_box' ).show();
  90. }
  91. if ( $( this ).data( 'order_button_text' ) ) {
  92. $( '#place_order' ).text( $( this ).data( 'order_button_text' ) );
  93. } else {
  94. $( '#place_order' ).text( $( '#place_order' ).data( 'value' ) );
  95. }
  96. var selectedPaymentMethod = $( '.woocommerce-checkout input[name="payment_method"]:checked' ).attr( 'id' );
  97. if ( selectedPaymentMethod !== wc_checkout_form.selectedPaymentMethod ) {
  98. $( document.body ).trigger( 'payment_method_selected' );
  99. }
  100. wc_checkout_form.selectedPaymentMethod = selectedPaymentMethod;
  101. },
  102. toggle_create_account: function() {
  103. $( 'div.create-account' ).hide();
  104. if ( $( this ).is( ':checked' ) ) {
  105. // Ensure password is not pre-populated.
  106. $( '#account_password' ).val( '' ).trigger( 'change' );
  107. $( 'div.create-account' ).slideDown();
  108. }
  109. },
  110. init_checkout: function() {
  111. $( document.body ).trigger( 'update_checkout' );
  112. },
  113. maybe_input_changed: function( e ) {
  114. if ( wc_checkout_form.dirtyInput ) {
  115. wc_checkout_form.input_changed( e );
  116. }
  117. },
  118. input_changed: function( e ) {
  119. wc_checkout_form.dirtyInput = e.target;
  120. wc_checkout_form.maybe_update_checkout();
  121. },
  122. queue_update_checkout: function( e ) {
  123. var code = e.keyCode || e.which || 0;
  124. if ( code === 9 ) {
  125. return true;
  126. }
  127. wc_checkout_form.dirtyInput = this;
  128. wc_checkout_form.reset_update_checkout_timer();
  129. wc_checkout_form.updateTimer = setTimeout( wc_checkout_form.maybe_update_checkout, '1000' );
  130. },
  131. trigger_update_checkout: function() {
  132. wc_checkout_form.reset_update_checkout_timer();
  133. wc_checkout_form.dirtyInput = false;
  134. $( document.body ).trigger( 'update_checkout' );
  135. },
  136. maybe_update_checkout: function() {
  137. var update_totals = true;
  138. if ( $( wc_checkout_form.dirtyInput ).length ) {
  139. var $required_inputs = $( wc_checkout_form.dirtyInput ).closest( 'div' ).find( '.address-field.validate-required' );
  140. if ( $required_inputs.length ) {
  141. $required_inputs.each( function() {
  142. if ( $( this ).find( 'input.input-text' ).val() === '' ) {
  143. update_totals = false;
  144. }
  145. });
  146. }
  147. }
  148. if ( update_totals ) {
  149. wc_checkout_form.trigger_update_checkout();
  150. }
  151. },
  152. ship_to_different_address: function() {
  153. $( 'div.shipping_address' ).hide();
  154. if ( $( this ).is( ':checked' ) ) {
  155. $( 'div.shipping_address' ).slideDown();
  156. }
  157. },
  158. reset_update_checkout_timer: function() {
  159. clearTimeout( wc_checkout_form.updateTimer );
  160. },
  161. is_valid_json: function( raw_json ) {
  162. try {
  163. var json = JSON.parse( raw_json );
  164. return ( json && 'object' === typeof json );
  165. } catch ( e ) {
  166. return false;
  167. }
  168. },
  169. validate_field: function( e ) {
  170. var $this = $( this ),
  171. $parent = $this.closest( '.form-row' ),
  172. validated = true,
  173. validate_required = $parent.is( '.validate-required' ),
  174. validate_email = $parent.is( '.validate-email' ),
  175. validate_phone = $parent.is( '.validate-phone' ),
  176. pattern = '',
  177. event_type = e.type;
  178. if ( 'input' === event_type ) {
  179. $parent.removeClass( 'woocommerce-invalid woocommerce-invalid-required-field woocommerce-invalid-email woocommerce-invalid-phone woocommerce-validated' ); // eslint-disable-line max-len
  180. }
  181. if ( 'validate' === event_type || 'change' === event_type ) {
  182. if ( validate_required ) {
  183. if ( 'checkbox' === $this.attr( 'type' ) && ! $this.is( ':checked' ) ) {
  184. $parent.removeClass( 'woocommerce-validated' ).addClass( 'woocommerce-invalid woocommerce-invalid-required-field' );
  185. validated = false;
  186. } else if ( $this.val() === '' ) {
  187. $parent.removeClass( 'woocommerce-validated' ).addClass( 'woocommerce-invalid woocommerce-invalid-required-field' );
  188. validated = false;
  189. }
  190. }
  191. if ( validate_email ) {
  192. if ( $this.val() ) {
  193. /* https://stackoverflow.com/questions/2855865/jquery-validate-e-mail-address-regex */
  194. pattern = new RegExp( /^([a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+(\.[a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)*|"((([ \t]*\r\n)?[ \t]+)?([\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*(([ \t]*\r\n)?[ \t]+)?")@(([a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.)+([a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[0-9a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.?$/i ); // eslint-disable-line max-len
  195. if ( ! pattern.test( $this.val() ) ) {
  196. $parent.removeClass( 'woocommerce-validated' ).addClass( 'woocommerce-invalid woocommerce-invalid-email woocommerce-invalid-phone' ); // eslint-disable-line max-len
  197. validated = false;
  198. }
  199. }
  200. }
  201. if ( validate_phone ) {
  202. pattern = new RegExp( /[\s\#0-9_\-\+\/\(\)\.]/g );
  203. if ( 0 < $this.val().replace( pattern, '' ).length ) {
  204. $parent.removeClass( 'woocommerce-validated' ).addClass( 'woocommerce-invalid woocommerce-invalid-phone' );
  205. validated = false;
  206. }
  207. }
  208. if ( validated ) {
  209. $parent.removeClass( 'woocommerce-invalid woocommerce-invalid-required-field woocommerce-invalid-email woocommerce-invalid-phone' ).addClass( 'woocommerce-validated' ); // eslint-disable-line max-len
  210. }
  211. }
  212. },
  213. update_checkout: function( event, args ) {
  214. // Small timeout to prevent multiple requests when several fields update at the same time
  215. wc_checkout_form.reset_update_checkout_timer();
  216. wc_checkout_form.updateTimer = setTimeout( wc_checkout_form.update_checkout_action, '5', args );
  217. },
  218. update_checkout_action: function( args ) {
  219. if ( wc_checkout_form.xhr ) {
  220. wc_checkout_form.xhr.abort();
  221. }
  222. if ( $( 'form.checkout' ).length === 0 ) {
  223. return;
  224. }
  225. args = typeof args !== 'undefined' ? args : {
  226. update_shipping_method: true
  227. };
  228. var country = $( '#billing_country' ).val(),
  229. state = $( '#billing_state' ).val(),
  230. postcode = $( ':input#billing_postcode' ).val(),
  231. city = $( '#billing_city' ).val(),
  232. address = $( ':input#billing_address_1' ).val(),
  233. address_2 = $( ':input#billing_address_2' ).val(),
  234. s_country = country,
  235. s_state = state,
  236. s_postcode = postcode,
  237. s_city = city,
  238. s_address = address,
  239. s_address_2 = address_2,
  240. $required_inputs = $( wc_checkout_form.$checkout_form ).find( '.address-field.validate-required:visible' ),
  241. has_full_address = true;
  242. if ( $required_inputs.length ) {
  243. $required_inputs.each( function() {
  244. if ( $( this ).find( ':input' ).val() === '' ) {
  245. has_full_address = false;
  246. }
  247. });
  248. }
  249. if ( $( '#ship-to-different-address' ).find( 'input' ).is( ':checked' ) ) {
  250. s_country = $( '#shipping_country' ).val();
  251. s_state = $( '#shipping_state' ).val();
  252. s_postcode = $( ':input#shipping_postcode' ).val();
  253. s_city = $( '#shipping_city' ).val();
  254. s_address = $( ':input#shipping_address_1' ).val();
  255. s_address_2 = $( ':input#shipping_address_2' ).val();
  256. }
  257. var data = {
  258. security : wc_checkout_params.update_order_review_nonce,
  259. payment_method : wc_checkout_form.get_payment_method(),
  260. country : country,
  261. state : state,
  262. postcode : postcode,
  263. city : city,
  264. address : address,
  265. address_2 : address_2,
  266. s_country : s_country,
  267. s_state : s_state,
  268. s_postcode : s_postcode,
  269. s_city : s_city,
  270. s_address : s_address,
  271. s_address_2 : s_address_2,
  272. has_full_address: has_full_address,
  273. post_data : $( 'form.checkout' ).serialize()
  274. };
  275. if ( false !== args.update_shipping_method ) {
  276. var shipping_methods = {};
  277. // eslint-disable-next-line max-len
  278. $( 'select.shipping_method, input[name^="shipping_method"][type="radio"]:checked, input[name^="shipping_method"][type="hidden"]' ).each( function() {
  279. shipping_methods[ $( this ).data( 'index' ) ] = $( this ).val();
  280. } );
  281. data.shipping_method = shipping_methods;
  282. }
  283. $( '.woocommerce-checkout-payment, .woocommerce-checkout-review-order-table' ).block({
  284. message: null,
  285. overlayCSS: {
  286. background: '#fff',
  287. opacity: 0.6
  288. }
  289. });
  290. wc_checkout_form.xhr = $.ajax({
  291. type: 'POST',
  292. url: wc_checkout_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'update_order_review' ),
  293. data: data,
  294. success: function( data ) {
  295. // Reload the page if requested
  296. if ( data && true === data.reload ) {
  297. window.location.reload();
  298. return;
  299. }
  300. // Remove any notices added previously
  301. $( '.woocommerce-NoticeGroup-updateOrderReview' ).remove();
  302. var termsCheckBoxChecked = $( '#terms' ).prop( 'checked' );
  303. // Save payment details to a temporary object
  304. var paymentDetails = {};
  305. $( '.payment_box :input' ).each( function() {
  306. var ID = $( this ).attr( 'id' );
  307. if ( ID ) {
  308. if ( $.inArray( $( this ).attr( 'type' ), [ 'checkbox', 'radio' ] ) !== -1 ) {
  309. paymentDetails[ ID ] = $( this ).prop( 'checked' );
  310. } else {
  311. paymentDetails[ ID ] = $( this ).val();
  312. }
  313. }
  314. });
  315. // Always update the fragments
  316. if ( data && data.fragments ) {
  317. $.each( data.fragments, function ( key, value ) {
  318. if ( ! wc_checkout_form.fragments || wc_checkout_form.fragments[ key ] !== value ) {
  319. $( key ).replaceWith( value );
  320. }
  321. $( key ).unblock();
  322. } );
  323. wc_checkout_form.fragments = data.fragments;
  324. }
  325. // Recheck the terms and conditions box, if needed
  326. if ( termsCheckBoxChecked ) {
  327. $( '#terms' ).prop( 'checked', true );
  328. }
  329. // Fill in the payment details if possible without overwriting data if set.
  330. if ( ! $.isEmptyObject( paymentDetails ) ) {
  331. $( '.payment_box :input' ).each( function() {
  332. var ID = $( this ).attr( 'id' );
  333. if ( ID ) {
  334. if ( $.inArray( $( this ).attr( 'type' ), [ 'checkbox', 'radio' ] ) !== -1 ) {
  335. $( this ).prop( 'checked', paymentDetails[ ID ] ).trigger( 'change' );
  336. } else if ( $.inArray( $( this ).attr( 'type' ), [ 'select' ] ) !== -1 ) {
  337. $( this ).val( paymentDetails[ ID ] ).trigger( 'change' );
  338. } else if ( null !== $( this ).val() && 0 === $( this ).val().length ) {
  339. $( this ).val( paymentDetails[ ID ] ).trigger( 'change' );
  340. }
  341. }
  342. });
  343. }
  344. // Check for error
  345. if ( data && 'failure' === data.result ) {
  346. var $form = $( 'form.checkout' );
  347. // Remove notices from all sources
  348. $( '.woocommerce-error, .woocommerce-message' ).remove();
  349. // Add new errors returned by this event
  350. if ( data.messages ) {
  351. $form.prepend( '<div class="woocommerce-NoticeGroup woocommerce-NoticeGroup-updateOrderReview">' + data.messages + '</div>' ); // eslint-disable-line max-len
  352. } else {
  353. $form.prepend( data );
  354. }
  355. // Lose focus for all fields
  356. $form.find( '.input-text, select, input:checkbox' ).trigger( 'validate' ).trigger( 'blur' );
  357. wc_checkout_form.scroll_to_notices();
  358. }
  359. // Re-init methods
  360. wc_checkout_form.init_payment_methods();
  361. // Fire updated_checkout event.
  362. $( document.body ).trigger( 'updated_checkout', [ data ] );
  363. }
  364. });
  365. },
  366. handleUnloadEvent: function( e ) {
  367. // Modern browsers have their own standard generic messages that they will display.
  368. // Confirm, alert, prompt or custom message are not allowed during the unload event
  369. // Browsers will display their own standard messages
  370. // Check if the browser is Internet Explorer
  371. if((navigator.userAgent.indexOf('MSIE') !== -1 ) || (!!document.documentMode)) {
  372. // IE handles unload events differently than modern browsers
  373. e.preventDefault();
  374. return undefined;
  375. }
  376. return true;
  377. },
  378. attachUnloadEventsOnSubmit: function() {
  379. $( window ).on('beforeunload', this.handleUnloadEvent);
  380. },
  381. detachUnloadEventsOnSubmit: function() {
  382. $( window ).off('beforeunload', this.handleUnloadEvent);
  383. },
  384. blockOnSubmit: function( $form ) {
  385. var isBlocked = $form.data( 'blockUI.isBlocked' );
  386. if ( 1 !== isBlocked ) {
  387. $form.block({
  388. message: null,
  389. overlayCSS: {
  390. background: '#fff',
  391. opacity: 0.6
  392. }
  393. });
  394. }
  395. },
  396. submitOrder: function() {
  397. wc_checkout_form.blockOnSubmit( $( this ) );
  398. },
  399. submit: function() {
  400. wc_checkout_form.reset_update_checkout_timer();
  401. var $form = $( this );
  402. if ( $form.is( '.processing' ) ) {
  403. return false;
  404. }
  405. // Trigger a handler to let gateways manipulate the checkout if needed
  406. // eslint-disable-next-line max-len
  407. if ( $form.triggerHandler( 'checkout_place_order' ) !== false && $form.triggerHandler( 'checkout_place_order_' + wc_checkout_form.get_payment_method() ) !== false ) {
  408. $form.addClass( 'processing' );
  409. wc_checkout_form.blockOnSubmit( $form );
  410. // Attach event to block reloading the page when the form has been submitted
  411. wc_checkout_form.attachUnloadEventsOnSubmit();
  412. // ajaxSetup is global, but we use it to ensure JSON is valid once returned.
  413. $.ajaxSetup( {
  414. dataFilter: function( raw_response, dataType ) {
  415. // We only want to work with JSON
  416. if ( 'json' !== dataType ) {
  417. return raw_response;
  418. }
  419. if ( wc_checkout_form.is_valid_json( raw_response ) ) {
  420. return raw_response;
  421. } else {
  422. // Attempt to fix the malformed JSON
  423. var maybe_valid_json = raw_response.match( /{"result.*}/ );
  424. if ( null === maybe_valid_json ) {
  425. console.log( 'Unable to fix malformed JSON' );
  426. } else if ( wc_checkout_form.is_valid_json( maybe_valid_json[0] ) ) {
  427. console.log( 'Fixed malformed JSON. Original:' );
  428. console.log( raw_response );
  429. raw_response = maybe_valid_json[0];
  430. } else {
  431. console.log( 'Unable to fix malformed JSON' );
  432. }
  433. }
  434. return raw_response;
  435. }
  436. } );
  437. $.ajax({
  438. type: 'POST',
  439. url: wc_checkout_params.checkout_url,
  440. data: $form.serialize(),
  441. dataType: 'json',
  442. success: function( result ) {
  443. // Detach the unload handler that prevents a reload / redirect
  444. wc_checkout_form.detachUnloadEventsOnSubmit();
  445. try {
  446. if ( 'success' === result.result && $form.triggerHandler( 'checkout_place_order_success', result ) !== false ) {
  447. if ( -1 === result.redirect.indexOf( 'https://' ) || -1 === result.redirect.indexOf( 'http://' ) ) {
  448. window.location = result.redirect;
  449. } else {
  450. window.location = decodeURI( result.redirect );
  451. }
  452. } else if ( 'failure' === result.result ) {
  453. throw 'Result failure';
  454. } else {
  455. throw 'Invalid response';
  456. }
  457. } catch( err ) {
  458. // Reload page
  459. if ( true === result.reload ) {
  460. window.location.reload();
  461. return;
  462. }
  463. // Trigger update in case we need a fresh nonce
  464. if ( true === result.refresh ) {
  465. $( document.body ).trigger( 'update_checkout' );
  466. }
  467. // Add new errors
  468. if ( result.messages ) {
  469. wc_checkout_form.submit_error( result.messages );
  470. } else {
  471. wc_checkout_form.submit_error( '<div class="woocommerce-error">' + wc_checkout_params.i18n_checkout_error + '</div>' ); // eslint-disable-line max-len
  472. }
  473. }
  474. },
  475. error: function( jqXHR, textStatus, errorThrown ) {
  476. // Detach the unload handler that prevents a reload / redirect
  477. wc_checkout_form.detachUnloadEventsOnSubmit();
  478. wc_checkout_form.submit_error( '<div class="woocommerce-error">' + errorThrown + '</div>' );
  479. }
  480. });
  481. }
  482. return false;
  483. },
  484. submit_error: function( error_message ) {
  485. $( '.woocommerce-NoticeGroup-checkout, .woocommerce-error, .woocommerce-message' ).remove();
  486. wc_checkout_form.$checkout_form.prepend( '<div class="woocommerce-NoticeGroup woocommerce-NoticeGroup-checkout">' + error_message + '</div>' ); // eslint-disable-line max-len
  487. wc_checkout_form.$checkout_form.removeClass( 'processing' ).unblock();
  488. wc_checkout_form.$checkout_form.find( '.input-text, select, input:checkbox' ).trigger( 'validate' ).trigger( 'blur' );
  489. wc_checkout_form.scroll_to_notices();
  490. $( document.body ).trigger( 'checkout_error' , [ error_message ] );
  491. },
  492. scroll_to_notices: function() {
  493. var scrollElement = $( '.woocommerce-NoticeGroup-updateOrderReview, .woocommerce-NoticeGroup-checkout' );
  494. if ( ! scrollElement.length ) {
  495. scrollElement = $( '.form.checkout' );
  496. }
  497. $.scroll_to_notices( scrollElement );
  498. }
  499. };
  500. var wc_checkout_coupons = {
  501. init: function() {
  502. $( document.body ).on( 'click', 'a.showcoupon', this.show_coupon_form );
  503. $( document.body ).on( 'click', '.woocommerce-remove-coupon', this.remove_coupon );
  504. $( 'form.checkout_coupon' ).hide().on( 'submit', this.submit );
  505. },
  506. show_coupon_form: function() {
  507. $( '.checkout_coupon' ).slideToggle( 400, function() {
  508. $( '.checkout_coupon' ).find( ':input:eq(0)' ).trigger( 'focus' );
  509. });
  510. return false;
  511. },
  512. submit: function() {
  513. var $form = $( this );
  514. if ( $form.is( '.processing' ) ) {
  515. return false;
  516. }
  517. $form.addClass( 'processing' ).block({
  518. message: null,
  519. overlayCSS: {
  520. background: '#fff',
  521. opacity: 0.6
  522. }
  523. });
  524. var data = {
  525. security: wc_checkout_params.apply_coupon_nonce,
  526. coupon_code: $form.find( 'input[name="coupon_code"]' ).val()
  527. };
  528. $.ajax({
  529. type: 'POST',
  530. url: wc_checkout_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'apply_coupon' ),
  531. data: data,
  532. success: function( code ) {
  533. $( '.woocommerce-error, .woocommerce-message' ).remove();
  534. $form.removeClass( 'processing' ).unblock();
  535. if ( code ) {
  536. $form.before( code );
  537. $form.slideUp();
  538. $( document.body ).trigger( 'applied_coupon_in_checkout', [ data.coupon_code ] );
  539. $( document.body ).trigger( 'update_checkout', { update_shipping_method: false } );
  540. }
  541. },
  542. dataType: 'html'
  543. });
  544. return false;
  545. },
  546. remove_coupon: function( e ) {
  547. e.preventDefault();
  548. var container = $( this ).parents( '.woocommerce-checkout-review-order' ),
  549. coupon = $( this ).data( 'coupon' );
  550. container.addClass( 'processing' ).block({
  551. message: null,
  552. overlayCSS: {
  553. background: '#fff',
  554. opacity: 0.6
  555. }
  556. });
  557. var data = {
  558. security: wc_checkout_params.remove_coupon_nonce,
  559. coupon: coupon
  560. };
  561. $.ajax({
  562. type: 'POST',
  563. url: wc_checkout_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'remove_coupon' ),
  564. data: data,
  565. success: function( code ) {
  566. $( '.woocommerce-error, .woocommerce-message' ).remove();
  567. container.removeClass( 'processing' ).unblock();
  568. if ( code ) {
  569. $( 'form.woocommerce-checkout' ).before( code );
  570. $( document.body ).trigger( 'removed_coupon_in_checkout', [ data.coupon_code ] );
  571. $( document.body ).trigger( 'update_checkout', { update_shipping_method: false } );
  572. // Remove coupon code from coupon field
  573. $( 'form.checkout_coupon' ).find( 'input[name="coupon_code"]' ).val( '' );
  574. }
  575. },
  576. error: function ( jqXHR ) {
  577. if ( wc_checkout_params.debug_mode ) {
  578. /* jshint devel: true */
  579. console.log( jqXHR.responseText );
  580. }
  581. },
  582. dataType: 'html'
  583. });
  584. }
  585. };
  586. var wc_checkout_login_form = {
  587. init: function() {
  588. $( document.body ).on( 'click', 'a.showlogin', this.show_login_form );
  589. },
  590. show_login_form: function() {
  591. $( 'form.login, form.woocommerce-form--login' ).slideToggle();
  592. return false;
  593. }
  594. };
  595. var wc_terms_toggle = {
  596. init: function() {
  597. $( document.body ).on( 'click', 'a.woocommerce-terms-and-conditions-link', this.toggle_terms );
  598. },
  599. toggle_terms: function() {
  600. if ( $( '.woocommerce-terms-and-conditions' ).length ) {
  601. $( '.woocommerce-terms-and-conditions' ).slideToggle( function() {
  602. var link_toggle = $( '.woocommerce-terms-and-conditions-link' );
  603. if ( $( '.woocommerce-terms-and-conditions' ).is( ':visible' ) ) {
  604. link_toggle.addClass( 'woocommerce-terms-and-conditions-link--open' );
  605. link_toggle.removeClass( 'woocommerce-terms-and-conditions-link--closed' );
  606. } else {
  607. link_toggle.removeClass( 'woocommerce-terms-and-conditions-link--open' );
  608. link_toggle.addClass( 'woocommerce-terms-and-conditions-link--closed' );
  609. }
  610. } );
  611. return false;
  612. }
  613. }
  614. };
  615. wc_checkout_form.init();
  616. wc_checkout_coupons.init();
  617. wc_checkout_login_form.init();
  618. wc_terms_toggle.init();
  619. });