1var flex = (typeof flex !== 'undefined') ? flex : {}; 2 3flex.checkout = (typeof flex.checkout !== 'undefined') ? flex.checkout : {}; 4flex.checkout.locationSelect = function($select, callback) { 5 callback = typeof callback !== 'undefined' ? callback : function($el) {}; 6 var location = { 7 _perPage: 20, 8 init: function() { 9 var self = this; 10 self.$select = $select; 11 self.$select.select2(self._select2Option('- '+i18n.gettext('Select Location')+' -')); 12 flex.applySelect2Aria(self.$select); 13 14 self.$select.val(self.$select.data('selected')).trigger('change'); 15 16 self.$select.on('change', function() { 17 callback($(this)); 18 }); 19 20 // identification that this was already initiated 21 self.$select.data('flex.checkout.locationSelect', 1); 22 }, 23 _select2Option: function(placeHolder) { 24 var self = this; 25 return { 26 placeholder: placeHolder, 27 ajax: { 28 url: baseUrl + "order/cart/locations", 29 delay: 250, 30 dataType: 'json', 31 data: function (params) { // page is the one-based page number tracked by Select2 32 $this = $(this); 33 return { 34 q: params.term, //search term 35 page: params.page, // page number 36 per_page: self._perPage, 37 for_pickup: 1 38 }; 39 }, 40 processResults: function (data, params) { 41 return { 42 results: data.items, 43 pagination: { 44 more: ((params.page ? params.page : 1) * self._perPage) < data.total_count 45 } 46 }; 47 } 48 }, 49 templateResult: self._formatResult, // omitted for brevity, see the source of this page 50 templateSelection: self._formatSelection, // omitted for brevity, see the source of this page 51 escapeMarkup: function (m) { 52 return m; 53 }, // we do not want to escape markup since we are displaying html in results 54 language: { 55 errorLoading: function () { 56 return i18n.gettext("Searching..."); 57 } 58 } 59 } 60 }, 61 62 _formatResult: function (result) { 63 if (result.loading) { 64 return flex.utils.e(result.text); 65 } 66 67 var markup = '<div class="row-fluid">' + 68 '<div class="span12">' + 69 '<div class="row-fluid">'; 70 markup += '<div class="span12"> <b>' + flex.utils.e(result.title) + '</b>'; 71 72 markup += ''; 73 var footerMarkup = ''; 74 var address = ''; 75 if (result.street) { 76 address += flex.utils.e(result.street); 77 } 78 79 if (result.state) { 80 address += ', ' + flex.utils.e(result.state); 81 } 82 83 if (result.postcode) { 84 address += ', ' + flex.utils.e(result.postcode); 85 } 86 87 if (result.phone) { 88 address += '<br/>P: ' + flex.utils.e(result.phone); 89 } 90 91 92 footerMarkup += '<small class="small">' + address + '</small>'; 93 94 markup += '<br />' + footerMarkup; 95 96 markup += '</div></div></div>'; 97 98 return markup; 99 }, 100 101 _formatSelection: function (item) { 102 return flex.utils.e(item.title ? item.title : item.text); 103 }, 104 }; 105 106 location.init(); 107}; 108 109flex.checkout.newLocationSelect = function($modal, showClose = false) { 110 var locationsList = { 111 _perPage: 10, 112 _totalPages: 0, 113 _currentPage: 0, 114 _isLoading: false, 115 init: function() { 116 var self = this; 117 self.xhr = null; 118 119 self.initModal(); 120 self.getLocationsList(); 121 self.initScrollView(); 122 self.updateContinueButton(); 123 124 self.getSearchButtonEl().on('click touchend', function (event) { 125 event.preventDefault(); 126 self.getLocationsList(self.getSearchFieldEl().val()); 127 }); 128 129 self.getSearchFieldEl().on('keypress', function (event) { 130 if (event.which === 13) { 131 event.preventDefault(); 132 self.getLocationsList(self.getSearchFieldEl().val()); 133 } 134 }); 135 136 self.getMethodFilterEl().on('click keydown', '.list-item', function (e) { 137 if (e.type === 'keydown' && e.which !== 13 && e.which !== 32) { 138 return; 139 } 140 141 e.preventDefault(); 142 self.setMethodFilterActive($(this)); 143 self.getLocationsList(self.getSearchFieldEl().val()); 144 }); 145 146 $modal.on('change', '.js-input-select-location', function () { 147 self.updateContinueButton(); 148 }); 149 150 self.getContinueButtonEl().on('click touchend', function (event) { 151 event.preventDefault(); 152 $(this).closest('form').submit(); 153 }); 154 155 if (showClose) { 156 $modal.find('.close').show(); 157 } else { 158 $modal.find('.close').hide(); 159 } 160 }, 161 initModal: function () { 162 $modal.modal('show'); 163 }, 164 getSearchFieldEl: function () { 165 return $modal.find('.js-search-location-field-modal'); 166 }, 167 getSearchButtonEl: function () { 168 return $modal.find('.js-search-location-modal-btn'); 169 }, 170 getMethodFilterEl: function () { 171 return $modal.find('.js-method-filter'); 172 }, 173 getLocationsContainerEl: function () { 174 return $modal.find('.js-location-container'); 175 }, 176 getLocationSpinnerEl: function () { 177 return $modal.find('.js-location-modal-loader'); 178 }, 179 getLocationStatusEl: function () { 180 return $modal.find('.js-location-modal-status'); 181 }, 182 getContinueButtonEl: function () { 183 return $modal.find('.js-submit-select-location'); 184 }, 185 getButtonContainerEl: function () { 186 return $modal.find('.js-location-btn-container'); 187 }, 188 189 getMethodFilterValue: function() { 190 return this.getMethodFilterEl().find('.list-item.active').data('value'); 191 }, 192 setMethodFilterActive: function($item) { 193 var $items = this.getMethodFilterEl().find('.list-item'); 194 195 $items.removeClass('active').attr('aria-pressed', 'false'); 196 $item.addClass('active').attr('aria-pressed', 'true'); 197 },
198 setLoadingState: function(isLoading) { 199 this.getLocationsContainerEl().attr('aria-busy', isLoading ? 'true' : 'false'); 200 201 if (isLoading) { 202 this.setLocationStatus(i18n.gettext("Loading locations.")); 203 } 204 }, 205 setLocationStatus: function(message) { 206 this.getLocationStatusEl().text(message); 207 }, 208 updateContinueButton: function() { 209 var hasSelectedLocation = this.getLocationsContainerEl().find('.js-input-select-location:checked').length > 0; 210 211 this.getContinueButtonEl() 212 .prop('disabled', !hasSelectedLocation) 213 .attr('aria-disabled', hasSelectedLocation ? 'false' : 'true'); 214 }, 215 216 getLocationsList: function (keyword = null, page = 1) { 217 var self = this, 218 spinnerEl = self.getLocationSpinnerEl(), 219 queryData = {}; 220 221 var filteredMethod = self.getMethodFilterValue(); 222 223 if (keyword != null && keyword != '') { 224 queryData = { 225 address: keyword 226 }; 227 } else { 228 queryData = {page: page, per_page: (filteredMethod? 1000: self._perPage)}; 229 } 230 231 if (self._isLoading) { 232 return; 233 } 234 235 if (self._currentPage == 0 || keyword != null) { 236 self.getLocationsContainerEl().empty(); 237 self.updateContinueButton(); 238 } 239 240 self._isLoading = true; 241 self.setLoadingState(true); 242 243 abortAjax(self.xhr); 244 spinnerEl.show(); 245 246 self.xhr = $.ajax({ 247 type: 'GET', 248 url: baseUrl + 'order/cart/locations', 249 data: queryData, 250 dataType: 'json', 251 success: function (data) { 252 253 if ('pagination' in data) { 254 self._totalPages = data.pagination.total_pages; 255 self._currentPage = data.pagination.page; 256 } else { 257 self._totalPages = 0; 258 self._currentPage = 0; 259 } 260 261 self.formatDataAndDisplay(data); 262 self.setLoadingState(false); 263 }, 264 error: function (xhr, textStatus) { 265 if (textStatus !== 'abort') { 266 self.setLoadingState(false); 267 self.setLocationStatus(i18n.gettext("Unable to load locations.")); 268 } 269 }, 270 complete: function () { 271 self._isLoading = false; 272 spinnerEl.hide(); 273 } 274 }); 275 }, 276 formatDataAndDisplay: function (data) { 277 var self = this; 278 var filteredMethod = self.getMethodFilterValue(); 279 280 let validItems = data.items.filter(function(location) { 281 if (filteredMethod === 'delivery' && !location.is_enable_delivery) return false; 282 if (filteredMethod === 'pickup' && !location.is_enable_pickup) return false; 283 return true; 284 }); 285 286 if (validItems.length) { 287 validItems.forEach(function(location) { 288 let distanceToSearch = ''; 289 if (location.distance_to_search) { 290 distanceToSearch = `<span class="location-distance mb-2">${flex.utils.e(location.distance_to_search)}</span>`; 291 } 292 293 var hasMethod = location.is_enable_delivery || location.is_enable_pickup; 294 295 let deliveryBadge = location.is_enable_delivery 296 ? `<div class="btn-badge">${i18n.gettext("Delivery")}</div>` 297 : ""; 298 299 let pickupBadge = location.is_enable_pickup 300 ? `<div class="btn-badge">${i18n.gettext("Pick Up")}</div>` 301 : ""; 302 303 let badges = (deliveryBadge || pickupBadge) 304 ? `<div class="shipping-method"> 305 ${deliveryBadge} 306 ${pickupBadge} 307 </div>` 308 : ""; 309 310 let locationInputId = 'checkout-location-' + location.id; 311 let locationTabIndex = hasMethod ? '0' : '-1'; 312 let locationAriaDisabled = hasMethod ? 'false' : 'true'; 313 let locationDisabledAttribute = hasMethod ? '' : 'disabled'; 314 315 let locationItem = `<div class="checkbox-card3 checkbox-card3-border
315checkbox-card3-location" > 316 <label class="checkbox-label" tabindex="${locationTabIndex}" role="radio" aria-checked="false" aria-disabled="${locationAriaDisabled}" for="${locationInputId}"> 317 <input 318 id="${locationInputId}" 319 value="${location.id}" 320 name="location_id" 321 type="radio" class="js-input-select-location" 322 tabindex="-1" 323 required 324 ${locationDisabledAttribute} 325 > 326 <div class="checkbox-text"> 327 <div class="checkbox-text-inner"> 328 <strong class="${!location.distance_to_search ? 'mb-2' : ''}">${flex.utils.e(location.title)}</strong> 329 ${distanceToSearch} 330 <span>${flex.utils.e(location.address_full_wo_country)}</span> 331 <span>${flex.utils.e(location.phone)}</span> 332 </div> 333 ${badges} 334 </div> 335 </label> 336 </div>`; 337 338 self.getLocationsContainerEl().append($(locationItem)); 339 }); 340 341 self.getButtonContainerEl().show(); 342 self.setLocationStatus(i18n.gettext("Locations loaded.")); 343 } else { 344 self.getLocationsContainerEl().append(`<span>${i18n.gettext("No matching locations found.")}</span>`); 345 self.getButtonContainerEl().hide(); 346 self.setLocationStatus(i18n.gettext("No matching locations found.")); 347 } 348 349 self.updateContinueButton(); 350 }, 351 initScrollView: function () { 352 let self = this; 353 let $container = self.getLocationsContainerEl(); 354 355 // Use native element reference for standard dimension calls 356 let containerEl = $container[0]; 357 358 $container.on('scroll', function (event) { 359 // DO NOT use event.preventDefault() or event.stopPropagation() here; 360 // it breaks scroll bubbling in legacy Android WebViews. 361 362 // Use native JS properties for accurate height measurements 363 var containerHeight = containerEl.clientHeight; 364 var scrollTop = containerEl.scrollTop; 365 var scrollHeight = containerEl.scrollHeight; 366 367 // Add a 10px buffer to handle Android 9 sub-pixel fractional rounding bugs 368 if (scrollTop + containerHeight >= scrollHeight - 10) { 369 // Check scroll bounds and loading lock state 370 if (!self._isLoading && parseInt(self._currentPage, 10) < parseInt(self._totalPages, 10)) { 371 self.getLocationsList(null, parseInt(self._currentPage, 10) + 1); 372 } 373 } 374 }); 375 } 376 }; 377 378 locationsList.init(); 379}; 380 381window.checkoutForm = { 382 checkoutCaptcha: null, 383 _deferInitServiceFee: false, 384 _paymentInitAjaxPosted: false, 385 386 abortCheckoutAjax: function (key) { 387 var xhr = window['xhrCheckout' + key]; 388 389 if (xhr) { 390 xhr.abort(); 391 } 392 }, 393 394 setCheckoutAjax: function (key, jqXHR) { 395 window['xhrCheckout' + key] = jqXHR; 396 }, 397 398 finishDeferredInitServiceFee: function () { 399 if (!this._deferInitServiceFee) { 400 return; 401 } 402 403 if ($('.js-payment-form-wrapper-pay-now').length) { 404 return; 405 } 406 407 this._deferInitServiceFee = false; 408 this.runInitServiceFeeAutoSelect(true); 409 }, 410 411 init: function() { 412 var self = this, 413 $body = $('body'), 414 $checkoutForm = this.getCheckoutFormEl(); 415 416 var $wrapper = this.getWrapperEl(); 417 418 if ($checkoutForm && $checkoutForm.length) {
419 var $shipDifferent = this.getShipDifferentAddressEl(); 420 if ($shipDifferent.length) { 421 $shipDifferent.change(function (e) { 422 if ($(this).is(':checked')) { 423 $checkoutForm.find('#shipping-details').show(); 424 $checkoutForm.find('.js-same-as-billing').val(0); 425 } else { 426 $checkoutForm.find('#shipping-details').hide(); 427 $checkoutForm.find('.js-same-as-billing').val(1); 428 } 429 430 }).trigger('change'); 431 } 432 433 $body.on('change', '.js-address-update', function () { 434 self.updateOrderDetails(); 435 }); 436 437 $body.on('change', '.js-input-update-summary', function (e) { 438 self.updateOrderDetails(); 439 }); 440 441 $body.on('change', '[name="shipping_method"]', function (e) { 442 self.updateOrderShippingMethod(); 443 }); 444 445 $body.on('change', '[name="payment_method"]', function (e) { 446 self.reEvaluatePaymentMethodBillingAddress(); 447 self.updateOrderPaymentMethod(); 448 }); 449 450 $body.on('change', 'input[name="service_fee_option"]', function (e) { 451 if ($(e.currentTarget).closest('.js-payment-form-wrapper-pay-now').length) { 452 return; 453 } 454 self.handleServiceFeeOptionChange(e, self.getServiceFeeSlotConfig(1)); 455 }); 456 457 $body.on('change keyup', 'input[name="user_service_fee_price"]', function (e) { 458 if ($(e.currentTarget).closest('.js-payment-form-wrapper-pay-now').length) { 459 return; 460 } 461 flex.utils.debounce('user_service_fee_price_keyup', function () { 462 self.handleServiceFeeCustomAmountChange(self.getServiceFeeSlotConfig(1)); 463 }, 800); 464 }); 465 466 $body.on('change', 'input[name="service_fee_2_option"]', function (e) { 467 if ($(e.currentTarget).closest('.js-payment-form-wrapper-pay-now').length) { 468 return; 469 } 470 self.handleServiceFeeOptionChange(e, self.getServiceFeeSlotConfig(2)); 471 }); 472 473 $body.on('change keyup', 'input[name="user_service_fee_2_price"]', function (e) { 474 if ($(e.currentTarget).closest('.js-payment-form-wrapper-pay-now').length) { 475 return; 476 } 477 flex.utils.debounce('user_service_fee_price_2_keyup', function () { 478 self.handleServiceFeeCustomAmountChange(self.getServiceFeeSlotConfig(2)); 479 }, 800); 480 }); 481 482 $body.on('change', 'input[name="service_fee_3_option"]', function (e) { 483 if ($(e.currentTarget).closest('.js-payment-form-wrapper-pay-now').length) { 484 return; 485 } 486 self.handleServiceFeeOptionChange(e, self.getServiceFeeSlotConfig(3)); 487 }); 488 489 $body.on('change keyup', 'input[name="user_service_fee_3_price"]', function (e) { 490 if ($(e.currentTarget).closest('.js-payment-form-wrapper-pay-now').length) { 491 return; 492 } 493 flex.utils.debounce('user_service_fee_price_3_keyup', function () { 494 self.handleServiceFeeCustomAmountChange(self.getServiceFeeSlotConfig(3)); 495 }, 800); 496 }); 497 498 $body.on('click', '.js-switch-delivery-address-option', function(e) { 499 e.preventDefault(); 500 self.switchDeliveryAddressOption(); 501 }); 502 503 $body.on('click', '.js-apply-loyalty', function (e) { 504 e.preventDefault(); 505 506 $button = $('.js-loyalty-container').find('.js-apply-loyalty'); 507 508 self.updateDiscountWithLoyaltyPoints($button.attr('data-action')); 509 }); 510 511 self.createAnAccountListener(); 512 self.venueAndRoomListener(); 513 self.adjustShippingDisplayForVenue(); 514 self.reEvaluatePaymentMethodBillingAddress(); 515 } 516 517 // show loader on submit 518 if ($wrapper.length) { 519 self._deferInitServiceFee = true; 520 self._paymentInitAjaxPosted = false;
521 522 var $paymentMethods = self.getPaymentMethodsEl(); 523 524 // select first payment 525 var checked = $paymentMethods.filter(':checked'); 526 if (checked.length) { 527 checked.trigger('change'); 528 } else { 529 var $paymentMethodCreditCard = $paymentMethods 530 .filter('[data-isCreditCard="1"]').first(); 531 532 if ($paymentMethodCreditCard.length) { 533 $paymentMethodCreditCard 534 .prop('checked', 'checked') 535 .trigger('change'); 536 } else { 537 $paymentMethods 538 .first() 539 .prop('checked', 'checked') 540 .trigger('change'); 541 } 542 } 543 544 $wrapper.find('.js-edit-invoice-po').click(function (e) { 545 e.preventDefault(); 546 $wrapper.find('.js-submit-payment').prop('disabled', true); 547 $wrapper.find('.js-edit-invoice-po').addClass('hidden'); 548 $wrapper.find('.js-save-invoice-po').html('Save'); 549 $wrapper.find('.js-save-invoice-po').removeClass('hidden'); 550 $wrapper.find('.js-purchase-order-number').prop('disabled', false); 551 }); 552 553 $wrapper.find('.js-save-invoice-po').click(function (e) { 554 e.preventDefault(); 555 556 $wrapper.find('.js-purchase-order-number').prop('disabled', true); 557 let order_uuid = $wrapper.find('.js-order-uuid').val(); 558 $.ajax({ 559 type: 'post', 560 data: { 561 client_po: $wrapper.find('.js-purchase-order-number').val() 562 }, 563 url: baseUrl + 'order/update/' + order_uuid, 564 dataType: 'json', 565 beforeSend:function() { 566 $wrapper.find('.js-save-invoice-po').html(i18n.gettext('<img src="/themes/frontend/base/assets/images/preloader.gif" class="img-loader"/>')); 567 }, 568 success: function (data) { 569 $wrapper.find('.js-save-invoice-po').addClass('hidden'); 570 $wrapper.find('.js-edit-invoice-po').removeClass('hidden'); 571 $wrapper.find('.js-submit-payment').prop('disabled', false); 572 } 573 }); 574 }); 575 576 $wrapper.find('.js-submit-payment').click(function (e) { 577 var $formInWrapper = $(this).closest('.js-payment-form-wrapper').find('form.js-payment-form'); 578 if ($formInWrapper.parsley().validate()) { 579 $('.loading-order').show(); 580 $formInWrapper.submit(); 581 } 582 }); 583 } 584 585 this.tipListener(); 586 this.tip2Listener(); 587 this.tip3Listener(); 588 589 if (self._deferInitServiceFee && !self._paymentInitAjaxPosted) { 590 self.finishDeferredInitServiceFee(); 591 } 592 593 this.emailValidationListener(); 594 595 // make now active 596 setTimeout(function() { 597 $checkoutForm.removeClass('disabled'); 598 }, 600); 599 }, 600 601 submitLoading: function(loading) { 602 loading = typeof loading !== 'undefined' ? loading : false; 603 var $wrapper = this.getWrapperEl(); 604 var $submit = $wrapper.find('.js-submit-payment'); 605 606 if (loading) { 607 $submit.addClass('disabled'); 608 } else { 609 $submit.removeClass('disabled'); 610 } 611 }, 612 613 triggerCodeChallenge: function() { 614 var $wrapper = this.getWrapperEl(); 615 var $modal = $('#modal-email-challenge'); 616 var $form = this.getCheckoutFormEl(); 617 var isGuest = $form.data('is-guest'); 618 var requireVerification = $form.data('email-require-verification'); 619 var email = $form.data('email'); 620 var $modalBody = $modal.find('.modal-body'); 621 var $btn = $modal.find('.js-verify-continue'); 622 var $input = $modal.find('.js-input-code'); 623 var $resend = $modal.find('.js-resend'); 624 var countdownInterval = null; 625 626 function submit(code) { 627 $btn.addClass('disabled'); 628 $.ajax({ 629 type: 'post', 630 data: { 631 is_guest: isGuest ? 1 : 0, 632 email: email, 633 code: code, 634 }, 635 url: baseUrl + 'order/checkout/validate_challenge', 636 dataType: 'json', 637 success: function (data) { 638 $btn.removeClass('disabled'); 639 640 if (data.status === 'error') { 641 alert(data.message); 642 return ; 643 } 644 645 $form.find('input[name="_email_challenge_verified"]').val(1); 646 $form.find('input[name="_email_challenge_email"]').val(email); 647 $modal.modal('hide'); 648 setTimeout(function() { 649 $wrapper.find('.js-submit-payment').trigger('click'); 650 }, 1000); 651 } 652 }) 653 } 654 655 function sendCode(isResend) { 656 isResend = typeof isResend !== 'undefined' ? isResend : false;
657 $modalBody.addClass('disabled'); 658 console.log('js-email', $modalBody.find('.js-email')) 659 $modalBody.find('.js-email').text(email); 660 661 $.ajax({ 662 type: 'post', 663 data: { 664 is_guest: isGuest ? 1 : 0, 665 email: email, 666 }, 667 url: baseUrl + 'order/checkout/send_challenge', 668 dataType: 'json', 669 success: function (data) { 670 $modalBody.removeClass('disabled'); 671 672 $btn.addClass('disabled'); 673 $input.off('keyup change').on('keyup change', function() { 674 if ($(this).val()) { 675 $btn.removeClass('disabled'); 676 } else { 677 $btn.addClass('disabled'); 678 } 679 }); 680 681 $btn.off('click').on('click', function(e) { 682 e.preventDefault(); 683 684 submit($input.val()); 685 }); 686 687 if (isResend) { 688 var $newCodeSent = $modalBody.find('.js-new-code-sent'); 689 var $newCodeSentStatus = $modalBody.find('.js-email-challenge-announce'); 690 var message = $newCodeSent.text().trim(); 691 692 // update the new code sent status 693 $newCodeSentStatus.text(''); 694 $newCodeSentStatus.text(message); 695 696 $newCodeSent.slideDown(); 697 698 // retain focus on the input 699 setTimeout(function() { 700 $input.focus(); 701 }, 100); 702 703 setTimeout(function() { 704 $newCodeSent.slideUp(function () { 705 $newCodeSent.attr('aria-hidden', 'true'); 706 }); 707 }, 3000); 708 709 var countDown = 30; 710 clearInterval(countdownInterval); 711 $resend.addClass('disabled'); 712 713 countdownInterval = setInterval(function() { 714 countDown -= 1; 715 716 if (countDown <= 0) { 717 $modal.find('.js-resend-countdown-wrapper').hide(); 718 $resend.removeClass('disabled'); 719 } else { 720 $modal.find('.js-resend-countdown-wrapper').show(); 721 $modal.find('.js-resend-countdown').text(countDown); 722 $resend.addClass('disabled'); 723 } 724 }, 1000); 725 } else { 726 $modal.find('.js-resend-countdown-wrapper').hide(); 727 $resend.removeClass('disabled'); 728 } 729 } 730 }); 731 } 732 733 $resend.off('click').on('click', function(e) { 734 e.preventDefault(); 735 sendCode(true); 736 }); 737 738 $modal.off('shown.bs.modal.email-challenge-announce').on('shown.bs.modal.email-challenge-announce', function () { 739 var description = $modal.find('#email-challenge-modal-description').text().trim(); 740 $modal.find('.js-email-challenge-announce').text(description); 741 }); 742 743 // if guest 744 // then past the email and validate\ 745 if (requireVerification) { 746 if (isGuest) { 747 if ($form.data('guest-email-is-valid')) { 748 sendCode(); 749 $modal.appendTo('body'); 750 $modal.modal('show'); 751 } 752 } else { 753 sendCode(); 754 $modal.appendTo('body'); 755 $modal.modal('show'); 756 } 757 } 758 }, 759 760 /** 761 * called via themes/frontend/base/js/validate-captcha.js 762 */ 763 checkoutCaptcha: function() { 764 765 766 /* Hidden recaptch is disabled for now*/ 767 var $form = this.getCheckoutFormEl(); 768 769 if (this.getCheckoutFormEl().length && $('#checkout_recaptcha').length) { 770 this.checkoutCaptcha = grecaptcha.render('checkout_recaptcha', { 771 'sitekey': $form.find('#checkout_recaptcha').attr('data-sitekey'), 772 }); 773 } 774 }, 775 776 venueAndRoomListener: function() { 777 var self = this, 778 $checkoutForm = self.getCheckoutFormEl(), 779 $venueEl = $checkoutForm.find('.js-select-venue'), 780 $venueRoomEl = $checkoutForm.find('.js-select-main-room'), 781 $venueBusinessNameEl = $checkoutForm.find('.js-venue-business-name'), 782 $venueAddress = $checkoutForm.find('.js-venue-address'), 783 $venueAddress2 = $checkoutForm.find('.js-venue-address2'), 784 $venuePostcode = $checkoutForm.find('.js-venue-postcode'), 785 $venueSuburb = $checkoutForm.find('.js-venue-suburb'), 786 $venueState = $checkoutForm.find('.js-venue-state'), 787 $venueCountry = $checkoutForm.find('.js-venue-country'), 788 $venuePhone = $checkoutForm.find('.js-venue-phone'), 789 $venueAddressExceptStEl = $checkoutForm.find('.js-venue-address-except-st'); 790 791 var venueSelect = new flex.venue.selectVenue({ 792 $select: $venueEl, 793 $selectRoom: $venueRoomEl, 794 onChangeRoom: function(data) { 795 // update venue fields 796 $venueBusinessNameEl.val(data.venue && data.venue.company ? data.venue.company : ''); // hidden 797 $venueAddress.val(data.venue && data.venue.address ? data.venue.address : ''); 798 $venueAddress2.val(data.venue && data.venue.address2 ? data.venue.address2 : ''); 799 $venuePostcode.val(data.venue && data.venue.postcode ? data.venue.postcode : ''); // hidden 800 $venueSuburb.val(data.venue && data.venue.suburb ? data.venue.suburb : ''); // hidden 801 $venueState.val(data.venue && data.venue.state ? data.venue.state : ''); // hidden 802 $venueCountry.val(data.venue && data.venue.country ? data.venue.country : ''); // hidden 803 $venuePhone.val(data.venue && data.venue.phone ? data.venue.phone : ''); 804 805 $venueAddressExceptStEl.val([ 806 $venueSuburb.val(), 807 $venueState.val(), 808 $venuePostcode.val(), 809 $venueCountry.val(), 810 ].join(' ').trim()); 811 812 self.adjustShippingDisplayForVenue(); 813 814 self.updateOrderDetails(); 815 } 816 }); 817 818 venueSelect.updateRooms(); 819 venueSelect.updateRoomValue($venueRoomEl.val()); 820 venueSelect.options.$selectRoom.change(function(e){ 821 if (venueSelect.options.$select.val()) { 822 self.updateOrderDetails(); 823 } 824 }); 825 }, 826 827 adjustShippingDisplayForVenue: function() { 828 var self = this, 829 $checkoutForm = self.getCheckoutFormEl(), 830 $deliveryAddressOptionEl = $checkoutForm.find('.js-delivery-address-option'), 831 $venueEl = $checkoutForm.find('.js-select-venue'), 832 $venueAddress = $checkoutForm.find('.js-venue-address'), 833 $venueAddress2 = $checkoutForm.find('.js-venue-address2'), 834 $venuePhone = $checkoutForm.find('.js-venue-phone'), 835 $venueAddressExceptStEl = $checkoutForm.find('.js-venue-address-except-st'), 836 hideElementClosest = function($el){ 837 $el.closest('div.form-group').hide(); 838 }, 839 showElementClosest = function($el){ 840 if ($el.val()) { 841 $el.closest('div.form-group').show(); 842 } 843 }; 844 845 if ($deliveryAddressOptionEl.val() != 'venue') return; 846 847 hideElementClosest($venueAddress); 848 hideElementClosest($venueAddress2); 849 hideElementClosest($venuePhone); 850 hideElementClosest($venueAddressExceptStEl); 851 852 if ($venueEl.val()) { 853 showElementClosest($venueAddress); 854 showElementClosest($venueAddress2); 855 showElementClosest($venuePhone); 856 showElementClosest($venueAddressExceptStEl); 857 } 858 }, 859 860 switchDeliveryAddressOption: function() { 861 var self = this, 862 $checkoutForm = this.getCheckoutFormEl(), 863 $switchDeliveryAddresEl = $checkoutForm.find('.js-switch-delivery-address-option'), 864 $deliveryAddressOptionEl = $checkoutForm.find('.js-delivery-address-option'), 865 $shipDifferentAddressEl = $checkoutForm.find('.js-ship-different-address'), 866 867 currentDeliveryAddressOption = $checkoutForm.find('.js-delivery-address-option:checked').val(), 868 currentDeliveryAddressOptionText = $switchDeliveryAddresEl.text(), 869 refreshShippingCheckoutAddressBox = false;
870 871 // switch 872 if (currentDeliveryAddressOption == 'manual') { 873 currentDeliveryAddressOption = 'venue'; 874 currentDeliveryAddressOptionText = i18n.gettext('Manual Address'); 875 $shipDifferentAddressEl.prop('checked', true).trigger('change'); 876 } else { 877 currentDeliveryAddressOption = 'manual'; 878 currentDeliveryAddressOptionText = i18n.gettext('Venue Address'); 879 $shipDifferentAddressEl.prop('checked', false).trigger('change'); 880 refreshShippingCheckoutAddressBox = true; 881 $('.js-address-box-type--shipping').addClass('loading-with-skeleton'); 882 } 883 884 $switchDeliveryAddresEl.text(currentDeliveryAddressOptionText); 885 $deliveryAddressOptionEl.prop('checked', false); 886 $checkoutForm.find('.js-delivery-address-option[value="' + currentDeliveryAddressOption + '"]').prop('checked', true).trigger('change'); 887 888 self.adjustShippingDisplayForVenue(); 889 890 self.updateOrderDetails(refreshShippingCheckoutAddressBox); 891 }, 892 893 syncServiceFeeOptionUi: function ($inputs, $custom, $selected) { 894 $inputs.not($selected).prop('checked', false); 895 $custom.hide(); 896 if ($selected.val() == flexconfig.order.SERVICE_FEE_TYPE_FIXED && $selected.is(':checked')) { 897 $custom.show(); 898 } 899 }, 900 901 getServiceFeeSlotConfig: function (slot) { 902 var configs = { 903 1: { 904 wrapperSelector: '.js-service-fee-wrapper', 905 optionsSelector: '.service-fee-options', 906 customSelector: '.js-custom-amount', 907 optionName: 'service_fee_option', 908 updateMethod: 'updateServiceFeeAmount' 909 }, 910 2: { 911 wrapperSelector: '.js-service-fee-2-wrapper', 912 optionsSelector: '.service-fee-2-options', 913 customSelector: '.js-custom-amount-2', 914 optionName: 'service_fee_2_option', 915 updateMethod: 'updateServiceFee2Amount' 916 }, 917 3: { 918 wrapperSelector: '.js-service-fee-3-wrapper', 919 optionsSelector: '.service-fee-3-options', 920 customSelector: '.js-custom-amount-3', 921 optionName: 'service_fee_3_option', 922 updateMethod: 'updateServiceFee3Amount' 923 } 924 }; 925 926 return configs[slot]; 927 }, 928 929 resolveServiceFeeOptionPostValue: function ($wrapper, optionName, $selectedInput) { 930 if ($selectedInput && $selectedInput.length) { 931 return $selectedInput.is(':checked') ? $selectedInput.val() : ''; 932 } 933 934 var $checked = $wrapper.find('input[name="' + optionName + '"]:checked'); 935 936 return $checked.length ? $checked.val() : ''; 937 }, 938 939 handleServiceFeeOptionChange: function (e, slotConfig) { 940 var self = this, 941 $clicked = $(e.currentTarget), 942 $wrapper = $clicked.closest(slotConfig.wrapperSelector), 943 $optionsContainer = $wrapper.find(slotConfig.optionsSelector), 944 $inputs = $wrapper.find('input[name=' + slotConfig.optionName + ']'), 945 $custom = $wrapper.find(slotConfig.customSelector), 946 isMandatory = $optionsContainer.length 947 && parseInt($optionsContainer.data('mandatory'), 10) === 1; 948 949 if (!$wrapper.length) { 950 return; 951 } 952 953 if (isMandatory && !$clicked.is(':checked')) { 954 $clicked.prop('checked', true); 955 956 return; 957 } 958 959 var isEnterAmount = $clicked.val() == flexconfig.order.SERVICE_FEE_TYPE_FIXED, 960 isEnterAmountSelect = isEnterAmount && $clicked.is(':checked'); 961 962 if ($clicked.is(':checked')) { 963 self.syncServiceFeeOptionUi($inputs, $custom, $clicked); 964 } else { 965 $custom.hide(); 966 } 967 968 if (!isEnterAmountSelect) { 969 self[slotConfig.updateMethod](false, $clicked); 970 } 971 }, 972 973 getCheckedEnterAmountOptionInput: function ($feeWrapper, optionName) { 974 var $fixedInput = $feeWrapper.find( 975 'input[name="' + optionName + '"][value="' + flexconfig.order.SERVICE_FEE_TYPE_FIXED + '"]' 976 ); 977 978 return $fixedInput.is(':checked') ? $fixedInput : null; 979 }, 980 981 handleServiceFeeCustomAmountChange: function (slotConfig) { 982 var self = this, 983 $feeWrapper = self.getWrapperEl().find(slotConfig.wrapperSelector), 984 $fixedInput = self.getCheckedEnterAmountOptionInput($feeWrapper, slotConfig.optionName); 985 986 if (!$fixedInput) { 987 return; 988 } 989 990 self[slotConfig.updateMethod](false, $fixedInput); 991 }, 992 993 syncServiceFeeCustomAmountVisibility: function (slotConfig) { 994 var $feeWrapper = this.getWrapperEl().find(slotConfig.wrapperSelector), 995 $fixedInput = this.getCheckedEnterAmountOptionInput($feeWrapper, slotConfig.optionName); 996 997 if (!$feeWrapper.length || !$fixedInput) { 998 return; 999 } 1000 1001 this.syncServiceFeeOptionUi( 1002 $feeWrapper.find('input[name=' + slotConfig.optionName + ']'), 1003 $feeWrapper.find(slotConfig.customSelector), 1004 $fixedInput 1005 ); 1006 }, 1007 1008 syncAllServiceFeeCustomAmountVisibility: function () { 1009 var self = this; 1010 1011 [1, 2, 3].forEach(function (slot) { 1012 self.syncServiceFeeCustomAmountVisibility(self.getServiceFeeSlotConfig(slot)); 1013 }); 1014 }, 1015 1016 snapshotEnterAmountCheckedBySlot: function () { 1017 var self = this, 1018 snapshot = {}; 1019 1020 [1, 2, 3].forEach(function (slot) { 1021 var slotConfig = self.getServiceFeeSlotConfig(slot), 1022 $feeWrapper = self.getWrapperEl().find(slotConfig.wrapperSelector); 1023 1024 snapshot[slot] = !!self.getCheckedEnterAmountOptionInput($feeWrapper, slotConfig.optionName); 1025 }); 1026 1027 return snapshot; 1028 }, 1029 1030 restoreEnterAmountSelectionAfterControlsRefresh: function (enterAmountWasCheckedBySlot) { 1031 var self = this; 1032 1033 [1, 2, 3].forEach(function (slot) { 1034 if (!enterAmountWasCheckedBySlot[slot]) { 1035 return; 1036 } 1037 1038 var slotConfig = self.getServiceFeeSlotConfig(slot), 1039 $feeWrapper = self.getWrapperEl().find(slotConfig.wrapperSelector), 1040 $fixedInput = $feeWrapper.find( 1041 'input[name="' + slotConfig.optionName + '"][value="' + flexconfig.order.SERVICE_FEE_TYPE_FIXED + '"]' 1042 ),
1043 $inputs = $feeWrapper.find('input[name=' + slotConfig.optionName + ']'), 1044 $custom = $feeWrapper.find(slotConfig.customSelector); 1045 1046 if (!$fixedInput.length) { 1047 return; 1048 } 1049 1050 $fixedInput.prop('checked', true); 1051 self.syncServiceFeeOptionUi($inputs, $custom, $fixedInput); 1052 }); 1053 }, 1054 1055 postServiceFeeForOption: function (optionName, silent) { 1056 if (optionName === 'service_fee_option') { 1057 this.updateServiceFeeAmount(silent); 1058 } else if (optionName === 'service_fee_2_option') { 1059 this.updateServiceFee2Amount(silent); 1060 } else if (optionName === 'service_fee_3_option') { 1061 this.updateServiceFee3Amount(silent); 1062 } 1063 }, 1064 1065 applyInitServiceFeeSlot: function (optionName, customSelector, presetSelector, optionsSelector, silent) { 1066 var self = this, 1067 $wrapper = this.getWrapperEl(), 1068 $custom = $wrapper.find(customSelector), 1069 $inputs = $wrapper.find('input[name=' + optionName + ']'), 1070 $serviceFeeOptionsInputs = $wrapper.find(presetSelector), 1071 $optionsContainer = $wrapper.find(optionsSelector), 1072 $checked = $inputs.filter(':checked'); 1073 1074 if ($optionsContainer.length && parseInt($optionsContainer.data('cart-qualifies'), 10) !== 1) { 1075 return; 1076 } 1077 1078 if ($checked.length) { 1079 if (!silent) { 1080 $checked.trigger('change'); 1081 } else if ($checked.val() == flexconfig.order.SERVICE_FEE_TYPE_FIXED) { 1082 self.syncServiceFeeOptionUi($inputs, $custom, $checked); 1083 } else { 1084 $custom.hide(); 1085 } 1086 1087 return; 1088 } 1089 1090 if ($checked.length === 0 1091 && $optionsContainer.length 1092 && parseInt($optionsContainer.data('mandatory'), 10) === 1 1093 && $serviceFeeOptionsInputs.length > 0) { 1094 var $firstMandatoryPreset = $serviceFeeOptionsInputs.first(); 1095 if (silent) { 1096 $firstMandatoryPreset.prop('checked', true); 1097 self.syncServiceFeeOptionUi($inputs, $custom, $firstMandatoryPreset); 1098 self.postServiceFeeForOption(optionName, true); 1099 } else { 1100 $firstMandatoryPreset.prop('checked', true).trigger('change'); 1101 } 1102 } 1103 1104 if (!silent && $custom.length && $inputs.filter(':checked').length) { 1105 var $selected = $inputs.filter(':checked'); 1106 1107 if ($selected.val() == flexconfig.order.SERVICE_FEE_TYPE_FIXED) { 1108 $custom.show(); 1109 } 1110 } 1111 }, 1112 1113 runInitServiceFeeAutoSelect: function (silent) { 1114 this.applyInitServiceFeeSlot( 1115 'service_fee_option', 1116 '.js-custom-amount', 1117 '.js-service-fee-options input[name=service_fee_option]', 1118 '.service-fee-options', 1119 silent 1120 ); 1121 this.applyInitServiceFeeSlot( 1122 'service_fee_2_option', 1123 '.js-custom-amount-2', 1124 '.js-service-fee-2-options input[name=service_fee_2_option]', 1125 '.service-fee-2-options', 1126 silent 1127 ); 1128 this.applyInitServiceFeeSlot( 1129 'service_fee_3_option', 1130 '.js-custom-amount-3', 1131 '.js-service-fee-3-options input[name=service_fee_3_option]', 1132 '.service-fee-3-options', 1133 silent 1134 ); 1135 }, 1136 1137 tipListener: function() { 1138 var self = this; 1139 1140 if (!self._deferInitServiceFee) { 1141 self.applyInitServiceFeeSlot( 1142 'service_fee_option', 1143 '.js-custom-amount', 1144 '.js-service-fee-options input[name=service_fee_option]', 1145 '.service-fee-options' 1146 ); 1147 } 1148 }, 1149 1150 tip2Listener: function() { 1151 var self = this; 1152 1153 if (!self._deferInitServiceFee) { 1154 self.applyInitServiceFeeSlot( 1155 'service_fee_2_option', 1156 '.js-custom-amount-2', 1157 '.js-service-fee-2-options input[name=service_fee_2_option]', 1158 '.service-fee-2-options' 1159 ); 1160 } 1161 }, 1162 1163 tip3Listener: function() { 1164 var self = this; 1165 1166 if (!self._deferInitServiceFee) { 1167 self.applyInitServiceFeeSlot( 1168 'service_fee_3_option', 1169 '.js-custom-amount-3', 1170 '.js-service-fee-3-options input[name=service_fee_3_option]', 1171 '.service-fee-3-options' 1172 ); 1173 } 1174 }, 1175 1176 emailValidationListener: function() { 1177 var xhr, self = this; 1178 var $checkoutForm = this.getCheckoutFormEl(); 1179 $checkoutForm.on('keyup', '#form_billing_email', _.debounce(function (e) { 1180 !xhr || xhr.abort(); 1181 1182 self.submitLoading(true); 1183 var emailVal = $(this).val(); 1184 xhr = $.ajax({ 1185 type: 'get', 1186 data: { 1187 email: emailVal, 1188 }, 1189 dataType: 'json', 1190 url: baseUrl + 'order/cart/validate_email', 1191 success: function (data) { 1192 var $emailExisist = $checkoutForm.find('#email-exists'); 1193 $checkoutForm.data('guest-email-is-valid', 0); 1194 1195 if (data.status === 'success') { 1196 $emailExisist.hide(); 1197 1198 // status is returned success so the email exists message is not showndata-guest-email-is-valid 1199 if (data.email_is_valid) { 1200 $checkoutForm.data('guest-email-is-valid', 1); 1201 $checkoutForm.data('email', emailVal); 1202 } 1203 } else { 1204 $emailExisist.show(); 1205 } 1206 } 1207 }).always(function() { 1208 self.submitLoading(false); 1209 }); 1210 }, 800)); 1211 1212 $checkoutForm.find("#form_billing_email").trigger('keyup'); 1213 }, 1214 1215 updateOrderDetails: _.debounce(function(refreshShippingCheckoutAddressBox = false) { 1216 var self = this, 1217 $checkoutForm = this.getCheckoutFormEl(); 1218 1219 self.abortCheckoutAjax('OrderDetails'); 1220 1221 if ($checkoutForm.length) { 1222 self.setCheckoutAjax('OrderDetails', $.ajax({ 1223 type: 'post', 1224 data: $checkoutForm.serialize(), 1225 url: baseUrl + 'order/checkout/update_order_details', 1226 dataType: 'json', 1227 success: function (data) { 1228 flex.cart.updateCartInformation(data.cart_information); 1229 1230 if (refreshShippingCheckoutAddressBox) { 1231 flex.account.refreshCheckoutAddressBox('shipping'); 1232 } 1233 1234 if (data.reload_to_update_payment) { 1235 alert(data.reload_to_update_payment_message); 1236 window.location.reload(); 1237 } 1238 } 1239 })); 1240 } 1241 }, 600), 1242 1243 updateShippingMethods: function() { 1244 var $shippingMethodsContainer = $('.js-shipping-methods-container'); 1245 if (typeof cartInformation.shipping_methods_html !== 'undefined' 1246 && $shippingMethodsContainer.length) { 1247 $shippingMethodsContainer.html(cartInformation.shipping_methods_html); 1248 } 1249 }, 1250 1251 updateOrderShippingMethod: function () { 1252 var self = this, 1253 getCheckoutFormEl = this.getCheckoutFormEl(); 1254 1255 1256 self.abortCheckoutAjax('ShippingMethod'); 1257 1258 if (getCheckoutFormEl.length) { 1259 self.hidePlaceOrderSection(); 1260 self.setCheckoutAjax('ShippingMethod', $.ajax({ 1261 type: 'post', 1262 data: {shipping_method: getCheckoutFormEl.find('[name="shipping_method"]:checked').val()}, 1263 url: baseUrl + 'order/cart/update_order_shipping_method', 1264 dataType: 'json', 1265 success: function (data) { 1266 flex.cart.updateCartInformation(data.cart_information); 1267 } 1268 })); 1269 } 1270 }, 1271 1272 updateOrderPaymentMethod: function() { 1273 var self = this, 1274 $wrapper = self.getWrapperEl(); 1275 1276 self.abortCheckoutAjax('PaymentMethod'); 1277 self.hidePlaceOrderSection(); 1278 self._paymentInitAjaxPosted = true; 1279 1280 self.setCheckoutAjax('PaymentMethod', $.ajax({ 1281 type: 'post', 1282 data: {payment_method: $wrapper.find('[name="payment_method"]:checked').val()}, 1283 url: baseUrl + 'order/cart/update_order_payment_method', 1284 dataType: 'json', 1285 success: function (data) { 1286 flex.cart.updateCartInformation(data.cart_information); 1287 }, 1288 complete: function () { 1289 self.finishDeferredInitServiceFee(); 1290 } 1291 })); 1292 }, 1293 1294 /** 1295 * Re-evaluate if we should show payment method billing address container 1296 * 1297 * @return {void} 1298 */ 1299 reEvaluatePaymentMethodBillingAddress: function() { 1300 var self = this, 1301 $wrapper = self.getWrapperEl(), 1302 $pmAddress = self.getPaymentMethodBillingAddressContainer(), 1303 $checkedPaymentMethod = $wrapper.find('[name="payment_method"]').filter(':checked'), 1304 $checkedCard = $wrapper.find('.js-change-show-payment_method-' + $checkedPaymentMethod.val()).find('.js-credit-card-save-select:checked'), 1305 shouldShow = $checkedPaymentMethod.data('show-payment-method-billing-address'); 1306 1307 $pmAddress.hide(); 1308 if (parseInt(shouldShow) === 1 && ! $checkedCard.val()) { 1309 $pmAddress.show(); 1310 } 1311 }, 1312 1313 updateServiceFeeAmount: function(silent, $selectedInput) { 1314 var self = this, 1315 $wrapper = self.getWrapperEl(), 1316 $userServiceFeePrice = $wrapper.find('.js-user-service-fee-price'), 1317 $userServiceFeePriceFormatted = $wrapper.find('.js-user-service-fee-price-formatted'); 1318 self.abortCheckoutAjax('ServiceFee1'); 1319 1320 if (!silent) { 1321 $wrapper.addClass('disabled'); 1322 !window.xhrUsfa || clearTimeout(window.xhrUsfa); 1323 window.xhrUsfa = setTimeout(function() { 1324 $wrapper.removeClass('disabled'); 1325 }, 700); 1326 1327 self.hidePlaceOrderSection(); 1328 } 1329 self.setCheckoutAjax('ServiceFee1', $.ajax({ 1330 type: 'post', 1331 data: { 1332 service_fee_option: self.resolveServiceFeeOptionPostValue( 1333 $wrapper, 1334 'service_fee_option', 1335 $selectedInput 1336 ), 1337 user_service_fee_price: $wrapper.find('input[name="user_service_fee_price"]').val() 1338 }, 1339 url: baseUrl + 'order/cart/update_service_fee_options', 1340 dataType: 'json', 1341 success: function (data) { 1342 // update user tip price 1343 if (data.status == 'success') { 1344 if (!$userServiceFeePrice.is(':focus')) { 1345 $userServiceFeePrice.val(data.cart_information.user_service_fee_price); 1346 } 1347 1348 $userServiceFeePriceFormatted.text(flex.formatMoney(data.cart_information.user_service_fee_price)); 1349 flex.cart.updateCartInformation(data.cart_information); 1350 } 1351 } 1352 })); 1353 }, 1354 1355 updateServiceFee2Amount: function(silent, $selectedInput) { 1356 var self = this, 1357 $wrapper = self.getWrapperEl(), 1358 $userServiceFee2Price = $wrapper.find('.js-user-service-fee-2-price'), 1359 $userServiceFee2PriceFormatted = $wrapper.find('.js-user-service-fee-2-price-formatted'); 1360 1361 self.abortCheckoutAjax('ServiceFee2'); 1362 1363 if (!silent) { 1364 $wrapper.addClass('disabled'); 1365 !window.xhrUsfa2 || clearTimeout(window.xhrUsfa2); 1366 window.xhrUsfa2 = setTimeout(function() { 1367 $wrapper.removeClass('disabled'); 1368 }, 700); 1369 1370 self.hidePlaceOrderSection(); 1371 } 1372 self.setCheckoutAjax('ServiceFee2', $.ajax({ 1373 type: 'post', 1374 data: { 1375 service_fee_2_option: self.resolveServiceFeeOptionPostValue( 1376 $wrapper, 1377 'service_fee_2_option', 1378 $selectedInput 1379 ), 1380 user_service_fee_2_price: $wrapper.find('input[name="user_service_fee_2_price"]').val() 1381 }, 1382 url: baseUrl + 'order/cart/update_service_fee_2_options', 1383 dataType: 'json', 1384 success: function (data) { 1385 // update user tip price 1386 if (data.status == 'success') { 1387 if (!$userServiceFee2Price.is(':focus')) { 1388 $userServiceFee2Price.val(data.cart_information.user_service_fee_2_price); 1389 } 1390 1391 $userServiceFee2PriceFormatted.text(flex.formatMoney(data.cart_information.user_service_fee_2_price)); 1392 flex.cart.updateCartInformation(data.cart_information); 1393 } 1394 } 1395 })); 1396 }, 1397 1398 updateServiceFee3Amount: function(silent, $selectedInput) { 1399 var self = this, 1400 $wrapper = self.getWrapperEl(), 1401 $userServiceFee3Price = $wrapper.find('.js-user-service-fee-3-price'), 1402 $userServiceFee3PriceFormatted = $wrapper.find('.js-user-service-fee-3-price-formatted'); 1403 1404 self.abortCheckoutAjax('ServiceFee3'); 1405 1406 if (!silent) { 1407 $wrapper.addClass('disabled'); 1408 !window.xhrUsfa3 || clearTimeout(window.xhrUsfa3); 1409 window.xhrUsfa3 = setTimeout(function() { 1410 $wrapper.removeClass('disabled'); 1411 }, 700); 1412 1413 self.hidePlaceOrderSection(); 1414 } 1415 self.setCheckoutAjax('ServiceFee3', $.ajax({ 1416 type: 'post', 1417 data: { 1418 service_fee_3_option: self.resolveServiceFeeOptionPostValue( 1419 $wrapper, 1420 'service_fee_3_option', 1421 $selectedInput 1422 ), 1423 user_service_fee_3_price: $wrapper.find('input[name="user_service_fee_3_price"]').val() 1424 }, 1425 url: baseUrl + 'order/cart/update_service_fee_3_options', 1426 dataType: 'json', 1427 success: function (data) { 1428 // update user tip price 1429 if (data.status == 'success') { 1430 if (!$userServiceFee3Price.is(':focus')) { 1431 $userServiceFee3Price.val(data.cart_information.user_service_fee_3_price); 1432 } 1433 1434 $userServiceFee3PriceFormatted.text(flex.formatMoney(data.cart_information.user_service_fee_3_price)); 1435 flex.cart.updateCartInformation(data.cart_information); 1436 } 1437 } 1438 })); 1439 }, 1440 1441 createAnAccountListener: function() { 1442 var $form = this.getCheckoutFormEl(), 1443 $createAnAccount = $form.find('.js-create-an-account'), 1444 $btnCheckoutAsGuest = this.getWrapperEl().find('.js-place-order-text-as-guest'), 1445 $btnCheckout = this.getWrapperEl().find('.js-place-order-text'); 1446 1447 // revalidate checkout state 1448 $createAnAccount.change(function() { 1449 $btnCheckoutAsGuest.hide(); 1450 $btnCheckout.hide(); 1451 1452 if ($(this).is(':checked')) { 1453 $btnCheckout.show(); 1454 } else { 1455 $btnCheckoutAsGuest.show(); 1456 } 1457 }); 1458 1459 if (typeof window.localStorage !== 'undefined') { 1460 var localKeyStore = '_checkout_create_an_account_input_value'; 1461 $createAnAccount.change(function() { 1462 window.localStorage.setItem(localKeyStore, $(this).is(':checked') ? 1 : 0); 1463 }); 1464 1465 if (window.localStorage.getItem(localKeyStore) !== null) { 1466 // Commented this, Ren don't want the "Create an account" tickbox to be ticked by default 1467 // $createAnAccount.prop('checked', (window.localStorage.getItem(localKeyStore) == 1)); 1468 } 1469 } 1470 1471 $createAnAccount.trigger('change'); 1472 }, 1473 1474 updateCheckoutInformation: function(cartInformation) { 1475 var
1475 self = this; 1476 var $checkoutForm = this.getCheckoutFormEl(), 1477 $paymentFormWrapper = this.getWrapperEl(); 1478 1479 var $cartTotals = $('.js-cart-totals'); 1480 1481 var x, total, $total, $prevTotal = false, 1482 cloneTrWithCode = function() { 1483 return $cartTotals.find('>tr[data-total-code]').first().clone() 1484 }; 1485 1486 // hide all totals 1487 $cartTotals.find('>tr[data-total-code]').hide(); 1488 for (x in cartInformation.totals) { 1489 total = cartInformation.totals[x]; 1490 1491 $total = $cartTotals.find('[data-total-code=' + total.code + ']'); 1492 1493 if (!$total.length) { 1494 $total = cloneTrWithCode(); 1495 $total.attr('data-total-code', total.code); 1496 // if no total founds 1497 if (!$prevTotal) { 1498 $cartTotals.prepend($total); 1499 } else { 1500 $total.insertAfter($prevTotal); 1501 } 1502 } 1503 1504 // record the previous total 1505 $prevTotal = $total; 1506 1507 if (total.is_show) { 1508 $total.find('.js-total-labels') 1509 .text(total.label); 1510 $total.find('.js-total-display') 1511 .text(total.total_formatted); 1512 $total.show(); 1513 } else { 1514 $total.hide(); 1515 } 1516 } 1517 1518 $('.js-cart-grand-total').text(cartInformation.grand_total_formatted); 1519 $('.js-grand-total-hidden').val(cartInformation.grand_total); 1520 $('.js-total-items-container').replaceWith(cartInformation.total_items_html); 1521 1522 var $serviceFeeControls = $('.js-service-fee-controls'), 1523 enterAmountSnapshot = {}; 1524 if (typeof cartInformation.service_fee_controls_html !== 'undefined' 1525 && $serviceFeeControls.length) { 1526 enterAmountSnapshot = self.snapshotEnterAmountCheckedBySlot(); 1527 $serviceFeeControls.html(cartInformation.service_fee_controls_html); 1528 // Preset mutual-exclusivity and POST payload use delegated handleServiceFeeOptionChange. 1529 self.runInitServiceFeeAutoSelect(true); 1530 self.syncAllServiceFeeCustomAmountVisibility(); 1531 self.restoreEnterAmountSelectionAfterControlsRefresh(enterAmountSnapshot); 1532 } 1533 1534 var $feeFormatted = $paymentFormWrapper.find('.js-user-service-fee-price-formatted'); 1535 if ($feeFormatted.length && typeof cartInformation.user_service_fee_price !== 'undefined') { 1536 $feeFormatted.text(flex.formatMoney(cartInformation.user_service_fee_price)); 1537 } 1538 1539 var $fee2Formatted = $paymentFormWrapper.find('.js-user-service-fee-2-price-formatted'); 1540 if ($fee2Formatted.length && typeof cartInformation.user_service_fee_2_price !== 'undefined') { 1541 $fee2Formatted.text(flex.formatMoney(cartInformation.user_service_fee_2_price)); 1542 } 1543 1544 var $fee3Formatted = $paymentFormWrapper.find('.js-user-service-fee-3-price-formatted'); 1545 if ($fee3Formatted.length && typeof cartInformation.user_service_fee_3_price !== 'undefined') { 1546 $fee3Formatted.text(flex.formatMoney(cartInformation.user_service_fee_3_price)); 1547 } 1548 1549 flex.globalPlugin.run($('.js-total-items-container')); 1550 1551 // should we show credit card surcharge details? 1552 var $creditCardDetails = $('.js-credit-card-surcharge-details'); 1553 if (cartInformation.show_credit_card_surcharge_checkout_message == 1) { 1554 $creditCardDetails.show(); 1555 } else { 1556 $creditCardDetails.hide(); 1557 } 1558 1559 // should we show ach surcharge details? 1560 var $achDetails = $('.js-ach-surcharge-details'); 1561 if (cartInformation.show_ach_surcharge_checkout_message == 1) { 1562 $achDetails.show(); 1563 } else { 1564 $achDetails.hide(); 1565 } 1566 1567 (function() { 1568 var $labelDelivery = $('.js-order-date-label-delivery'), 1569 $labelPickup = $('.js-order-date-label-pickup'), 1570 // for recurring 1571 $labelNextDelivery = $('.js-next-delivery-date-label-delivery'), 1572 $labelNextPickup = $('.js-next-delivery-date-label-pickup'); 1573 1574 $labelDelivery.show(); 1575 $labelPickup.hide(); 1576 1577 $labelNextDelivery.show(); 1578 $labelNextPickup.hide(); 1579 1580 1581 if (cartInformation.delivery_option === 'pickup') { 1582 $labelDelivery.hide(); 1583 $labelPickup.show(); 1584 1585 $labelNextDelivery.hide(); 1586 $labelNextPickup.show(); 1587 } 1588 })(); 1589 1590 // @todo, check this on checkout page 1591 if ($checkoutForm && $checkoutForm.length) { 1592 self.showPlaceOrderSection(); 1593 $paymentFormWrapper.find('.js-place-order-message-section').hide(); 1594 if (cartInformation.disable_place_order) { 1595 self.hidePlaceOrderSection(); 1596 $paymentFormWrapper.find('.js-place-order-message-section') 1597 .html(cartInformation.disable_place_order_msg) 1598 .show(); 1599 } 1600 1601 // update afterpay prices 1602 flex.cart.initiateAfterPayFromContainer($checkoutForm, cartInformation.grand_total); 1603 } 1604 1605 // update afterpay prices 1606 flex.cart.initiateAfterPayFromContainer(flex.cart.getCartContainer(), cartInformation.grand_total); 1607 1608 self.showZeroPaymentMethod(cartInformation.is_zero_checkout); 1609 1610 // store last billing address for payment reference 1611 $checkoutForm.data('billing-details', cartInformation.billing_details); 1612 }, 1613 1614 showZeroPaymentMethod: function(showZeroPaymentMethod) { 1615 var $methodWrapper = this.getMethodWrapperEl(), 1616 $zeroMethod = $methodWrapper.find('.payment-method-zero-checkout'), 1617 $paymentMethod = $methodWrapper.find('.payment-method'), 1618 $paymentMethodOptions = $methodWrapper.find('.payment-method-options'), 1619 $zeroMethodInput = $zeroMethod.find('[name=payment_method]'),
1620 $paymentMethodInputs = $paymentMethod.find('[name=payment_method]'); 1621 1622 // if already shown don't trigger again as it will always select 1623 // the first payment method in available payment list 1624 if ((showZeroPaymentMethod && ($zeroMethod.is(":hidden") || !$zeroMethodInput.is(':checked'))) 1625 || (!showZeroPaymentMethod && ($paymentMethod.is(":hidden") || $zeroMethodInput.is(':checked'))) 1626 ) { 1627 $zeroMethod.hide(); 1628 $paymentMethod.hide(); 1629 $paymentMethodOptions.hide(); 1630 1631 $paymentMethodInputs.prop('checked', false); 1632 $zeroMethodInput.prop('checked', false); 1633 1634 if (showZeroPaymentMethod) { 1635 $zeroMethodInput.prop('checked', true).trigger('change'); 1636 $zeroMethod.show(); 1637 } else { 1638 $paymentMethodInputs.first().prop('checked', true).trigger('change'); 1639 $paymentMethod.show(); 1640 $paymentMethodOptions.show(); 1641 } 1642 } 1643 }, 1644 1645 hidePlaceOrderSection: function() { 1646 this.getPlaceOrderSectionEl().addClass('disabled'); 1647 }, 1648 1649 showPlaceOrderSection: function() { 1650 this.getPlaceOrderSectionEl().removeClass('disabled'); 1651 }, 1652 1653 getPlaceOrderSectionEl: function() { 1654 var $wrapper = this.getWrapperEl(); 1655 return $wrapper.find('.js-place-order-section'); 1656 }, 1657 1658 getShipDifferentAddressEl: function() { 1659 var $checkoutForm = this.getCheckoutFormEl(); 1660 return $checkoutForm.find('[name=ship-different-address]'); 1661 }, 1662 1663 /** 1664 * Get the payment methods without the zero checkout. 1665 * 1666 * @returns {*|jQuery} 1667 */ 1668 getPaymentMethodsEl: function() { 1669 var $generalForm = this.getGeneralFormEl(); 1670 return $generalForm.find('.payment-method [name=payment_method]'); 1671 }, 1672 1673 getPaymentMethodBillingAddressContainer: function() { 1674 var $generalForm = this.getGeneralFormEl(); 1675 return $generalForm.find('.js-payment-method-billing-address-container'); 1676 }, 1677 1678 getCheckoutFormEl: function() { 1679 return $('.js-payment-form.js-checkout-form'); 1680 }, 1681 1682 getGeneralFormEl: function() { 1683 return $('.js-payment-form'); 1684 }, 1685 1686 getWrapperEl: function() { 1687 return $('.js-payment-form-wrapper'); 1688 }, 1689 1690 getMethodWrapperEl: function() { 1691 return $('.js-payment-method-wrapper'); 1692 }, 1693 1694 updateDiscountWithLoyaltyPoints: function(action, callback) { 1695 callback = typeof callback !== 'undefined' ? callback : function() {}; 1696 $loyaltyDiscountContainer = $('.js-loyalty-container'); 1697 $loyaltyDiscountButton = $loyaltyDiscountContainer.find('.js-apply-loyalty'); 1698 $loyaltyDiscountLoader = $loyaltyDiscountContainer.find('.js-apply-loyalty-loader'); 1699 $loyaltyDiscountMsg = $loyaltyDiscountContainer.find('.js-loyalty-msg'); 1700 $loyaltyDiscountMsg.hide(); 1701 $loyaltyDiscountLoader.show(); 1702 1703 $.ajax({ 1704 type: 'POST', 1705 url: baseUrl + '/order/cart/update_discount', 1706 data: { 1707 action: action, 1708 discount_type: flexconfig.discount.DISCOUNT_SOURCE_TYPE_LOYALTY_POINTS 1709 }, 1710 dataType: 'json', 1711 success: function (data) { 1712 var btnLabel = i18n.gettext('Use loyalty points'); 1713 if (data.next_action == 'remove') { 1714 btnLabel = i18n.gettext('Remove loyalty points'); 1715 } 1716 1717 $loyaltyDiscountButton.attr('data-action', data.next_action); 1718 $button.text(btnLabel); 1719 1720 if (data.status == 'success') { 1721 $loyaltyDiscountMsg.hide(); 1722 1723 flex.cart.updateCartInformation(data.cart_information); 1724 } else { 1725 if (data.error_message) { 1726 $loyaltyDiscountMsg.text(data.error_message); 1727 } 1728 1729 $loyaltyDiscountMsg.show(); 1730 } 1731 1732 $loyaltyDiscountLoader.hide(); 1733 callback(data); 1734 } 1735 }); 1736 }, 1737}; 1738 1739$(function () {
1740 window.checkoutForm.init(); 1741 1742 $('body').on('click', '.js-select-location', function(e) { 1743 e.preventDefault(); 1744 $(this).attr('aria-expanded', 'true'); 1745 flex.checkout.newLocationSelect($('.modal-location'), true); 1746 }); 1747 1748 $('.modal-location').on('hidden.bs.modal', function () { 1749 $('.js-select-location[aria-expanded]').attr('aria-expanded', 'false'); 1750 }); 1751});
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.