1/** 2 * Cart Dropdown Widget for UI 3 * Handles DOM and UI representation of the state of a Catalog object 4 * @author Jake Grice 5 */ 6(function ($, undefined) { 7 8 "use strict"; 9 10 $.widget("rec1.cartDropdown", { 11 12 options: { 13 cart: null, 14 catalogWidet: null, 15 newItemCount: 0, 16 basePath: '', 17 removable: false, 18 editable: false, 19 childrenRemovable: false, 20 checkoutButton: true, 21 quickPay: false, 22 quickPayData: null, 23 receiptPrinterConfig: null, 24 quickpayTypes: null, 25 highlightClass: "bg-primary", 26 selectLatestAddons: false, 27 }, 28 29 $cartIcon: null, 30 $catalogTabs: null, 31 32 _create: function () { 33 var _this = this; 34 35 this._resetQuickPayData(); 36 37 // Cleanup old dropdown menus 38 $('.rec1-catalog-cart').remove(); 39 40 // Create the dropdown widget 41 this.dropdown = $('<div class="rec1-catalog-cart"></div>'); 42 this.dropdown.appendTo("body").dropdown({ 43 useOffsetParent: false, 44 align: "right", 45 beforeOpen: function (event) { 46 _this.refresh(); 47 return true; 48 }, 49 open: function (event) { 50 _this.scrollToNewItems(); 51 return true; 52 }, 53 close: function () { 54 _this._hideAddonPanel(); 55 } 56 }); 57 58 this._isFloatingControls(); 59 60 this.element.addClass('rec1-catalog-minicart').html(this._renderButton()).attr({ 61 tabindex: "0", 62 role: "button", 63 }).on("keydown", function (event) { 64 if (event.key === "Enter" || event.key === " ") { 65 $(this).trigger("click"); 66 event.preventDefault(); // Prevent page scrolling when pressing space 67 } 68 }); 69 70 71 if (this.options.quickPay) { 72 this.dropdown.css({minWidth: 600}); 73 } 74 75 this._on(this.dropdown, { 76 "click .cart-item": "_handleCartItemClick", 77 "click .cart-item-remove": "_removeItem", 78 "change .cart-item-quantity": "_updateQuantity", 79 "change .cart-item-price": "_updatePrice", 80 "click .cart-checkout-button": "_checkoutClicked", 81 "click .cart-item-quantity-decrease": "_decreaseQuantity", 82 "click .cart-item-quantity-increase": "_increaseQuantity", 83 "click input.cart-item-price": "_focusItemPrice", 84 "keydown .cart-item-note": "_handleCartItemNoteKeydown", 85 "blur .cart-item-note": "_handleCartItemNoteBlur", 86 "click .cart-item-note-save": "_handleCartItemNoteSaveClick", 87 "click .quickpay-thermal-print": "_handleThermalPrintClick", 88 "change .quickpay-total": "_handleQuickPayAmountChange" 89 }); 90 91 _this._initQuickPayEvents(); 92 93 // if not internal, turn off 94 if (this.options.cart.catalog.view === "internal") { 95 _this._initQuickPaySwipe(); 96 } 97 98 this.dropdown.dropdown('option', 'triggerSelector', this.element); 99 100 if (this.options.catalogWidget) { 101 this.options.receiptPrinterConfig = this.options.catalogWidget.catalog("option", "receiptPrinterConfig"); 102 } 103 104 this.$window = $(window); 105 this.$document = $(document); 106 107 this.$cartIcon = this.$document.find(".rec1-catalog-minicart"); 108 this.$catalogTabs = this.$document.find(".rec1-catalog-tabs"); 109 110 this.refresh(); 111 112 this._on(this.$window, { 113 "scroll": _this._adjustCartPosition, 114 "resize": _this._adjustCartPosition 115 }); 116 117 this._observeElementVisibility(["rec1-catalog-cart-container"], 118 this._handleCartFloatableState.bind(this) 119 ); 120 }, 121 122 _handleCartFloatableState: function (isVisible) { 123 var $cartIcon = this.$cartIcon; 124 var $cartDropdown = this.dropdown; 125 126 if (isVisible) { 127 $cartIcon.removeClass("floatable"); 128 $cartDropdown.removeClass("floatable"); 129 } else { 130 $cartIcon.addClass("floatable"); 131 $cartDropdown.addClass("floatable"); 132 } 133 }, 134 135 _observeElementVisibility: function (elementIds, callback) { 136 const ids = Array.isArray(elementIds) ? elementIds : [elementIds]; 137 138 ids.forEach(function(elementId) { 139 const element = document.getElementById(elementId); 140 141 if (!element) { 142 return; 143 } 144 145 const observer = new IntersectionObserver( 146 function(entries) { 147 callback(entries[0].isIntersecting, elementId); 148 }, 149 { 150 root: null, 151 threshold: 0.01 152 } 153 ); 154 155 observer.observe(element); 156 }); 157 }, 158
159 _isFloatingControls: function () { 160 if ($("#rec1-container").hasClass("floating-controls")) { 161 this.dropdown.addClass("floating-controls"); 162 } else { 163 this.dropdown.removeClass("floating-controls"); 164 } 165 }, 166 167 _adjustCartPosition: function (event) { 168 var $cartIcon = this.$cartIcon; 169 var $cartDropdown = this.dropdown; 170 var $catalogTabs = this.$catalogTabs; 171 172 var startingOffset = 0; 173 if ($catalogTabs.length == 0) { 174 startingOffset = $cartIcon.offset().top; 175 } else { 176 startingOffset = $catalogTabs.offset().top; 177 } 178 179 if ($(".rec1-header-cp-toolbar").length) { 180 var toolbarHeight = parseInt($(".rec1-header-cp-toolbar").height()); 181 if (parseInt(this.$window.scrollTop()) > toolbarHeight - 10) { 182 $cartDropdown.addClass("scrollMarginTop"); 183 } else { 184 $cartDropdown.removeClass("scrollMarginTop"); 185 } 186 } 187 188 this._isFloatingControls(); 189 190 if (this.$highlightedItem) { 191 this._positionAddonPanel(); 192 } 193 194 // Re-adjust mobile cart height on window resize/orientation change 195 if ($cartDropdown.is(':visible')) { 196 this._adjustCartHeightForMobile(); 197 } 198 }, 199 200 _focusItemPrice: function (event) { 201 202 $(event.currentTarget).focus().select(); 203 }, 204 205 open: function (autoFocus) { 206 if (autoFocus === undefined) { 207 autoFocus = true; 208 } 209 this.dropdown.dropdown('open', null, autoFocus); 210 211 // Adjust cart height on mobile to prevent checkout button obstruction 212 this._adjustCartHeightForMobile(); 213 214 if (this.options.selectLatestAddons) { 215 // Get last item in the cart 216 var items = this.options.cart.items; 217 if (items.length > 0) { 218 var lastItem = items[items.length - 1]; 219 for (var i = 1; i <= items.length; i++) { 220 lastItem = items[items.length - i]; 221 if (lastItem instanceof CatalogItemMerchandise) { 222 break; 223 } 224 } 225 var $lastItem = this.dropdown.find(".cart-item:last"); 226 this._notifyItemClicked($lastItem, lastItem); 227 } 228 } 229 }, 230 231 _adjustCartHeightForMobile: function () { 232 var _this = this; 233 234 // Only run on mobile/small screens 235 if (window.innerWidth >= 768) { 236 return; 237 } 238 239 // Use requestAnimationFrame to ensure DOM is ready 240 requestAnimationFrame(function() { 241 var $checkoutButton = _this.dropdown.find('.cart-checkout-button, .cart-catalog-button'); 242 243 if ($checkoutButton.length === 0) { 244 return; 245 } 246 247 var checkoutRect = $checkoutButton[0].getBoundingClientRect(); 248 var viewportHeight = window.innerHeight; 249 250 // Check if checkout button bottom is beyond viewport bottom 251 // Add 20px buffer for safe measure 252 if (checkoutRect.bottom > (viewportHeight - 20)) { 253 // Get the totals section height to ensure it's always visible 254 var $totalsSection = _this.dropdown.find('.cart-contents-total'); 255 var totalsHeight = $totalsSection.length > 0 ? $totalsSection.outerHeight(true) : 0; 256 257 // Calculate how much we need to reduce 258 var overflow = checkoutRect.bottom - viewportHeight + 40; // 40px total buffer 259 var currentHeight = _this.dropdown.outerHeight(); 260 261 // Minimum height should accommodate: 262 // - At least some visible items 263 // - Full totals section 264 // - Checkout button 265 // We'll use 50vh or enough space for totals + 200px for items, whichever is larger
266 var minHeight = Math.max(viewportHeight * 0.5, totalsHeight + 200); 267 var newHeight = Math.max(currentHeight - overflow, minHeight); 268 269 // Set custom property for dynamic height 270 _this.dropdown[0].style.setProperty('--cart-height', newHeight + 'px'); 271 _this.dropdown.css({ 272 'height': newHeight + 'px', 273 'max-height': newHeight + 'px' 274 }); 275 } 276 }); 277 }, 278 279 refresh: function () { 280 if (this.options.quickPay && this.quickPayComplete) { 281 this.dropdown.html(this._renderQuickPayComplete()).enhanceWithin(); 282 } else { 283 this.dropdown.html(this._renderCartContents()).enhanceWithin(); 284 this._cacheDom(); 285 } 286 287 this._refreshQuickPayDisplay(); 288 this._maybeHideAddonPanel(); 289 }, 290 291 _cacheDom: function () { 292 this.$quickPayTotal = this.dropdown.find(".quickpay-total"); 293 this.$quickPayPaymentType = this.dropdown.find(".quickpay-payment-type"); 294 this.$quickPaySubmit = this.dropdown.find(".quickpay-submit"); 295 this.$quickPayOpenDrawer = this.dropdown.find(".quickpay-open-drawer"); 296 this.$quickPayCardMessage = this.dropdown.find(".quickpay-card-swipe-message"); 297 this.$quickPayCardPlaceholder = this.dropdown.find(".quickpay-card-swipe-placeholder"); 298 this.$quickPayCardInfo = this.dropdown.find(".quickpay-card-swipe-info"); 299 this.$quickPayCardCvv = this.dropdown.find(".quickpay-card-cvv"); 300 this.$quickPayChangeDue = this.dropdown.find(".quickpay-change-due"); 301 this.$quickPayAmount = this.dropdown.find("input[name='quickpayAmount']"); 302 this.$addonPanel = this._getAddonPanel(); 303 }, 304 305 _initQuickPayEvents: function () { 306 307 this._on(this.dropdown, { 308 "click .btn-quickpay-cash": "_quickPayCash", 309 "click .btn-quickpay-type": "_quickPayOther", 310 "click .quickpay-payment-clear": "_quickPayClearPayment", 311 "click .quickpay-card-clear": "_quickPayClearCard", 312 "click .quickpay-submit": "_quickPaySubmit", 313 "keyup .quickpay-card-cvv": "_quickPayCvvChange", 314 "change .quickpay-card-cvv": "_quickPayCvvChange", 315 "click .quickpay-done": "_quickPayDone" 316 }); 317 }, 318 319 _initQuickPaySwipe: function () { 320 var _this = this; 321 var cardReader = this.dropdown.cardReader(); 322 cardReader.cardReader("enable"); 323 cardReader.cardReader({ 324 scanned: function (event, data) { 325 // data.data contains keys for: 326 // firstname, lastname, cardNum, last4, expMonth, expYear, trackData 327 328 _this.options.quickPayData.cardData = data.data; 329 _this.options.quickPayData.paymentMethod = 'CREDIT/DEBIT'; 330 _this._refreshQuickPayDisplay(); 331 _this.$quickPayCardCvv.focus(); 332 333 }, 334 error: function (event, data) { 335 // data.error contains an error message 336 // data.format contains the card format that could not be read 337 console.log("card read error ", data); 338 alert(data.error); 339 } 340 }); 341 342 }, 343 344 _quickPaySwipe: function () { 345 }, 346 347 _quickPayReceipt: function () { 348 // TODO: Leverage existing ui-receipt class with data-id or whatever it is 349 }, 350 351 close: function () { 352 this.dropdown.dropdown('close'); 353 }, 354 355 _renderButton: function () { 356 var cart = this.options.cart, 357 empty = cart.isEmpty(); 358 this.element[empty ? 'addClass' : 'removeClass']('empty'); 359 var html = '<div class="ui-row"><div class="ui-col-4 text-center cart-icon"><i class="flaticon-cart6"></i></div>' + 360 '<div class="ui-col-8 cart-text"><div class="rec1-catalog-minicart-total">
360' + cart.getTotalString() + '</div>' + 361 '<div class="rec1-catalog-minicart-count">' + cart.getItemCountString() + '</div></div></div>'; 362 this.refreshMobileButton(); 363 364 return html; 365 }, 366 367 refreshButton: function () { 368 var cart = this.options.cart, 369 empty = cart.isEmpty(); 370 this.element[empty ? 'addClass' : 'removeClass']('empty'); 371 this.element.find(".rec1-catalog-minicart-total").html(cart.getTotalString()); 372 this.element.find(".rec1-catalog-minicart-count").html(cart.getItemCountString()); 373 this.refreshMobileButton(); 374 }, 375 376 refreshMobileButton: function () { 377 var cart = this.options.cart; 378 $('.header-mobile').find(".rec1-catalog-minicart-count").html(cart.getTotalItems()); 379 }, 380 381 _allowMultipleQuantity: function () { 382 var cart = this.options.cart, 383 items = cart.items, 384 i, item, j, childItem; 385 386 for (i in items) { 387 item = items[i]; 388 if (item.allowMultipleQuantity()) { 389 return true; 390 } 391 if (typeof item.children === 'object') { 392 for (j in item.children) { 393 childItem = item.children[j]; 394 if (childItem.allowMultipleQuantity()) { 395 return true; 396 } 397 } 398 } 399 } 400 return false; 401 }, 402 403 quickPayClearCompleted: function () { 404 if (this.quickPayComplete) { 405 406 this._quickPayDone(); 407 } 408 }, 409 410 _quickPayCash: function (event) { 411 var $target = $(event.currentTarget), 412 amount = $target.data('amount'); 413 414 var paymentType = this._getCashPaymentType(); 415 416 var surchargeAmount = (paymentType == null) ? 0 : Math.ceil(paymentType.surchargeAmt); 417 var surchargeAmountPercent = (paymentType == null) ? 0 : ((paymentType.surchargeAmtPercent * this.options.cart.totalCharges) / 100); 418 var surchargeAppliesOver = (paymentType == null) ? 0 : paymentType.surchargeAppliesOver; 419 var surchargeInternal = (paymentType == null) ? 0 : Math.ceil(paymentType.surchargeInternal); 420 var totalSurchargeAmt = surchargeInternal ? (surchargeAmount + surchargeAmountPercent) : 0; 421 422 if (amount === 'exact') { 423 this.options.quickPayData.amount = this.options.cart.totalCharges; 424 if (this.options.cart.totalCharges >= Math.ceil(surchargeAppliesOver)) { 425 this.options.quickPayData.amount += totalSurchargeAmt; 426 } 427 } else { 428 this.options.quickPayData.amount = this._addValue( 429 this.options.quickPayData.amount, 430 parseFloat(amount) 431 ); 432 } 433 this.options.quickPayData.paymentMethod = 'CASH'; 434 this.options.quickPayData.totalSurchargeAmt = totalSurchargeAmt; 435 this.options.quickPayData.surchargeAppliesOver = surchargeAppliesOver; 436 this.options.quickPayData.paymentTypeCash = true; 437 438 this._refreshQuickPayDisplay(); 439 }, 440 441 _getCashPaymentType: function () { 442 var cashPaymentType = null; 443 $.each(this.options.quickpayTypes, function (index, paymentType) { 444 if (paymentType.description.toUpperCase() === 'CASH') { 445 cashPaymentType = paymentType; 446 return false; 447 } 448 }); 449 return cashPaymentType; 450 }, 451 452 _quickPayOther: function (event) { 453 var $target = $(event.currentTarget), 454 id = $target.data('id'); 455 456 this.options.quickPayData.amount = this.options.cart.totalCharges; 457 this.options.quickPayData.paymentMethod = 'CUSTOM'; 458 this.options.quickPayData.paymentType = this._getPaymentType(id); 459 460 this._refreshQuickPayDisplay(); 461 }, 462 463 _getPaymentType: function (id) { 464 var matchedPaymentType = null; 465 $.each(this.options.quickpayTypes, function (index, paymentType) { 466 if (paymentType.id == id) { 467 matchedPaymentType = paymentType; 468 return false;
469 } 470 }); 471 return matchedPaymentType; 472 }, 473 474 _addValue: function (value1, value2) { 475 var result = value1 + value2; 476 // Prevent floating point errors 477 return parseFloat(result.toFixed(10)); 478 }, 479 480 _handleQuickPayAmountChange: function () { 481 482 var $input = this.$quickPayAmount, 483 options = this.options, 484 quickPayData = options.quickPayData, 485 amount = $input.val(); 486 487 this.options.quickPayData.amount = parseFloat(amount); 488 this.options.quickPayData.paymentMethod = 'CASH'; 489 490 this._refreshQuickPayDisplay(); 491 }, 492 493 _refreshQuickPayDisplay: function () { 494 var options = this.options; 495 var quickPayData = options.quickPayData; 496 var changeDue = ''; 497 var surchargeAmount = (this.options.quickPayData.paymentType == null) ? 0 : Math.ceil(this.options.quickPayData.paymentType.surchargeAmt); 498 var surchargeAmountPercent = (this.options.quickPayData.paymentType == null) ? 0 : ((this.options.quickPayData.paymentType.surchargeAmtPercent * options.cart.totalCharges) / 100); 499 var surchargeAppliesOver = (this.options.quickPayData.paymentType == null) ? 0 : this.options.quickPayData.paymentType.surchargeAppliesOver; 500 var surchargeInternal = (this.options.quickPayData.paymentType == null) ? 0 : Math.ceil(this.options.quickPayData.paymentType.surchargeInternal); 501 var totalSurchargeAmt = surchargeInternal ? (surchargeAmount + surchargeAmountPercent) : 0; 502 503 if (!options.quickPay || this.quickPayComplete) { 504 return; 505 } 506 507 const pageData = $(".ui-page").data("pageData"); 508 if (pageData && pageData.checkoutData) { 509 const $hostedPaymentContainer = $(".rec1-checkout-hosted-payment"); 510 511 if ($hostedPaymentContainer.data("checkoutHostedPayment") !== undefined) { 512 $hostedPaymentContainer.checkoutHostedPayment("option", "amount", this.options.cart.catalog.owedAmountCC); 513 } else { 514 $hostedPaymentContainer.checkoutHostedPayment({ 515 catalog: this.options.cart.catalog, 516 amount: this.options.cart.catalog.owedAmountCC, 517 buttonText: "Pay with Credit/Debit" 518 }); 519 } 520 } 521 522 var displayedPaymentMethod = quickPayData.paymentMethod; 523 524 if (quickPayData.paymentMethod === 'CREDIT/DEBIT') { 525 quickPayData.amount = options.cart.totalCharges; 526 var cardInfo = quickPayData.cardData.cardType + ' Ending In ' + quickPayData.cardData.last4; 527 changeDue = 'N/A (Credit/Debit)'; 528 quickPayData.changeDue = 0; 529 this.$quickPayCardInfo.html(cardInfo); 530 this.$quickPayCardMessage.hide(); 531 this.$quickPayCardPlaceholder.show(); 532 this.$quickPayTotal.attr('disabled', 'disabled').addClass('disabled'); 533 } else if (quickPayData.paymentMethod === 'CUSTOM') { 534 quickPayData.amount = options.cart.totalCharges; 535 536 if (options.cart.totalCharges >= Math.ceil(surchargeAppliesOver)) { 537 quickPayData.amount += totalSurchargeAmt; 538 } 539 540 quickPayData.changeDue = 0; 541 displayedPaymentMethod = quickPayData.paymentType.description; 542 changeDue = 'N/A'; 543 this.$quickPayCardInfo.empty(); 544 this.$quickPayCardMessage.show(); 545 this.$quickPayCardPlaceholder.hide(); 546 this.$quickPayTotal.removeAttr('disabled').removeClass('disabled'); 547 } else { 548 quickPayData.paymentMethod = 'CASH'; 549 550 var changeDueRaw = parseFloat((quickPayData.amount - options.cart.totalCharges).toFixed(10)); 551 552 if (options.cart.totalCharges >= Math.ceil(surchargeAppliesOver)) { 553 changeDueRaw -= totalSurchargeAmt; 554 } 555 556 quickPayData.changeDue = changeDueRaw; 557 558 if (changeDueRaw > 0) { 559 changeDue = '$' + changeDueRaw.toFixed(2); 560 } else { 561 changeDue = '$0.00'; 562 } 563 564 this.$quickPayCardInfo.empty(); 565 this.$quickPayCardMessage.show(); 566 this.$quickPayCardPlaceholder.hide(); 567 this.$quickPayTotal.removeAttr('disabled').removeClass('disabled'); 568 } 569 570 if (this.options.quickPayData.paymentMethod === 'CASH' && this.options.quickPayData.paymentTypeCash) { 571 572 if (options.cart.totalCharges >= Math.ceil(this.options.quickPayData.surchargeAppliesOver)) { 573 changeDueRaw -= this.options.quickPayData.totalSurchargeAmt; 574 } 575 576 if (changeDueRaw > 0) { 577 changeDue = '$' + changeDueRaw.toFixed(2); 578 } else { 579 changeDue = '$0.00'; 580 } 581 } 582 583 quickPayData.changeDueFormatted = changeDue; 584 var enableQuickPaySubmit = quickPayData.amount >= options.cart.totalCharges; 585 if (quickPayData.paymentMethod === 'CREDIT/DEBIT' && !quickPayData.cardData.cvv) { 586 this.$quickPayCardCvv.parent().addClass("has-danger"); 587 enableQuickPaySubmit = false;
588 } else { 589 this.$quickPayCardCvv.parent().removeClass("has-danger"); 590 } 591 592 if (this.options.cart.items.length == 0) { 593 enableQuickPaySubmit = false; 594 } 595 596 this.$quickPaySubmit.btn(enableQuickPaySubmit ? 'enable' : 'disable'); 597 this.$quickPayTotal.cash('val', quickPayData.amount); 598 this.$quickPayPaymentType.html(displayedPaymentMethod); 599 this.$quickPayChangeDue.html(changeDue); 600 601 this.dropdown.trigger("quickpayDisplayed"); 602 }, 603 604 _quickPayClearPayment: function () { 605 this.options.quickPayData.amount = 0; 606 this.options.quickPayData.paymentMethod = 'CASH'; 607 this.options.quickPayData.cardData = null; 608 this.options.quickPayData.paymentType = null; 609 this.options.quickPayData.paymentTypeCash = false; 610 this._refreshQuickPayDisplay(); 611 }, 612 613 _quickPayClearCard: function () { 614 this._quickPayClearPayment(); 615 }, 616 617 submitCapturedPayment: function (transactionData, amount) { 618 if (typeof transactionData !== "string") { 619 transactionData = JSON.stringify(transactionData); 620 } 621 this.options.quickPayData.transactionData = transactionData; 622 this.options.quickPayData.paymentMethod = 'CREDIT/DEBIT'; 623 this.options.quickPayData.amount = amount; 624 this._quickPaySubmit(); 625 }, 626 627 _quickPaySubmit: function () { 628 var _this = this; 629 this.dropdown.loadingmask(); 630 this.options.cart.catalog.submitQuickPay( 631 this.options.quickPayData, 632 this._quickPaySubmitted.bind(this), 633 { 634 // Supress alert for errors, we'll handle manually 635 // and display a nice UI for the error 636 errorAlert: false 637 } 638 ).always(function (data) { 639 if (data.useApgCashDrawerApp === "1" && data.cashDrawerKick) { 640 var cashDrawer = new CashDrawerSocket(); 641 cashDrawer.openDrawer(); 642 } 643 _this.dropdown.loadingmask('remove'); 644 }).fail(function (errors) { 645 _this._displayQuickpayErrors(errors); 646 }); 647 }, 648 649 _quickPayCvvChange: function () { 650 this.options.quickPayData.cardData.cvv = this.$quickPayCardCvv.val(); 651 this._refreshQuickPayDisplay(); 652 }, 653 654 _quickPaySubmitted: function (data) { 655 656 // data should have changeDue, transactionId, receipt link type of stuff, etc 657 this.quickPayComplete = true; 658 659 this.quickPayResult = data; 660 661 this.options.catalogWidget.catalog('createCart'); 662 this.refresh(); 663 this._printReceipt(); 664 // Refresh current tab after a quickpay 665 this.options.cart.catalog.selectTab(this.options.cart.catalog.tabIndex); 666 }, 667 668 displayQuickpayErrors: function (errors) { 669 this._displayQuickpayErrors(errors); 670 }, 671 672 _displayQuickpayErrors: function (errors) { 673 var errorHtml = errors.join('<br>'); 674 this.dropdown.find(".quickpay-error").html(errorHtml).show(); 675 }, 676 677 _hideQuickpayErrors: function () { 678 this.dropdown.find(".quickpay-error").hide(); 679 }, 680 681 /** 682 * @param isTriggeredByUser Used to indicate a user interaction triggered this print command 683 * @private 684 */ 685 _printReceipt: function (isTriggeredByUser) { 686 687 var receiptPrinterConfig = this.options.receiptPrinterConfig, 688 _this = this; 689 690 if (receiptPrinterConfig.isUsed) { 691 var $receiptPrinterStatus = this.dropdown.find(".receipt-printer-status"); 692 //$iframe = $dropdown.find("iframe"), 693 //$iframeDocument = $(); 694 695 696 var shouldPrint = true; 697 if (!isTriggeredByUser && (receiptPrinterConfig.promptType == 'always' || receiptPrinterConfig.promptType == 'pos')) { 698 //shouldPrint = confirm("Print thermal receipt?"); 699 // Let's have a print button they can click instead of having this confirm popup 700 shouldPrint = false;
701 } 702 703 if (shouldPrint || _this.quickPayResult.cashDrawerKick) { 704 705 this.document.one("printerDonePrinting", function () { 706 $receiptPrinterStatus.slideUp(); 707 //$iframe[0].contentWindow.$("body").trigger("loadingCompleted"); 708 }); 709 710 var cashDrawerKick = isTriggeredByUser ? false : _this.quickPayResult.cashDrawerKick; 711 712 if ($receiptPrinterStatus.data('rec1TextReceiptPrinter')) { 713 $receiptPrinterStatus.textReceiptPrinter('option', 'transaction', _this.quickPayResult.transactionId); 714 $receiptPrinterStatus.textReceiptPrinter('option', 'autoDrawerKick', cashDrawerKick); 715 $receiptPrinterStatus.textReceiptPrinter('option', 'drawerNonce', _this.quickPayResult.drawerNonce); 716 $receiptPrinterStatus.textReceiptPrinter('printWhenReady'); 717 } else { 718 $receiptPrinterStatus.textReceiptPrinter({ 719 transaction: _this.quickPayResult.transactionId, 720 config: receiptPrinterConfig, 721 autoPrint: shouldPrint, 722 autoDrawerKick: cashDrawerKick, 723 drawerNonce: _this.quickPayResult.drawerNonce 724 }); 725 } 726 727 //console.log($receiptPrinterStatus); 728 } 729 730 } 731 732 }, 733 734 _quickPayDone: function () { 735 this.quickPayComplete = false; 736 this.quickPayResult = null; 737 this._resetQuickPayData(); 738 this.refresh(); 739 }, 740 741 _resetQuickPayData: function () { 742 this.options.quickPayData = { 743 amount: 0, 744 paymentMethod: '', 745 paymentType: null, 746 cardData: null 747 }; 748 }, 749 750 _renderQuickPay: function () { 751 var html = '<div class="quickpay-container form-horizontal">' + 752 '<input type="hidden" class="unpaid-amount" name="unpaidAmount" value="' + this.options.cart.catalog.owedAmountCC + '">' + 753 '<div class="heading-muted">QuickPay — (Cash/Credit/POS Only)</div>' + 754 '<div class="quickpay-error ui-notice-block bg-danger" style="display:none;"></div>' + 755 '<div class="quickpay-inner-container"><div>' + 756 '<div class="form-group row">' + 757 '<label class="col-xs-4 col-4 control-label col-form-label">CASH:</label>' + 758 '<div class="col-xs-8 col-8">' + 759 '<div class="btn-group">' + 760 '<button data-amount="1" class="btn btn-default btn-quickpay-cash">$1</button>' + 761 '<button data-amount="5" class="btn btn-default btn-quickpay-cash">$5</button>' + 762 '<button data-amount="10" class="btn btn-default btn-quickpay-cash">$10</button>' + 763 '<button data-amount="20" class="btn btn-default btn-quickpay-cash">$20</button>' + 764 '<button data-amount="exact" class="btn btn-default btn-quickpay-cash">Exact Change</button>' + 765 '</div>' + 766 '</div>' + 767 '</div>' + 768 this._renderCreditCardPayment() + 769 this._renderQuickPayTypes() + 770 '<div class="form-group row">' + 771 '<label class="col-xs-4 col-4 control-label col-form-label">PAYMENT SUMMARY:</label>' + 772 '<div class="col-xs-8 col-8">' + 773 '<input type="text" class="ui-cash input-size-5 quickpay-total form-control" name="quickpayAmount" value="' + this.options.quickPayData.amount + '"> ' + 774 '<span class="quickpay-payment-type">CASH</span> ' + 775 '<span class="quickpay-payment-clear">' + 776 '<button class="btn btn-sm btn-link quickpay-payment-clear">CLEAR</button>' + 777 '</span>' + 778 '</div>' + 779 '</div>' + 780 '<div class="form-group row">' + 781 '<label class="col-xs-4 col-4 control-label col-form-label">CHANGE DUE:</label>' + 782 '<div class="col-xs-8 col-8 form-control-static quickpay-change-due">TBD</div>' + 783 '</div>' + 784 '</div>' + 785 '<div class="text-right ui-voffset-lg">' + 786 '<div class="receipt-printer-status-notrans"></div>' + 787 '<button class="btn btn-link ui-dropdown-close" type="button">Close</button> ' + 788 '<button class="btn btn-lg btn-primary disabled quickpay-submit" data-icon="cart-checkout" disabled>Submit QuickPay</button>' + 789 '</div>' + 790 '</div>'; // End of inner-container 791 return html; 792 }, 793 794 _renderQuickPayEmv: function () { 795 // Don't include button when no EMV is configured 796 if (!$(".ui-page").data("pageData").emvConfiguration) { 797 return ''; 798 } 799 800 return '<span>' + 801 '<button type="button" class="btn btn-default cc-local-service-init disabled" disabled>Use EMV/Chip Reader</button>' + 802 '</span>'; 803 }, 804 805 _renderCPPay: function () { 806 // Don't include button when no EMV is configured 807 if (!$(".ui-page").data("pageData").isCPPay) { 808 return ''; 809 } 810 811 return '<div class="rec1-checkout-hosted-payment" style="display: inline"></div>'; 812 }, 813
814 _renderCreditCardPayment: function () { 815 var html = '<div class="form-group row">' + 816 '<label class="col-xs-4 col-4 control-label col-form-label" style="padding-top: 12px;">CREDIT/DEBIT:</label>' + 817 '<div class="col-xs-8 col-8 form-control-static">' + 818 '<div class="row">'; 819 820 if (!$(".ui-page").data("pageData").isCPPay) { 821 html += '<div class="col-xs-4 col-4" style="padding-top: 6px;">' + 822 '<span class="quickpay-card-swipe-message">Swipe Card Now</span>' + 823 '</div>'; 824 } 825 826 html += '<div class="col-xs-8 col-8">' + 827 this._renderQuickPayEmv() + 828 this._renderCPPay() + 829 '</div>' + 830 '</div>' + 831 '<span class="quickpay-card-swipe-placeholder" style="display:none;">' + 832 '<span class="quickpay-card-swipe-info"></span>' + 833 '<span class="quickpay-card-swipe-cvv numeric ui-offset-lg">CVV: <input type="text" name="cvv" class="quickpay-card-cvv" size="4"></span>' + 834 '<span><btn class="btn btn-link btn-sm quickpay-card-clear">CLEAR</btn></span>' + 835 '</span>' + 836 '</div>' + 837 '</div>'; 838 839 return html; 840 }, 841 842 _renderQuickPayTypes: function () { 843 var html = ''; 844 if (this.options.quickpayTypes && this.options.quickpayTypes.length) { 845 html = '<div class="form-group row">' + 846 '<label class="col-xs-4 col-4 control-label col-form-label">OTHER:</label>' + 847 '<div class="col-xs-8 col-8">' + 848 '<div class="btn-group">'; 849 850 $.each(this.options.quickpayTypes, function (index, paymentType) { 851 html += '<button data-id="' + paymentType.id + '" class="btn btn-default btn-quickpay-type">' + paymentType.description + '</button>'; 852 }); 853 854 html += '</div>' + 855 '</div>' + 856 '</div>'; 857 } 858 return html; 859 }, 860 861 _renderQuickPayComplete: function () { 862 var data = this.quickPayResult; 863 var html = '<div class="form-horizontal">' + 864 '<h2>TRANSACTION COMPLETE</h2>' + 865 '<div class="form-group">' + 866 '<label class="form-label col-xs-4">Transaction:</label>' + 867 '<div class="col-xs-8 form-control-static">' + data.transactionLink + '</div>' + 868 '</div>' + 869 '<div class="form-group">' + 870 '<label class="form-label col-xs-4">Amount Tendered:</label>' + 871 '<div class="col-xs-8 form-control-static">$' + (this.options.quickPayData.amount).toFixed(2) + ' ' + this.options.quickPayData.paymentMethod + '</div>' + 872 '</div>' + 873 '<div class="form-group">' + 874 '<label class="form-label col-xs-4">Change Due:</label>' + 875 '<div class="col-xs-8 form-control-static">' + this.options.quickPayData.changeDueFormatted + '</div>' + 876 '</div>' + 877 '</div>' + 878 '<div class="ui-voffset-lg text-center">' + 879 '<button class="btn btn-lg btn-info ui-receipt-popup" data-id="' + data.transactionId + '" type="button" data-icon="file8">Receipt (' + data.transactionId + ')</button></div>' + 880 '</div>' + 881 this._renderThermalPrintButton(data) + 882 '<div class="ui-voffset-lg text-center">' + 883 '<button class="btn btn-default quickpay-done" type="button" data-icon="cart6">Done: New Cart</button></div>' + 884 '</div>' + 885 '<div class="receipt-printer-status"></div>'; 886 887 return html; 888 }, 889 890 _renderThermalPrintButton: function (data) { 891 if (!this.options.receiptPrinterConfig.isUsed) { 892 return ''; 893 } 894 return '<div class="ui-voffset-lg text-center">' + 895 '<button class="btn btn-default quickpay-thermal-print" t
895ype="button" data-icon="file8">Thermal Print</button></div>' + 896 '</div>'; 897 }, 898 899 _renderCartContents: function () { 900 var cart = this.options.cart, 901 items = cart.items, 902 total = cart.totalChargesFormatted, 903 checkoutKey = cart.catalog.checkoutKey, 904 showQuantity = this._allowMultipleQuantity(), 905 showQuantityClass = showQuantity ? ' has-quantity' : ''; 906 907 // Heading 908 var html = '<div class="rec1-cart-flex"><div class="cart-contents ' + showQuantityClass + '"><table class="ui-table">' + 909 '<thead><tr>' + 910 '<th>#</th>' + 911 '<th>Item</th>'; 912 913 if (showQuantity) { 914 html += '<th align="center">Quantity</th>'; 915 } 916 917 html += '<th align="right">Total</th>' + 918 '<th> </th>' + 919 '</tr></thead>'; 920 921 // All items 922 var itemCount = items.length, 923 newIndex = itemCount - this.options.newItemCount; 924 925 for (var i in items) { 926 var item = items[i], 927 newItem = i >= newIndex; 928 929 if (!item.displayInCart) { 930 continue; 931 } 932 933 html += '<tbody class="cart-item-group">' + this._renderItem(item, i, newItem, showQuantity) + '</tbody>'; 934 935 if (item.children && item.children.length > 0) { 936 for (var j in item.children) { 937 var childItem = item.children[j]; 938 html += this._renderItem(childItem, i, newItem, showQuantity, j, item); 939 } 940 } 941 } 942 943 if (itemCount == 0) { 944 html += '<tr><td colspan="5"><div class="cart-contents-empty"><div class="empty-icon"><i class="flaticon-cart"></i></div> Your cart is empty</div></td></tr>'; 945 } 946 947 // Footer 948 html += '</table></div><div class="cart-contents-total ' + showQuantityClass + '"><table class="ui-table">'; 949 950 // Tax totals 951 if (Object.keys(cart.taxTotals).length > 0) { 952 for (var i in cart.taxTotals) { 953 html += '<tr><th> </th>'; 954 955 if (showQuantity) { 956 html += '<th> </th>'; 957 } 958 959 html += '<th>' + cart.taxTotals[i].description + '</th>' + 960 '<th align="right">' + cart.taxTotals[i].totalFormatted + '</th>' + 961 '<th></th>' + 962 '</tr>'; 963 } 964 } 965 966 // Total 967 html += '<tr>' + 968 '<th> </th>'; 969 970 if (showQuantity) { 971 html += '<th> </th>'; 972 } 973 974 html += '<th>TOTAL</th>' + 975 '<th align="right">' + total + '</th>' + 976 '<th> </th>' + 977 '</tr>'; 978 979 980 var hasItems = items.length !== 0; 981 982 983 // Checkout Link 984 var footerColspan = showQuantity ? 5 : 4; 985 986 html += '<tr>' + 987 '<td colspan="' + footerColspan + '" align="right">' + 988 '<button class="btn btn-link ui-dropdown-close" type="button">Close</button> '; 989 990 if (this.options.checkoutButton) { 991 var checkoutDisabled = hasItems ? '' : 'disabled'; 992 html += '<a ' + checkoutDisabled + ' class="btn btn-primary cart-checkout-button" href="' + this.options.basePath + 'checkout/index/' + checkoutKey + '">' + 993 '<i class="flaticon-cart-checkout"></i><span>Checkout</span></a>'; 994 } else { 995 html += '<a class="btn btn-primary cart-catalog-button" href="' + this.options.basePath + 'catalog/index/' + checkoutKey + '">' + 996 '<i class="flaticon-cart6"></i><span>Browse Catalog</span></a>'; 997 } 998 999 html += '</td>' + 1000 '</tr></table></div>'; 1001 1002 1003 // Quickpay box 1004 if (this.options.quickPay && (this.options.cart.catalog.canQuickPay)) { 1005 html += '<div class="cart-contents-quickpay"><table class="ui-table quickpay-table"><tr class="bordered-top"><td colspan="' + footerColspan + '">' + this._renderQuickPay() + '</td></tr></table></div>'; 1006 this.dropdown.addClass("cart-quickpay"); 1007 } else { 1008 this.dropdown.removeClass("cart-quickpay"); 1009 } 1010 1011 1012 html += '</table></div>'; 1013 1014 // Reset "new" items to 0 1015 this.options.newItemCount = 0; 1016 1017 return html; 1018 }, 1019 1020 _renderItem: function (item, index, isNew, displayQuantity, subIndex, parentItem) { 1021 var rowNum = (parseInt(index) + 1) + '.', 1022 rowClass = 'cart-item', 1023 nameClass = 'cart-item-name', 1024 parentId = ''; 1025 1026 if (parentItem) { 1027 rowClass += ' cart-item-child'; 1028 nameClass = 'cart-item-name-child'; 1029 parentId = ' data-parent="' + parentItem.hash + '"'; 1030 rowNum = ' '; 1031 } 1032 1033 if (isNew) { 1034 rowClass += ' new-item'; 1035 } 1036 1037 var html = '<tr class="' + rowClass + '" data-hash="' + item.hash + '" ' + parentId + '>' + 1038 '<td>' + rowNum + '</td>' + 1039 '<td class="' + nameClass + '" title="' + item.description.replace(/"/g, '"') + '">' + item.description + '</td>'; 1040 1041 if (displayQuantity) { 1042 if (item.allowMultipleQuantity()) { 1043 var overriddenHtml = item.quantityOverridden ? '<div class="small text-warning text-center"><strong>(Quantity limit exceeded)</strong></div>' : ''; 1044 html += '<td align="center"><div class="cart-item-quantity-wrapper input-group input-group-inline">' + 1045 '<div class="input-group-btn"><button type="button" class="btn btn-default cart-item-quantity-decrease" data-icon="minus"></button></div>' + 1046 '<input type="text" value="' + item.quantity + 1047 '" name="item-quantity[' + item.hash + ']" class="cart-item-quantity integer numeric form-control">' + 1048 '<div class="input-group-btn"><button type="button" class="btn btn-default cart-item-quantity-increase" data-icon="plus"></button></div>' + 1049 '</div>
1049' + overriddenHtml + '</td>'; 1050 } else { 1051 html += '<td align="center">' + item.quantity + '</td>' 1052 } 1053 } 1054 1055 html += '<td align="right">'; 1056 if (this.options.editable) { 1057 html += '<input type="text" class="ui-cash cart-item-price" value="' + item.itemTotal + '">'; 1058 } else { 1059 html += item.itemTotalFormatted; 1060 } 1061 html += '</td>'; 1062 1063 1064 // "Remove" column 1065 html += '<td>'; 1066 1067 if ((parentItem && (this.options.childrenRemovable === true 1068 || (this.options.childrenRemovable === 'auto' && item.removable))) 1069 || (!parentItem && this.options.removable && item.removable)) { 1070 html += '<a href="Javascript:void(0);" class="flaticon-close cart-item-remove"></a>'; 1071 } 1072 html += '</td></tr>'; 1073 1074 if (item.allowNote) { 1075 var colspan = displayQuantity ? 3 : 2; 1076 html += '<tr data-hash="' + item.hash + '"><td> </td>' + 1077 '<td colspan="' + colspan + '">' + 1078 '<input type="text" value="' + item.getNoteEscaped() + 1079 '" name="item-note[' + item.hash + ']" class="cart-item-note" placeholder="Note...">' + 1080 '</div>' + 1081 '</td><td> </td>'; 1082 } 1083 1084 1085 return html; 1086 }, 1087 1088 scrollToNewItems: function () { 1089 if (this.dropdown.length !== 0) { 1090 var $newItems = this.dropdown.find(".new-item"); 1091 if ($newItems.length !== 0) { 1092 var $newItem = $newItems.eq(0); 1093 this.dropdown.scrollTop($newItem.position().top); 1094 } 1095 } 1096 }, 1097 1098 _removeItem: function (event) { 1099 var _this = this, 1100 $item = $(event.currentTarget).closest("tr"), 1101 hash = $item.data("hash"), 1102 parent = $item.data("parent"); 1103 1104 $item.loadingmask(); 1105 1106 // Cart.removeItem returns a jQuery promise, 1107 // which will resolve when the item is removed on the server side 1108 this.options.cart.removeItem(hash, parent).done(function (data) { 1109 if (data.errors && data.errors.length) { 1110 return; 1111 } 1112 if (_this.options.catalogWidget) { 1113 _this.options.catalogWidget.catalog('updateCart'); 1114 } 1115 if ($item.data("loadingmask") !== undefined) { 1116 $item.loadingmask("remove"); 1117 } 1118 _this.refresh(); 1119 _this._updateDropdownPosition(); 1120 }).always(function () { 1121 if ($item && $item.data('uiLoadingmask')) { 1122 $item.loadingmask('remove'); 1123 } 1124 }); 1125 1126 return false; 1127 }, 1128 1129 _updateQuantity: function (event) { 1130 const _this = this, 1131 $target = $(event.currentTarget), 1132 $item = $target.closest("tr"), 1133 hash = $item.data("hash"), 1134 parent = $item.data("parent"), 1135 quantity = parseInt($target.val()), 1136 item = this.options.cart.getItem(hash, parent); 1137 1138 if (isNaN(quantity) || quantity < 1) { 1139 $target.val(item.quantity); 1140 return; 1141 } 1142 1143 $item.loadingmask(); 1144 1145 const oldQuantity = item.quantity; 1146 item.quantity = quantity; 1147 $target.val(quantity); 1148 1149 // CatalogItem.updateQuantity returns a jQuery promise, 1150 // which will resolve when the item is removed on the server side 1151 item.updateQuantity(quantity) 1152 .fail(this._handleCountUpdateFailed($target, item, oldQuantity)) 1153 .always(function () { 1154 if (_this.options.catalogWidget) { 1155 _this.options.catalogWidget.catalog('updateCart'); 1156 } 1157 $item.loadingmask('remove'); 1158 _this._updateDropdownPosition(); 1159 _this.refresh(); 1160 }); 1161 1162 return false;
1163 }, 1164 1165 _updatePrice: function (event) { 1166 var _this = this, 1167 $target = $(event.currentTarget), 1168 $item = $target.closest("tr"), 1169 hash = $item.data("hash"), 1170 parent = $item.data("parent"), 1171 price = parseFloat($target.cash('val')), 1172 item = this.options.cart.getItem(hash, parent); 1173 1174 if (isNaN(price) || price < 0) { 1175 $target.cash('val', item.itemTotal); 1176 return; 1177 } 1178 1179 $item.loadingmask(); 1180 1181 // CatalogItem.updateQuantity returns a jQuery promise, 1182 // which will resolve when the item is removed on the server side 1183 item.setPrice(price).always(function () { 1184 if (_this.options.catalogWidget) { 1185 _this.options.catalogWidget.catalog('updateCart'); 1186 } 1187 $item.loadingmask('remove'); 1188 _this._updateDropdownPosition(); 1189 _this.refresh(); 1190 }); 1191 1192 return false; 1193 }, 1194 1195 _checkoutClicked: function (event) { 1196 this.dropdown.dropdown('close'); 1197 }, 1198 1199 _decreaseQuantity: function (event) { 1200 const _this = this, 1201 $target = $(event.currentTarget), 1202 $item = $target.closest("tr"), 1203 $quantity = $item.find(".cart-item-quantity"), 1204 $buttons = $item.find(".cart-item-quantity-decrease, .cart-item-quantity-increase"), 1205 hash = $item.data("hash"), 1206 parent = $item.data("parent"), 1207 item = this.options.cart.getItem(hash, parent), 1208 quantity = parseInt($quantity.val()) - 1; 1209 1210 if (quantity <= 0) { 1211 return; 1212 } 1213 1214 $item.loadingmask(); 1215 $buttons.prop('disabled', true); 1216 const oldQuantity = item.quantity; 1217 item.quantity = quantity; 1218 $quantity.val(quantity); 1219 1220 // CatalogItem.updateQuantity returns a jQuery promise, 1221 // which will resolve when the item is removed on the server side 1222 item.updateQuantity(quantity) 1223 .fail(this._handleCountUpdateFailed($quantity, item, oldQuantity)) 1224 .always(function () { 1225 _this._changedQuantity.bind(_this); 1226 $buttons.prop('disabled', false); 1227 $item.loadingmask('remove'); 1228 if (_this.options.catalogWidget) { 1229 _this.options.catalogWidget.catalog('updateCart'); 1230 } 1231 _this._updateDropdownPosition(); 1232 _this.refresh(); 1233 }); 1234 1235 return false; 1236 }, 1237 1238 _handleCountUpdateFailed: function ($quantityControl, item, oldQuantity) { 1239 return function () { 1240 item.quantity = oldQuantity; 1241 item.refreshVisibleItems(); 1242 $quantityControl.val(oldQuantity); 1243 }; 1244 }, 1245 1246 _increaseQuantity: function (event) { 1247 const _this = this, 1248 $target = $(event.currentTarget), 1249 $item = $target.closest("tr"), 1250 $quantity = $item.find(".cart-item-quantity"), 1251 $buttons = $item.find(".cart-item-quantity-decrease, .cart-item-quantity-increase"), 1252 hash = $item.data("hash"), 1253 parent = $item.data("parent"), 1254 item = this.options.cart.getItem(hash, parent), 1255 quantity = parseInt($quantity.val()) + 1; 1256 1257 $item.loadingmask(); 1258 $buttons.prop('disabled', true); 1259 const oldQuantity = item.quantity; 1260 item.quantity = quantity; 1261 $quantity.val(quantity); 1262 1263 // CatalogItem.updateQuantity returns a jQuery promise, 1264 // which will resolve when the item is removed on the server side 1265 item.updateQuantity(quantity) 1266 .fail(this._handleCountUpdateFailed($quantity, item, oldQuantity)) 1267 .always(function () { 1268 _this._changedQuantity.bind(_this); 1269 $buttons.prop('disabled', false); 1270 $item.loadingmask('remove'); 1271 if (_this.options.catalogWidget) { 1272 _this.options.catalogWidget.catalog('updateCart'); 1273 } 1274 _this._updateDropdownPosition(); 1275 _this.refresh(); 1276 }); 1277 1278 return false;
1279 }, 1280 1281 _changedQuantity: function () { 1282 this.refresh(); 1283 }, 1284 1285 _handleCartItemNoteKeydown: function (event) { 1286 if (event.which === $.ui.keyCode.ENTER) { 1287 var _this = this, 1288 $target = $(event.currentTarget), 1289 $item = $target.closest("tr"), 1290 $note = $item.find(".cart-item-note"), 1291 hash = $item.data("hash"), 1292 parent = $item.data("parent"), 1293 item = this.options.cart.getItem(hash); 1294 1295 item.setNote($note.val()); 1296 1297 $note.blur(); 1298 } 1299 }, 1300 1301 _handleCartItemNoteBlur: function (event) { 1302 var _this = this, 1303 $target = $(event.currentTarget), 1304 $item = $target.closest("tr"), 1305 $note = $item.find(".cart-item-note"), 1306 hash = $item.data("hash"), 1307 parent = $item.data("parent"), 1308 item = this.options.cart.getItem(hash); 1309 1310 item.setNote($note.val()); 1311 }, 1312 1313 _handleCartItemNoteSaveClick: function (event) { 1314 var _this = this, 1315 $target = $(event.currentTarget), 1316 $item = $target.closest("tr"), 1317 $note = $item.find(".cart-item-note"), 1318 hash = $item.data("hash"), 1319 parent = $item.data("parent"), 1320 item = this.options.cart.getItem(hash); 1321 1322 item.setNote($note.val()); 1323 }, 1324 1325 _handleCartItemClick: function (e) { 1326 var $item = $(e.currentTarget), 1327 hash = $item.data("hash"), 1328 parent = $item.data("parent"), 1329 item = this.options.cart.getItem(hash, parent); 1330 1331 this._notifyItemClicked($item, item); 1332 }, 1333 1334 _notifyItemClicked: function ($item, item) { 1335 if (this.isHighlightedItem($item)) { 1336 this._hideAddonPanel(); 1337 this._highlightNone(); 1338 return; 1339 } 1340 1341 // Only items with addons can be selected 1342 if (!item.setAddonsByIds) { 1343 return; 1344 } 1345 1346 if (item.possibleAddons && item.possibleAddons.length) { 1347 this._showAddonPanel(item); 1348 this._highlightItem($item, item); 1349 } else { 1350 this._hideAddonPanel(); 1351 } 1352 }, 1353 1354 _showAddonPanel: function (item) { 1355 this._getAddonPanel().show(); 1356 this._positionAddonPanel(); 1357 this.refreshAddonPanelContents(item); 1358 }, 1359 1360 _maybeHideAddonPanel: function () { 1361 if (this.$addonPanel) { 1362 this._hideAddonPanel(); 1363 } 1364 }, 1365 1366 _hideAddonPanel: function () { 1367 this._getAddonPanel().hide(); 1368 this._highlightNone(); 1369 }, 1370 1371 _createAddonPanel: function (e) { 1372 var _this = this, 1373 $panel = $("<div/>"); 1374 $panel[0].className = 'rec1-cart-dropdown-addon-panel ui-dropdown'; 1375 $panel[0].style.display = 'none'; 1376 $panel.on("click", ".addon-panel-save", function (e) { 1377 _this._saveAddonPanel(); 1378 _this._hideAddonPanel(); 1379 1380 if (_this.options.selectLatestAddons) { 1381 _this.options.selectLatestAddons = false; 1382 } else { 1383 e.preventDefault(); 1384 e.stopPropagation(); 1385 } 1386 }); 1387 // Don't let our dropdown close 1388 $panel.on("click", function (event) { 1389 event.stopPropagation(); 1390 }); 1391 this.dropdown.before($panel); 1392 return $panel; 1393 }, 1394 1395 _renderAddonPanelContents: function (item) { 1396 var _this = this; 1397 var html = '<div class="heading-muted text-center">OPTIONS/ADDONS (' + item.description + ')</div>'; 1398 html += '<ul class="ui-listview" data-checkable="true" data-inset="false">'; 1399 //'<li class="ui-listview-heading">ADDONS</li>'; 1400 1401 1402 //<li class="checkable 1403 $.each(item.possibleAddons, function (index, addon) { 1404 var hasAddon = item.hasAddon(addon.id), 1405 checked = hasAddon ? 'checked' : ''; 1406 1407 html += '<li class="checkable">' + 1408 '<label>' + 1409 '<input type="checkbox" name="addons[]" value="' + addon.id + '" ' + checked + '>
1409' + 1410 '<h2>' + addon.name + '</h2>' + 1411 _this._renderAddonAmountBadge(addon) 1412 '</label>' + 1413 '</li>'; 1414 }); 1415 1416 html += '</ul>'; 1417 1418 html += '<div class="ui-offset-lg text-center">' + 1419 '<button class="btn btn-primary addon-panel-save" type="button">Save</button>' + 1420 '</div>'; 1421 1422 return html; 1423 }, 1424 1425 _renderAddonAmountBadge: function (addon) { 1426 if (addon.amount != 0) { 1427 var displayAmount = addon.meas == "1" 1428 ? addon.amount + '%' 1429 : '$' + parseFloat(addon.amount).toFixed(2); 1430 1431 var html = '<div class="badge listview-badge">' + displayAmount + '</div>'; 1432 return html; 1433 } 1434 return ''; 1435 }, 1436 1437 refreshAddonPanelContents: function (item) { 1438 this.$addonPanel.html(this._renderAddonPanelContents(item)).enhanceWithin(); 1439 }, 1440 1441 1442 _positionAddonPanel: function () { 1443 this.$addonPanel.position({ 1444 my: 'right top', 1445 at: 'left top+40', 1446 of: this.dropdown 1447 }); 1448 }, 1449 1450 _getAddonPanel: function () { 1451 this.$addonPanel = this.$addonPanel || this._createAddonPanel(); 1452 return this.$addonPanel; 1453 }, 1454 1455 _saveAddonPanel: function () { 1456 var item = this._highlightedItem, 1457 addonIds = $.map(this.$addonPanel.find("input").serializeArray(), function (paramElement) { 1458 return paramElement.value; 1459 }); 1460 1461 // do the save here 1462 item.setAddonsByIds(addonIds); 1463 }, 1464 1465 _highlightNone: function () { 1466 if (this.$highlightedItem) { 1467 this.$highlightedItem.removeClass(this.options.highlightClass); 1468 this.$highlightedItem = null; 1469 this._highlightedItem = null; 1470 } 1471 }, 1472 1473 _highlightItem: function ($item, item) { 1474 this._highlightNone(); 1475 this.$highlightedItem = $item; 1476 this._highlightedItem = item; 1477 $item.addClass(this.options.highlightClass).removeClass("new-item"); 1478 }, 1479 1480 isHighlightedItem: function ($item) { 1481 return this.$highlightedItem && this.$highlightedItem[0] === $item[0]; 1482 }, 1483 1484 _handleThermalPrintClick: function (e) { 1485 this._printReceipt(true); 1486 }, 1487 1488 logDecline: function (paymentData, amount) { 1489 var data = { 1490 amount: amount, 1491 payer: 0, 1492 preCapturedData: JSON.stringify(paymentData) 1493 }; 1494 1495 var result = this.options.cart.catalog.logDecline(0, data); 1496 return result; 1497 }, 1498 1499 _updateDropdownPosition: function() { 1500 // For Bootstrap 3: recalculate dropdown position after content changes 1501 if (typeof $.fn.dropdown === 'function' && typeof bootstrap === 'undefined') { 1502 this.dropdown.dropdown('update'); 1503 } 1504 } 1505 1506 }); 1507 1508})(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.