diff --git a/cartridges/adyen_controllers_changes/app_storefront_controllers_changes/cartridge/controllers/COBilling.js b/cartridges/adyen_controllers_changes/app_storefront_controllers_changes/cartridge/controllers/COBilling.js
new file mode 100644
index 000000000..7cf14bea6
--- /dev/null
+++ b/cartridges/adyen_controllers_changes/app_storefront_controllers_changes/cartridge/controllers/COBilling.js
@@ -0,0 +1,849 @@
+'use strict';
+
+/**
+ * Controller for the billing logic. It is used by both the single shipping and the multishipping
+ * functionality and is responsible for payment method selection and entering a billing address.
+ *
+ * @module controllers/COBilling
+ */
+
+/* API Includes */
+var GiftCertificate = require('dw/order/GiftCertificate');
+var GiftCertificateMgr = require('dw/order/GiftCertificateMgr');
+var GiftCertificateStatusCodes = require('dw/order/GiftCertificateStatusCodes');
+var PaymentInstrument = require('dw/order/PaymentInstrument');
+var PaymentMgr = require('dw/order/PaymentMgr');
+var ProductListMgr = require('dw/customer/ProductListMgr');
+var Resource = require('dw/web/Resource');
+var Status = require('dw/system/Status');
+var StringUtils = require('dw/util/StringUtils');
+var Transaction = require('dw/system/Transaction');
+var URLUtils = require('dw/web/URLUtils');
+var Countries = require('app_storefront_core/cartridge/scripts/util/Countries');
+
+/* Script Modules */
+var app = require('~/cartridge/scripts/app');
+var guard = require('~/cartridge/scripts/guard');
+// ### Custom Adyen cartridge start ###
+var AdyenController = require("int_adyen_controllers/cartridge/controllers/Adyen");
+var AdyenHelper = require("int_adyen_overlay/cartridge/scripts/util/adyenHelper");
+var AdyenConfigs = require("int_adyen_overlay/cartridge/scripts/util/adyenConfigs");
+var constants = require("*/cartridge/adyenConstants/constants");
+// ### Custom Adyen cartridge end ###
+var BasketMgr = require('dw/order/BasketMgr');
+var OrderMgr = require('dw/order/OrderMgr');
+
+/**
+ * Initializes the address form. If the customer chose "use as billing
+ * address" option on the single shipping page the form is prepopulated with the shipping
+ * address, otherwise it prepopulates with the billing address that was already set.
+ * If neither address is available, it prepopulates with the default address of the authenticated customer.
+ */
+function initAddressForm(cart) {
+ if (app.getForm('singleshipping').object.shippingAddress.useAsBillingAddress.value === true) {
+ app.getForm('billing').object.billingAddress.addressFields.firstName.value = app.getForm('singleshipping').object.shippingAddress.addressFields.firstName.value;
+ app.getForm('billing').object.billingAddress.addressFields.lastName.value = app.getForm('singleshipping').object.shippingAddress.addressFields.lastName.value;
+ app.getForm('billing').object.billingAddress.addressFields.address1.value = app.getForm('singleshipping').object.shippingAddress.addressFields.address1.value;
+ app.getForm('billing').object.billingAddress.addressFields.address2.value = app.getForm('singleshipping').object.shippingAddress.addressFields.address2.value;
+ app.getForm('billing').object.billingAddress.addressFields.city.value = app.getForm('singleshipping').object.shippingAddress.addressFields.city.value;
+ app.getForm('billing').object.billingAddress.addressFields.postal.value = app.getForm('singleshipping').object.shippingAddress.addressFields.postal.value;
+ app.getForm('billing').object.billingAddress.addressFields.phone.value = app.getForm('singleshipping').object.shippingAddress.addressFields.phone.value;
+ app.getForm('billing').object.billingAddress.addressFields.states.state.value = app.getForm('singleshipping').object.shippingAddress.addressFields.states.state.value;
+ app.getForm('billing').object.billingAddress.addressFields.country.value = app.getForm('singleshipping').object.shippingAddress.addressFields.country.value;
+ app.getForm('billing').object.billingAddress.addressFields.phone.value = app.getForm('singleshipping').object.shippingAddress.addressFields.phone.value;
+ } else if (cart.getBillingAddress() !== null) {
+ app.getForm('billing.billingAddress.addressFields').copyFrom(cart.getBillingAddress());
+ app.getForm('billing.billingAddress.addressFields.states').copyFrom(cart.getBillingAddress());
+ } else if (customer.authenticated && customer.profile.addressBook.preferredAddress !== null) {
+ app.getForm('billing.billingAddress.addressFields').copyFrom(customer.profile.addressBook.preferredAddress);
+ app.getForm('billing.billingAddress.addressFields.states').copyFrom(customer.profile.addressBook.preferredAddress);
+ }
+}
+
+/**
+ * Initializes the email address form field. If there is already a customer
+ * email set at the basket, that email address is used. If the
+ * current customer is authenticated the email address of the customer's profile
+ * is used.
+ */
+function initEmailAddress(cart) {
+ if (cart.getCustomerEmail() !== null) {
+ app.getForm('billing').object.billingAddress.email.emailAddress.value = cart.getCustomerEmail();
+ } else if (customer.authenticated && customer.profile.email !== null) {
+ app.getForm('billing').object.billingAddress.email.emailAddress.value = customer.profile.email;
+ }
+}
+
+/**
+ * Updates data for the billing page and renders it.
+ * If payment method is set to gift certificate, gets the gift certificate code from the form.
+ * Updates the page metadata. Gets a view and adds any passed parameters to it. Sets the Basket and ContinueURL properties.
+ * Renders the checkout/billing/billing template.
+ * @param {module:models/CartModel~CartModel} cart - A CartModel wrapping the current Basket.
+ * @param {object} params - (optional) if passed, added to view properties so they can be accessed in the template.
+ */
+// ### Custom Adyen cartridge start ###
+function returnToForm(cart, params) {
+ var pageMeta = require('~/cartridge/scripts/meta');
+
+ // if the payment method is set to gift certificate get the gift certificate code from the form
+ if (!empty(cart.getPaymentInstrument()) && cart.getPaymentInstrument().getPaymentMethod() === PaymentInstrument.METHOD_GIFT_CERTIFICATE) {
+ app.getForm('billing').copyFrom({
+ giftCertCode: cart.getPaymentInstrument().getGiftCertificateCode()
+ });
+ }
+ pageMeta.update({
+ pageTitle: Resource.msg('billing.meta.pagetitle', 'checkout', 'SiteGenesis Checkout')
+ });
+ if (params) {
+ app.getView(require('~/cartridge/scripts/object').extend(params, {
+ Basket: cart.object,
+ AdyenHelper: AdyenHelper,
+ ContinueURL: URLUtils.https('COBilling-Billing')
+ })).render('checkout/billing/billing');
+ } else {
+ app.getView({
+ Basket: cart.object,
+ AdyenHelper: AdyenHelper,
+ ContinueURL: URLUtils.https('COBilling-Billing')
+ }).render('checkout/billing/billing');
+ }
+}
+// ### Custom Adyen cartridge end ###
+
+/**
+ * Updates cart calculation and page information and renders the billing page.
+ * @transactional
+ * @param {module:models/CartModel~CartModel} cart - A CartModel wrapping the current Basket.
+ * @param {object} params - (optional) if passed, added to view properties so they can be accessed in the template.
+ */
+function start(cart, params) {
+ app.getController('COShipping').PrepareShipments();
+ Transaction.wrap(function () {
+ cart.calculate();
+ });
+ var pageMeta = require('~/cartridge/scripts/meta');
+ pageMeta.update({
+ pageTitle: Resource.msg('billing.meta.pagetitle', 'checkout', 'SiteGenesis Checkout')
+ });
+ returnToForm(cart, params);
+}
+
+/**
+ * Initializes the credit card list by determining the saved customer payment methods for the current locale.
+ * @param {module:models/CartModel~CartModel} cart - A CartModel wrapping the current Basket.
+ * @return {object} JSON object with members ApplicablePaymentMethods and ApplicableCreditCards.
+ */
+function initCreditCardList(cart) {
+ var paymentAmount = cart.getNonGiftCertificateAmount();
+ var countryCode;
+ var applicablePaymentMethods;
+ var applicablePaymentCards;
+ var applicableCreditCards;
+ countryCode = Countries.getCurrent({
+ CurrentRequest: {
+ locale: request.locale
+ }
+ }).countryCode;
+ applicablePaymentMethods = PaymentMgr.getApplicablePaymentMethods(customer, countryCode, paymentAmount.value);
+ applicablePaymentCards = PaymentMgr.getPaymentMethod(PaymentInstrument.METHOD_CREDIT_CARD).getApplicablePaymentCards(customer, countryCode, paymentAmount.value);
+ app.getForm('billing').object.paymentMethods.creditCard.type.setOptions(applicablePaymentCards.iterator());
+ applicableCreditCards = null;
+ if (customer.authenticated) {
+ var profile = app.getModel('Profile').get();
+ if (profile) {
+ applicableCreditCards = profile.validateWalletPaymentInstruments(countryCode, paymentAmount.getValue()).ValidPaymentInstruments;
+ }
+ }
+ return {
+ ApplicablePaymentMethods: applicablePaymentMethods,
+ ApplicableCreditCards: applicableCreditCards
+ };
+}
+
+/**
+ * Starting point for billing. After a successful shipping setup, both COShipping
+ * and COShippingMultiple call this function.
+ */
+// ### Custom Adyen cartridge start ###
+function publicStart() {
+ var cart = app.getModel('Cart').get();
+ if (cart) {
+ // Initializes all forms of the billing page including: - address form - email address - coupon form
+ initAddressForm(cart);
+ initEmailAddress(cart);
+
+ // Get the Saved Cards from Adyen to get latest saved cards
+ if (customer.authenticated) {
+ require('int_adyen_overlay/cartridge/scripts/updateSavedCards').updateSavedCards({
+ CurrentCustomer: customer
+ });
+ }
+ var creditCardList = initCreditCardList(cart);
+ var applicablePaymentMethods = creditCardList.ApplicablePaymentMethods;
+ var billingForm = app.getForm('billing').object;
+ var paymentMethods = billingForm.paymentMethods;
+ if (paymentMethods.valid) {
+ paymentMethods.selectedPaymentMethodID.setOptions(applicablePaymentMethods.iterator());
+ } else {
+ paymentMethods.clearFormElement();
+ }
+ app.getForm('billing.couponCode').clear();
+ app.getForm('billing.giftCertCode').clear();
+ var AdyenSessionsResponse = AdyenController.Sessions(customer);
+
+ // var AdyenPosTerminals = AdyenController.GetTerminals();
+ //TODO fix terminals
+ start(cart, {
+ ApplicableCreditCards: creditCardList.ApplicableCreditCards,
+ AdyenSessionsResponse: AdyenSessionsResponse
+ });
+ } else {
+ app.getController('Cart').Show();
+ }
+}
+// ### Custom Adyen cartridge end ###
+
+/**
+ * Adjusts gift certificate redemptions after applying coupon(s), because this changes the order total.
+ * Removes and then adds currently added gift certificates to reflect order total changes.
+ */
+function adjustGiftCertificates() {
+ var i, j, cart, gcIdList, gcID, gc;
+ cart = app.getModel('Cart').get();
+ if (cart) {
+ gcIdList = cart.getGiftCertIdList();
+ Transaction.wrap(function () {
+ for (i = 0; i < gcIdList.length; i += 1) {
+ cart.removeGiftCertificatePaymentInstrument(gcIdList[i]);
+ }
+ gcID = null;
+ for (j = 0; j < gcIdList.length; j += 1) {
+ gcID = gcIdList[j];
+ gc = GiftCertificateMgr.getGiftCertificateByCode(gcID);
+ if (gc &&
+ // make sure exists
+ gc.isEnabled() &&
+ // make sure it is enabled
+ gc.getStatus() !== GiftCertificate.STATUS_PENDING &&
+ // make sure it is available for use
+ gc.getStatus() !== GiftCertificate.STATUS_REDEEMED &&
+ // make sure it has not been fully redeemed
+ gc.balance.currencyCode === cart.getCurrencyCode()) {
+ // make sure the GC is in the right currency
+ cart.createGiftCertificatePaymentInstrument(gc);
+ }
+ }
+ });
+ }
+}
+
+/**
+ * Used to adjust gift certificate totals, update page metadata, and render the billing page.
+ * This function is called whenever a billing form action is handled.
+ * @see {@link module:controllers/COBilling~returnToForm|returnToForm}
+ * @see {@link module:controllers/COBilling~adjustGiftCertificates|adjustGiftCertificates}
+ * @see {@link module:controllers/COBilling~billing|billing}
+ */
+function handleCoupon() {
+ var CouponError;
+ // @FIXME what is that used for?
+ if (empty(CouponError)) {
+ /*
+ * Adjust gift certificate redemptions as after applying coupon(s),
+ * order total is changed. AdjustGiftCertificate pipeline removes and
+ * then adds currently added gift certificates to reflect order total
+ * changes.
+ */
+ adjustGiftCertificates();
+ }
+ returnToForm(app.getModel('Cart').get());
+}
+
+/**
+ * Redeems a gift certificate. If the gift certificate was not successfully
+ * redeemed, the form field is invalidated with the appropriate error message.
+ * If the gift certificate was redeemed, the form gets cleared. This function
+ * is called by an Ajax request and generates a JSON response.
+ * @param {String} giftCertCode - Gift certificate code entered into the giftCertCode field in the billing form.
+ * @returns {object} JSON object containing the status of the gift certificate.
+ */
+function redeemGiftCertificate(giftCertCode) {
+ var cart, gc, newGCPaymentInstrument, gcPaymentInstrument, status, result;
+ cart = app.getModel('Cart').get();
+ if (cart) {
+ // fetch the gift certificate
+ gc = GiftCertificateMgr.getGiftCertificateByCode(giftCertCode);
+ if (!gc) {
+ // make sure exists
+ result = new Status(Status.ERROR, GiftCertificateStatusCodes.GIFTCERTIFICATE_NOT_FOUND);
+ } else if (!gc.isEnabled()) {
+ // make sure it is enabled
+ result = new Status(Status.ERROR, GiftCertificateStatusCodes.GIFTCERTIFICATE_DISABLED);
+ } else if (gc.getStatus() === GiftCertificate.STATUS_PENDING) {
+ // make sure it is available for use
+ result = new Status(Status.ERROR, GiftCertificateStatusCodes.GIFTCERTIFICATE_PENDING);
+ } else if (gc.getStatus() === GiftCertificate.STATUS_REDEEMED) {
+ // make sure it has not been fully redeemed
+ result = new Status(Status.ERROR, GiftCertificateStatusCodes.GIFTCERTIFICATE_INSUFFICIENT_BALANCE);
+ } else if (gc.balance.currencyCode !== cart.getCurrencyCode()) {
+ // make sure the GC is in the right currency
+ result = new Status(Status.ERROR, GiftCertificateStatusCodes.GIFTCERTIFICATE_CURRENCY_MISMATCH);
+ } else {
+ newGCPaymentInstrument = Transaction.wrap(function () {
+ gcPaymentInstrument = cart.createGiftCertificatePaymentInstrument(gc);
+ cart.calculate();
+ return gcPaymentInstrument;
+ });
+ status = new Status(Status.OK);
+ status.addDetail('NewGCPaymentInstrument', newGCPaymentInstrument);
+ result = status;
+ }
+ } else {
+ result = new Status(Status.ERROR, 'BASKET_NOT_FOUND');
+ }
+ return result;
+}
+
+/**
+ * Updates credit card information from the httpParameterMap and determines if there is a currently selected credit card.
+ * If a credit card is selected, it adds the the credit card number to the billing form. Otherwise, the {@link module:controllers/COBilling~publicStart|publicStart} method is called.
+ * In either case, it will initialize the credit card list in the billing form and call the {@link module:controllers/COBilling~start|start} function.
+ */
+function updateCreditCardSelection() {
+ var cart, applicableCreditCards, UUID, selectedCreditCard, instrumentsIter, creditCardInstrument;
+ cart = app.getModel('Cart').get();
+ applicableCreditCards = initCreditCardList(cart).ApplicableCreditCards;
+ UUID = request.httpParameterMap.creditCardUUID.value || request.httpParameterMap.dwfrm_billing_paymentMethods_creditCardList.stringValue;
+ selectedCreditCard = null;
+ if (UUID && applicableCreditCards && !applicableCreditCards.empty) {
+ // find credit card in payment instruments
+ instrumentsIter = applicableCreditCards.iterator();
+ while (instrumentsIter.hasNext()) {
+ creditCardInstrument = instrumentsIter.next();
+ if (UUID.equals(creditCardInstrument.UUID)) {
+ selectedCreditCard = creditCardInstrument;
+ }
+ }
+ if (selectedCreditCard) {
+ app.getForm('billing').object.paymentMethods.creditCard.number.value = selectedCreditCard.creditCardNumber;
+ } else {
+ publicStart();
+ }
+ } else {
+ publicStart();
+ }
+ app.getForm('billing.paymentMethods.creditCard').copyFrom(selectedCreditCard);
+ initCreditCardList(cart);
+ start(cart);
+}
+
+/**
+ * Clears the form element for the currently selected payment method and removes the other payment methods.
+ *
+ * @return {Boolean} Returns true if payment is successfully reset. Returns false if the currently selected payment
+ * method is bml and the ssn cannot be validated.
+ */
+function resetPaymentForms() {
+ var cart = app.getModel('Cart').get();
+ var status = Transaction.wrap(function () {
+ if (app.getForm('billing').object.paymentMethods.selectedPaymentMethodID.value.equals('PayPal')) {
+ app.getForm('billing').object.paymentMethods.creditCard.clearFormElement();
+ app.getForm('billing').object.paymentMethods.bml.clearFormElement();
+ cart.removePaymentInstruments(cart.getPaymentInstruments(PaymentInstrument.METHOD_CREDIT_CARD));
+ cart.removePaymentInstruments(cart.getPaymentInstruments(PaymentInstrument.METHOD_BML));
+ } else if (app.getForm('billing').object.paymentMethods.selectedPaymentMethodID.value.equals(PaymentInstrument.METHOD_CREDIT_CARD)) {
+ app.getForm('billing').object.paymentMethods.bml.clearFormElement();
+ cart.removePaymentInstruments(cart.getPaymentInstruments(PaymentInstrument.METHOD_BML));
+ cart.removePaymentInstruments(cart.getPaymentInstruments('PayPal'));
+ } else if (app.getForm('billing').object.paymentMethods.selectedPaymentMethodID.value.equals(PaymentInstrument.METHOD_BML)) {
+ app.getForm('billing').object.paymentMethods.creditCard.clearFormElement();
+ if (!app.getForm('billing').object.paymentMethods.bml.ssn.valid) {
+ return false;
+ }
+ cart.removePaymentInstruments(cart.getPaymentInstruments(PaymentInstrument.METHOD_CREDIT_CARD));
+ cart.removePaymentInstruments(cart.getPaymentInstruments('PayPal'));
+ }
+ return true;
+ });
+ return status;
+}
+
+/**
+ * Validates the billing form.
+ * @returns {boolean} Returns true if the billing address is valid or no payment is needed. Returns false if the billing form is invalid.
+ */
+function validateBilling() {
+ if (!app.getForm('billing').object.billingAddress.valid) {
+ return false;
+ }
+ if (!empty(request.httpParameterMap.noPaymentNeeded.value)) {
+ return true;
+ }
+ if (!empty(app.getForm('billing').object.paymentMethods.selectedPaymentMethodID.value) && app.getForm('billing').object.paymentMethods.selectedPaymentMethodID.value.equals(PaymentInstrument.METHOD_CREDIT_CARD)) {
+ if (!app.getForm('billing').object.valid) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Handles the selection of the payment method and performs payment method-specific
+ * validation and verification on the entered form fields. If the
+ * order total is 0 (if the user has product promotions) then we do not
+ * need a valid payment method.
+ */
+function handlePaymentSelection(cart) {
+ var result;
+ if (empty(app.getForm('billing').object.paymentMethods.selectedPaymentMethodID.value)) {
+ if (cart.getTotalGrossPrice() > 0) {
+ result = {
+ error: true
+ };
+ } else {
+ result = {
+ ok: true
+ };
+ }
+ }
+
+ // skip the payment handling if the whole payment was made using gift cert
+ if (app.getForm('billing').object.paymentMethods.selectedPaymentMethodID.value.equals(PaymentInstrument.METHOD_GIFT_CERTIFICATE)) {
+ result = {
+ ok: true
+ };
+ }
+ if (empty(PaymentMgr.getPaymentMethod(app.getForm('billing').object.paymentMethods.selectedPaymentMethodID.value).paymentProcessor)) {
+ result = {
+ error: true,
+ MissingPaymentProcessor: true
+ };
+ }
+ if (!result) {
+ result = app.getModel('PaymentProcessor').handle(cart.object, app.getForm('billing').object.paymentMethods.selectedPaymentMethodID.value);
+ }
+ return result;
+}
+
+/**
+ * Gets or creates a billing address and copies it to the billingaddress form. Also sets the customer email address
+ * to the value in the billingAddress form.
+ * @transaction
+ * @param {module:models/CartModel~CartModel} cart - A CartModel wrapping the current Basket.
+ * @returns {boolean} true
+ */
+function handleBillingAddress(cart) {
+ var billingAddress = cart.getBillingAddress();
+ Transaction.wrap(function () {
+ if (!billingAddress) {
+ billingAddress = cart.createBillingAddress();
+ }
+ app.getForm('billing.billingAddress.addressFields').copyTo(billingAddress);
+ app.getForm('billing.billingAddress.addressFields.states').copyTo(billingAddress);
+ cart.setCustomerEmail(app.getForm('billing').object.billingAddress.email.emailAddress.value);
+ });
+ return true;
+}
+
+/**
+ * Checks if there is currently a cart and if one exists, gets the customer address from the httpParameterMap and saves it to the customer address book.
+ * Initializes the list of credit cards and calls the {@link module:controllers/COBilling~start|start} function.
+ * If a cart does not already exist, calls the {@link module:controllers/Cart~Show|Cart controller Show function}.
+ */
+function updateAddressDetails() {
+ var cart, address, billingAddress;
+ cart = app.getModel('Cart').get();
+ if (cart) {
+ address = customer.getAddressBook().getAddress(empty(request.httpParameterMap.addressID.value) ? request.httpParameterMap.dwfrm_billing_addressList.value : request.httpParameterMap.addressID.value);
+ app.getForm('billing.billingAddress.addressFields').copyFrom(address);
+ app.getForm('billing.billingAddress.addressFields.states').copyFrom(address);
+ billingAddress = cart.getBillingAddress();
+ app.getForm('billing.billingAddress.addressFields').copyTo(billingAddress);
+ initCreditCardList(cart);
+ start(cart);
+ } else {
+ //@FIXME redirect
+ app.getController('Cart').Show();
+ }
+}
+
+/**
+ * Form handler for the billing form. Handles the following actions:
+ * - __applyCoupon__ - gets the coupon to add from the httpParameterMap couponCode property and calls {@link module:controllers/COBilling~handleCoupon|handleCoupon}
+ * - __creditCardSelect__ - calls the {@link module:controllers/COBilling~updateCreditCardSelection|updateCreditCardSelection} function.
+ * - __paymentSelect__ - calls the {@link module:controllers/COBilling~publicStart|publicStart} function.
+ * - __redeemGiftCert__ - redeems the gift certificate entered into the billing form and returns to the cart.
+ * - __save__ - validates payment and address information and handles any errors. If the billing form is valid,
+ * saves the billing address to the customer profile, sets a flag to indicate the billing step is successful, and calls
+ * the {@link module:controllers/COSummary~start|COSummary controller Start function}.
+ * - __selectAddress__ - calls the {@link module:controllers/COBilling~updateAddressDetails|updateAddressDetails} function.
+ */
+// ### Custom Adyen cartridge start ###
+function billing(data) {
+ // restore cart and redirect to billing stage if successful
+ if (session.privacy.currentOrderNumber && session.privacy.currentOrderToken) {
+ var order = OrderMgr.getOrder(session.privacy.currentOrderNumber, session.privacy.currentOrderToken);
+
+ // Clear cache so the order restore will only be attmpted once per order
+ session.privacy.currentOrderNumber = null;
+ session.privacy.currentOrderToken = null;
+ Transaction.wrap(function () {
+ OrderMgr.failOrder(order, true);
+ });
+ publicStart();
+ return;
+ }
+ if (!validateBilling()) {
+ var responseUtils = require('~/cartridge/scripts/util/Response');
+ responseUtils.renderJSON({
+ fieldErrors: true
+ });
+ }
+ var paymentInformation = app.getForm('adyPaydata');
+ if (paymentInformation.get("paymentFromComponentStateData").value()) {
+ AdyenController.ShowConfirmationPaymentFromComponent();
+ return;
+ }
+ app.getForm('billing').handleAction({
+ applyCoupon: function () {
+ var couponCode = request.httpParameterMap.couponCode.stringValue || request.httpParameterMap.dwfrm_billing_couponCode.stringValue;
+
+ // TODO what happened to this start node?
+ app.getController('Cart').AddCoupon(couponCode);
+ handleCoupon();
+ return;
+ },
+ creditCardSelect: function () {
+ updateCreditCardSelection();
+ return;
+ },
+ paymentSelect: function () {
+ // ToDo - pass parameter ?
+ publicStart();
+ return;
+ },
+ redeemGiftCert: function () {
+ var status = redeemGiftCertificate(app.getForm('billing').object.giftCertCode.htmlValue);
+ if (!status.isError()) {
+ returnToForm(app.getModel('Cart').get(), {
+ NewGCPaymentInstrument: status.getDetail('NewGCPaymentInstrument')
+ });
+ } else {
+ returnToForm(app.getModel('Cart').get());
+ }
+ return;
+ },
+ save: function () {
+ Transaction.wrap(function () {
+ var cart = app.getModel('Cart').get();
+ if (!resetPaymentForms() || !validateBilling() || !handleBillingAddress(cart) ||
+ // Performs validation steps, based upon the entered billing address
+ // and address options.
+ handlePaymentSelection(cart).error) {
+ // Performs payment method specific checks, such as credit card verification.
+ returnToForm(cart);
+ } else {
+ if (customer.authenticated && app.getForm('billing').object.billingAddress.addToAddressBook.value) {
+ app.getModel('Profile').get(customer.profile).addAddressToAddressBook(cart.getBillingAddress());
+ }
+ // Mark step as fulfilled
+ app.getForm('billing').object.fulfilled.value = true;
+ if (!paymentInformation.get("paymentFromComponentStateData").value()) {
+ // A successful billing page will jump to the next checkout step.
+ app.getController('COSummary').Start();
+ }
+ return;
+ }
+ });
+ },
+ selectAddress: function () {
+ updateAddressDetails();
+ return;
+ }
+ });
+}
+// ### Custom Adyen cartridge end ###
+
+/**
+* Gets the gift certificate code from the httpParameterMap and redeems it. For an ajax call, renders an empty JSON object.
+* Otherwise, renders a JSON object with information about the gift certificate code and the success and status of the redemption.
+*/
+function redeemGiftCertificateJson() {
+ var giftCertCode, giftCertStatus;
+ giftCertCode = request.httpParameterMap.giftCertCode.stringValue;
+ giftCertStatus = redeemGiftCertificate(giftCertCode);
+ let responseUtils = require('~/cartridge/scripts/util/Response');
+ if (request.httpParameterMap.format.stringValue !== 'ajax') {
+ // @FIXME we could also build an ajax guard?
+ responseUtils.renderJSON({});
+ } else {
+ responseUtils.renderJSON({
+ status: giftCertStatus.code,
+ success: !giftCertStatus.error,
+ message: Resource.msgf('billing.' + giftCertStatus.code, 'checkout', null, giftCertCode),
+ code: giftCertCode
+ });
+ }
+}
+
+/**
+ * Removes gift certificate from the basket payment instruments and
+ * generates a JSON response with a status. This function is called by an Ajax
+ * request.
+ */
+function removeGiftCertificate() {
+ if (!empty(request.httpParameterMap.giftCertificateID.stringValue)) {
+ var cart = app.getModel('Cart').get();
+ Transaction.wrap(function () {
+ cart.removeGiftCertificatePaymentInstrument(request.httpParameterMap.giftCertificateID.stringValue);
+ cart.calculate();
+ });
+ }
+ publicStart();
+}
+
+/**
+ * Updates the order totals and recalculates the basket after a coupon code is applied.
+ * Renders the checkout/minisummary template, which includes the mini cart order totals and shipment summary.
+ */
+function updateSummary() {
+ var cart = app.getModel('Cart').get();
+ Transaction.wrap(function () {
+ cart.calculate();
+ });
+ app.getView({
+ checkoutstep: 4,
+ Basket: cart.object
+ }).render('checkout/minisummary');
+}
+
+/**
+ * Renders a form dialog to edit an address. The dialog is supposed to be opened
+ * by an Ajax request and ends in templates, which trigger a certain JavaScript
+ * event. The calling page of this dialog is responsible for handling these
+ * events.
+ */
+function editAddress() {
+ app.getForm('billing').objectaddress.clearFormElement();
+ var address = customer.getAddressBook().getAddress(request.httpParameterMap.addressID.stringValue);
+ if (address) {
+ app.getForm('billinaddress').copyFrom(address);
+ app.getForm('billingaggdress.states').copyFrom(address);
+ }
+ app.getView({
+ ContinueURL: URLUtils.https('COBilling-EditBillingAddress')
+ }).render('checkout/billing/billingaddressdetails');
+}
+
+/**
+ * Form handler for the returnToForm form.
+ * - __apply __ - attempts to save billing address information to the platform. If there is an error, renders the
+ * components/dialog/dialogapply template. If it is successful, sets the ContinueURL to {@link module:controllers/COBilling~EditBillingAddress|EditBillingAddress} and renders the
+ * checkout/billing/billingaddressdetails template.
+ * - __remove __ - Checks if the customer owns any product lists. If they do not, removes the address from the customer address book
+ * and renders the components/dialog/dialogdelete template.
+ * If they do own product lists, sets the ContinueURL to {@link module:controllers/COBilling~EditBillingAddress|EditBillingAddress} and renders the checkout/billing/billingaddressdetails template.
+ */
+function editBillingAddress() {
+ app.getForm('returnToForm').handleAction({
+ apply: function () {
+ if (!app.getForm('billingaddress').copyTo(app.getForm('billingaddress').object)) {
+ app.getView({
+ ContinueURL: URLUtils.https('COBilling-EditBillingAddress')
+ }).render('checkout/billing/billingaddressdetails');
+ } else {
+ app.getView().render('components/dialog/dialogapply');
+ }
+ },
+ remove: function () {
+ if (ProductListMgr.getProductLists(app.getForm('billing').objectaddress.object).isEmpty()) {
+ customer.getAddressBook().removeAddress(app.getForm('billing').objectaddress.object);
+ app.getView().render('components/dialog/dialogdelete');
+ } else {
+ app.getView({
+ ContinueURL: URLUtils.https('COBilling-EditBillingAddress')
+ }).render('checkout/billing/billingaddressdetails');
+ }
+ }
+ });
+}
+
+/**
+ * Returns information of a gift certificate including its balance as JSON
+ * response. Required to check the remaining balance.
+ */
+function getGiftCertificateBalance() {
+ var giftCertificate = GiftCertificateMgr.getGiftCertificateByCode(request.httpParameterMap.giftCertificateID.value);
+ var responseUtils = require('~/cartridge/scripts/util/Response');
+ if (giftCertificate && giftCertificate.isEnabled()) {
+ responseUtils.renderJSON({
+ giftCertificate: {
+ ID: giftCertificate.getGiftCertificateCode(),
+ balance: StringUtils.formatMoney(giftCertificate.getBalance())
+ }
+ });
+ } else {
+ responseUtils.renderJSON({
+ error: Resource.msg('billing.giftcertinvalid', 'checkout', null)
+ });
+ }
+}
+
+/**
+ * Selects a customer credit card and returns the details of the credit card as
+ * JSON response. Required to fill credit card form with details of selected
+ * credit card.
+ */
+function selectCreditCard() {
+ var cart, applicableCreditCards, selectedCreditCard, instrumentsIter, creditCardInstrument;
+ cart = app.getModel('Cart').get();
+ applicableCreditCards = initCreditCardList(cart).ApplicableCreditCards;
+ selectedCreditCard = null;
+
+ // ensure mandatory parameter 'CreditCardUUID' and 'CustomerPaymentInstruments'
+ // in pipeline dictionary and collection is not empty
+ if (request.httpParameterMap.creditCardUUID.value && applicableCreditCards && !applicableCreditCards.empty) {
+ // find credit card in payment instruments
+ instrumentsIter = applicableCreditCards.iterator();
+ while (instrumentsIter.hasNext()) {
+ creditCardInstrument = instrumentsIter.next();
+ if (request.httpParameterMap.creditCardUUID.value.equals(creditCardInstrument.UUID)) {
+ selectedCreditCard = creditCardInstrument;
+ }
+ }
+ if (selectedCreditCard) {
+ app.getForm('billing').object.paymentMethods.creditCard.number.value = selectedCreditCard.getCreditCardNumber();
+ }
+ }
+ app.getView({
+ SelectedCreditCard: selectedCreditCard
+ }).render('checkout/billing/creditcardjson');
+}
+
+/**
+ * Revalidates existing payment instruments in later checkout steps.
+ *
+ * @param {module:models/CartModel~CartModel} cart - A CartModel wrapping the current Basket.
+ * @return {Boolean} true if existing payment instruments are valid, false if not.
+ */
+// ### Custom Adyen cartridge start ###
+function validatePayment(cart) {
+ var paymentAmount, countryCode, invalidPaymentInstruments, result;
+ if (cart.getPaymentInstrument() && [constants.METHOD_ADYEN_POS, constants.METHOD_ADYEN_COMPONENT].indexOf(cart.getPaymentInstrument().getPaymentMethod()) !== -1) {
+ result = true;
+ return result;
+ }
+ if (app.getForm('billing').object.fulfilled.value) {
+ paymentAmount = cart.getNonGiftCertificateAmount();
+ countryCode = Countries.getCurrent({
+ CurrentRequest: {
+ locale: request.locale
+ }
+ }).countryCode;
+ invalidPaymentInstruments = cart.validatePaymentInstruments(customer, countryCode, paymentAmount.value).InvalidPaymentInstruments;
+ if (!invalidPaymentInstruments && cart.calculatePaymentTransactionTotal()) {
+ result = true;
+ } else {
+ app.getForm('billing').object.fulfilled.value = false;
+ result = false;
+ }
+ } else {
+ result = false;
+ }
+ return result;
+}
+// ### Custom Adyen cartridge end ###
+
+/**
+ * Attempts to save the used credit card in the customer payment instruments.
+ * The logic replaces an old saved credit card with the same masked credit card
+ * number of the same card type with the new credit card. This ensures creating
+ * only unique cards as well as replacing expired cards.
+ * @transactional
+ * @return {Boolean} true if credit card is successfully saved.
+ */
+// ### Custom Adyen cartridge start ###
+function saveCreditCard() {
+ if (AdyenConfigs.getAdyenRecurringPaymentsEnabled()) {
+ //saved credit cards are handling in COPlaceOrder and Login for Adyen - saved cards are synced with Adyen ListRecurringDetails API call
+ return true;
+ } else {
+ var i, creditCards, newCreditCard;
+ if (customer.authenticated && app.getForm('billing').object.paymentMethods.creditCard.saveCard.value) {
+ creditCards = customer.getProfile().getWallet().getPaymentInstruments(PaymentInstrument.METHOD_CREDIT_CARD);
+ Transaction.wrap(function () {
+ newCreditCard = customer.getProfile().getWallet().createPaymentInstrument(PaymentInstrument.METHOD_CREDIT_CARD);
+
+ // copy the credit card details to the payment instrument
+ newCreditCard.setCreditCardHolder(app.getForm('billing').object.paymentMethods.creditCard.owner.value);
+ newCreditCard.setCreditCardNumber(app.getForm('billing').object.paymentMethods.creditCard.number.value);
+ newCreditCard.setCreditCardExpirationMonth(app.getForm('billing').object.paymentMethods.creditCard.expiration.month.value);
+ newCreditCard.setCreditCardExpirationYear(app.getForm('billing').object.paymentMethods.creditCard.expiration.year.value);
+ newCreditCard.setCreditCardType(app.getForm('billing').object.paymentMethods.creditCard.type.value);
+ for (i = 0; i < creditCards.length; i++) {
+ var creditcard = creditCards[i];
+ if (creditcard.maskedCreditCardNumber === newCreditCard.maskedCreditCardNumber && creditcard.creditCardType === newCreditCard.creditCardType) {
+ customer.getProfile().getWallet().removePaymentInstrument(creditcard);
+ }
+ }
+ });
+ }
+ return true;
+ }
+}
+// ### Custom Adyen cartridge end ###
+
+/*
+* Module exports
+*/
+
+/*
+* Web exposed methods
+*/
+/** Starting point for billing.
+ * @see module:controllers/COBilling~publicStart */
+exports.Start = guard.ensure(['https'], publicStart);
+
+/** Redeems gift certificates.
+ * @see module:controllers/COBilling~redeemGiftCertificateJson */
+exports.RedeemGiftCertificateJson = guard.ensure(['https', 'get'], redeemGiftCertificateJson);
+/** Removes gift certificate from the basket payment instruments.
+ * @see module:controllers/COBilling~removeGiftCertificate */
+exports.RemoveGiftCertificate = guard.ensure(['https', 'get'], removeGiftCertificate);
+/** Updates the order totals and recalculates the basket after a coupon code is applied.
+ * @see module:controllers/COBilling~updateSummary */
+exports.UpdateSummary = guard.ensure(['https', 'get'], updateSummary);
+/** Gets the customer address and saves it to the customer address book.
+ * @see module:controllers/COBilling~updateAddressDetails */
+exports.UpdateAddressDetails = guard.ensure(['https', 'get'], updateAddressDetails);
+/** Renders a form dialog to edit an address.
+ * @see module:controllers/COBilling~editAddress */
+exports.EditAddress = guard.ensure(['https', 'get', 'csrf'], editAddress);
+/** Returns information of a gift certificate including its balance as JSON response.
+ * @see module:controllers/COBilling~getGiftCertificateBalance */
+exports.GetGiftCertificateBalance = guard.ensure(['https', 'get'], getGiftCertificateBalance);
+/** Selects a customer credit card and returns the details of the credit card as JSON response.
+ * @see module:controllers/COBilling~selectCreditCard */
+exports.SelectCreditCard = guard.ensure(['https', 'get'], selectCreditCard);
+/** Adds the currently selected credit card to the billing form and initializes the credit card selection list.
+ * @see module:controllers/COBilling~updateCreditCardSelection */
+exports.UpdateCreditCardSelection = guard.ensure(['https', 'get'], updateCreditCardSelection);
+/** Form handler for the billing form.
+ * @see module:controllers/COBilling~billing */
+exports.Billing = guard.ensure(['https', 'csrf'], billing);
+/** Form handler for the returnToForm form.
+ * @see module:controllers/COBilling~editBillingAddress */
+exports.EditBillingAddress = guard.ensure(['https', 'post'], editBillingAddress);
+/*
+ * Local methods
+ */
+/** Saves the credit card used in the billing form in the customer payment instruments.
+ * @see module:controllers/COBilling~saveCreditCard */
+exports.SaveCreditCard = saveCreditCard;
+/** Revalidates existing payment instruments in later checkout steps.
+ * @see module:controllers/COBilling~validatePayment */
+exports.ValidatePayment = validatePayment;
+/** Handles the selection of the payment method and performs payment method specific validation and verification upon the entered form fields.
+ * @see module:controllers/COBilling~handlePaymentSelection */
+exports.HandlePaymentSelection = handlePaymentSelection;
\ No newline at end of file
diff --git a/cartridges/adyen_controllers_changes/app_storefront_controllers_changes/cartridge/controllers/COPlaceOrder.js b/cartridges/adyen_controllers_changes/app_storefront_controllers_changes/cartridge/controllers/COPlaceOrder.js
new file mode 100644
index 000000000..2be72cdfb
--- /dev/null
+++ b/cartridges/adyen_controllers_changes/app_storefront_controllers_changes/cartridge/controllers/COPlaceOrder.js
@@ -0,0 +1,261 @@
+'use strict';
+
+/**
+ * Controller that creates an order from the current basket. It's a pure processing controller and does
+ * no page rendering. The controller is used by checkout and is called upon the triggered place order action.
+ * It contains the actual logic to authorize the payment and create the order. The controller communicates the result
+ * of the order creation process and uses a status object PlaceOrderError to set proper error states.
+ * The calling controller is must handle the results of the order creation and evaluate any errors returned by it.
+ *
+ * @module controllers/COPlaceOrder
+ */
+
+/* API Includes */
+var OrderMgr = require('dw/order/OrderMgr');
+var PaymentMgr = require('dw/order/PaymentMgr');
+var Status = require('dw/system/Status');
+var Transaction = require('dw/system/Transaction');
+
+/* Script Modules */
+var app = require('~/cartridge/scripts/app');
+var guard = require('~/cartridge/scripts/guard');
+var Cart = app.getModel('Cart');
+var Order = app.getModel('Order');
+var PaymentProcessor = app.getModel('PaymentProcessor');
+
+/**
+ * Responsible for payment handling. This function uses PaymentProcessorModel methods to
+ * handle payment processing specific to each payment instrument. It returns an
+ * error if any of the authorizations failed or a payment
+ * instrument is of an unknown payment method. If a payment method has no
+ * payment processor assigned, the payment is accepted as authorized.
+ *
+ * @transactional
+ * @param {dw.order.Order} order - the order to handle payments for.
+ * @return {Object} JSON object containing information about missing payments, errors, or an empty object if the function is successful.
+ */
+// ### Custom Adyen cartridge start ###
+function handlePayments(order) {
+ if (order.getTotalNetPrice().value !== 0.00) {
+ var paymentInstruments = order.getPaymentInstruments();
+ if (paymentInstruments.length === 0) {
+ return {
+ missingPaymentInfo: true
+ };
+ }
+ /**
+ * Sets the transaction ID for the payment instrument.
+ */
+ var handlePaymentTransaction = function () {
+ paymentInstrument.getPaymentTransaction().setTransactionID(order.getOrderNo());
+ };
+ for (var i = 0; i < paymentInstruments.length; i++) {
+ var paymentInstrument = paymentInstruments[i];
+ if (PaymentMgr.getPaymentMethod(paymentInstrument.getPaymentMethod()).getPaymentProcessor() === null) {
+ Transaction.wrap(handlePaymentTransaction);
+ } else {
+ var authorizationResult = PaymentProcessor.authorize(order, paymentInstrument);
+ if (authorizationResult.not_supported || authorizationResult.error) {
+ return {
+ error: true
+ };
+ }
+ if (authorizationResult.isAdyen) {
+ return authorizationResult;
+ }
+ }
+ }
+ }
+ return {};
+}
+// ### Custom Adyen cartridge end ###
+
+/**
+ * The entry point for order creation. This function is not exported, as this controller must only
+ * be called by another controller.
+ *
+ * @transactional
+ * @return {Object} JSON object that is empty, contains error information, or PlaceOrderError status information.
+ */
+// ### Custom Adyen cartridge start ###
+function start() {
+ var cart = Cart.get();
+ if (!cart) {
+ app.getController('Cart').Show();
+ return {};
+ }
+ var COShipping = app.getController('COShipping');
+
+ // Clean shipments.
+ COShipping.PrepareShipments(cart);
+
+ // Make sure there is a valid shipping address, accounting for gift certificates that do not have one.
+ if (cart.getProductLineItems().size() > 0 && cart.getDefaultShipment().getShippingAddress() === null) {
+ COShipping.Start();
+ return {};
+ }
+
+ // Make sure the billing step is fulfilled, otherwise restart checkout.
+ if (!session.forms.billing.fulfilled.value) {
+ app.getController('COCustomer').Start();
+ return {};
+ }
+ Transaction.wrap(function () {
+ cart.calculate();
+ });
+ var COBilling = app.getController('COBilling');
+ Transaction.wrap(function () {
+ if (!COBilling.ValidatePayment(cart)) {
+ COBilling.Start();
+ return {};
+ }
+ });
+
+ // Recalculate the payments. If there is only gift certificates, make sure it covers the order total, if not
+ // back to billing page.
+ Transaction.wrap(function () {
+ if (!cart.calculatePaymentTransactionTotal()) {
+ COBilling.Start();
+ return {};
+ }
+ });
+
+ // Handle used addresses and credit cards.
+ var saveCCResult = COBilling.SaveCreditCard();
+ if (!saveCCResult) {
+ return {
+ error: true,
+ PlaceOrderError: new Status(Status.ERROR, 'confirm.error.technical')
+ };
+ }
+
+ // Creates a new order. This will internally ReserveInventoryForOrder and will create a new Order with status
+ // 'Created'.
+ var order = cart.createOrder();
+ if (!order) {
+ // TODO - need to pass BasketStatus to Cart-Show ?
+ app.getController('Cart').Show();
+ return {};
+ }
+ var handlePaymentsResult = handlePayments(order);
+ var constants = require('*/cartridge/adyenConstants/constants');
+ var URLUtils = require('dw/web/URLUtils');
+
+ // Cache current order number in order to potentially restore cart.
+ session.privacy.currentOrderNumber = order.orderNo;
+ session.privacy.currentOrderToken = order.orderToken;
+ var submitOrder = handlePaymentsResult.isAdyen === false ||
+ //adyen is not the payment processor for this payment
+ handlePaymentsResult.isAdyen && !handlePaymentsResult.action ||
+ // isAdyen and no action
+ handlePaymentsResult.action && handlePaymentsResult.action.type === constants.ACTIONTYPES.VOUCHER ||
+ // action type is voucher
+ !handlePaymentsResult.action && !handlePaymentsResult.isFinal; // no action and payment is not final (SEPA)
+ if (handlePaymentsResult.error) {
+ return Transaction.wrap(function () {
+ OrderMgr.failOrder(order);
+ return {
+ continueUrl: URLUtils.url('Adyen-ShowConfirmation', 'error', 'true', 'errorStatus', 'confirm.error.technical').toString()
+ };
+ });
+ } else if (handlePaymentsResult.missingPaymentInfo) {
+ return Transaction.wrap(function () {
+ OrderMgr.failOrder(order);
+ return {
+ continueUrl: URLUtils.url('Adyen-ShowConfirmation', 'error', 'true', 'errorStatus', 'confirm.error.technical').toString()
+ };
+ });
+ } else {
+ if (submitOrder) {
+ var orderPlacementStatus = Order.submit(order);
+ if (!orderPlacementStatus.error) {
+ clearForms();
+ }
+ if (handlePaymentsResult.isAdyen) {
+ return {
+ continueUrl: URLUtils.url('Adyen-ShowConfirmation', 'authorized', 'true', 'merchantReference', order.orderNo, 'orderToken', order.orderToken).toString()
+ };
+ }
+ return orderPlacementStatus;
+ }
+ }
+ return handlePaymentsResult;
+}
+// ### Custom Adyen cartridge end ###
+
+function clearForms() {
+ // Clears all forms used in the checkout process.
+ session.forms.singleshipping.clearFormElement();
+ session.forms.multishipping.clearFormElement();
+ session.forms.billing.clearFormElement();
+
+ // clear cached order number
+ session.privacy.currentOrderNumber = null;
+ session.privacy.currentOrderToken = null;
+}
+
+/**
+ * Asynchronous Callbacks for OCAPI. These functions result in a JSON response.
+ * Sets the payment instrument information in the form from values in the httpParameterMap.
+ * Checks that the payment instrument selected is valid and authorizes the payment. Renders error
+ * message information if the payment is not authorized.
+ */
+function submitPaymentJSON() {
+ var order = Order.get(request.httpParameterMap.order_id.stringValue);
+ if (!order.object || request.httpParameterMap.order_token.stringValue !== order.getOrderToken()) {
+ app.getView().render('checkout/components/faults');
+ return;
+ }
+ session.forms.billing.paymentMethods.clearFormElement();
+ var requestObject = JSON.parse(request.httpParameterMap.requestBodyAsString);
+ var form = session.forms.billing.paymentMethods;
+ for (var requestObjectItem in requestObject) {
+ var asyncPaymentMethodResponse = requestObject[requestObjectItem];
+ var terms = requestObjectItem.split('_');
+ if (terms[0] === 'creditCard') {
+ var value = terms[1] === 'month' || terms[1] === 'year' ? Number(asyncPaymentMethodResponse) : asyncPaymentMethodResponse;
+ form.creditCard[terms[1]].setValue(value);
+ } else if (terms[0] === 'selectedPaymentMethodID') {
+ form.selectedPaymentMethodID.setValue(asyncPaymentMethodResponse);
+ }
+ }
+ if (app.getController('COBilling').HandlePaymentSelection('cart').error || handlePayments().error) {
+ app.getView().render('checkout/components/faults');
+ return;
+ }
+ app.getView().render('checkout/components/payment_methods_success');
+}
+
+/*
+ * Asynchronous Callbacks for SiteGenesis.
+ * Identifies if an order exists, submits the order, and shows a confirmation message.
+ */
+function submit() {
+ var order = Order.get(request.httpParameterMap.order_id.stringValue);
+ var orderPlacementStatus;
+ if (order.object && request.httpParameterMap.order_token.stringValue === order.getOrderToken()) {
+ orderPlacementStatus = Order.submit(order.object);
+ if (!orderPlacementStatus.error) {
+ clearForms();
+ return app.getController('COSummary').ShowConfirmation(order.object);
+ }
+ }
+ app.getController('COSummary').Start();
+}
+
+/*
+ * Module exports
+ */
+
+/*
+ * Web exposed methods
+ */
+/** @see module:controllers/COPlaceOrder~submitPaymentJSON */
+exports.SubmitPaymentJSON = guard.ensure(['https'], submitPaymentJSON);
+/** @see module:controllers/COPlaceOrder~submitPaymentJSON */
+exports.Submit = guard.ensure(['https'], submit);
+
+/*
+ * Local methods
+ */
+exports.Start = start;
\ No newline at end of file
diff --git a/cartridges/adyen_controllers_changes/app_storefront_controllers_changes/cartridge/controllers/COSummary.js b/cartridges/adyen_controllers_changes/app_storefront_controllers_changes/cartridge/controllers/COSummary.js
new file mode 100644
index 000000000..ed2d8a095
--- /dev/null
+++ b/cartridges/adyen_controllers_changes/app_storefront_controllers_changes/cartridge/controllers/COSummary.js
@@ -0,0 +1,130 @@
+'use strict';
+
+/**
+ * This controller implements the last step of the checkout. A successful handling
+ * of billing address and payment method selection leads to this controller. It
+ * provides the customer with a last overview of the basket prior to confirm the
+ * final order creation.
+ *
+ * @module controllers/COSummary
+ */
+
+/* API Includes */
+var Resource = require('dw/web/Resource');
+var Transaction = require('dw/system/Transaction');
+var URLUtils = require('dw/web/URLUtils');
+/* Script Modules */
+var app = require('~/cartridge/scripts/app');
+var guard = require('~/cartridge/scripts/guard');
+var Cart = app.getModel('Cart');
+// ### Custom Adyen cartridge start ###
+var AdyenController = require("int_adyen_controllers/cartridge/controllers/Adyen");
+// ### Custom Adyen cartridge end ###
+
+/**
+ * Renders the summary page prior to order creation.
+ * @param {Object} context context object used for the view
+ */
+function start(context) {
+ var cart = Cart.get();
+
+ // Checks whether all payment methods are still applicable. Recalculates all existing non-gift certificate payment
+ // instrument totals according to redeemed gift certificates or additional discounts granted through coupon
+ // redemptions on this page.
+ var COBilling = app.getController('COBilling');
+ if (!COBilling.ValidatePayment(cart)) {
+ COBilling.Start();
+ return;
+ } else {
+ Transaction.wrap(function () {
+ cart.calculate();
+ });
+ Transaction.wrap(function () {
+ if (!cart.calculatePaymentTransactionTotal()) {
+ COBilling.Start();
+ }
+ });
+ var pageMeta = require('~/cartridge/scripts/meta');
+ var viewContext = require('app_storefront_core/cartridge/scripts/common/extend').immutable(context, {
+ Basket: cart.object
+ });
+ pageMeta.update({
+ pageTitle: Resource.msg('summary.meta.pagetitle', 'checkout', 'SiteGenesis Checkout')
+ });
+ app.getView(viewContext).render('checkout/summary/summary');
+ }
+}
+
+/**
+ * This function is called when the "Place Order" action is triggered by the
+ * customer.
+ */
+// ### Custom Adyen cartridge start ###
+function submit() {
+ // Calls the COPlaceOrder controller that does the place order action and any payment authorization.
+ // If the order creation failed, it returns a JSON object with an error key and a boolean value.
+ var placeOrderResult = app.getController('COPlaceOrder').Start();
+ if (Object.keys(placeOrderResult).length === 0 || placeOrderResult.error) {
+ start({
+ PlaceOrderError: placeOrderResult.PlaceOrderError
+ });
+ } else {
+ if (placeOrderResult.isAdyen || placeOrderResult.continueUrl) {
+ const responseUtils = require('*/cartridge/scripts/util/Response');
+ responseUtils.renderJSON(placeOrderResult);
+ } else {
+ showConfirmation(placeOrderResult.Order);
+ }
+ }
+}
+// ### Custom Adyen cartridge end ##
+
+/**
+ * Renders the order confirmation page after successful order
+ * creation. If a nonregistered customer has checked out, the confirmation page
+ * provides a "Create Account" form. This function handles the
+ * account creation.
+ */
+// ### Custom Adyen cartridge start ###
+function showConfirmation(order) {
+ var AdyenHelper = require('*/cartridge/scripts/util/adyenHelper');
+ var adyenGivingConfig = AdyenHelper.getAdyenGivingConfig(order);
+ if (!customer.authenticated) {
+ // Initializes the account creation form for guest checkouts by populating the first and last name with the
+ // used billing address.
+ var customerForm = app.getForm('profile.customer');
+ customerForm.setValue('firstname', order.billingAddress.firstName);
+ customerForm.setValue('lastname', order.billingAddress.lastName);
+ customerForm.setValue('email', order.customerEmail);
+ customerForm.setValue('orderNo', order.orderNo);
+ }
+ app.getForm('profile.login.passwordconfirm').clear();
+ app.getForm('profile.login.password').clear();
+ var pageMeta = require('~/cartridge/scripts/meta');
+ pageMeta.update({
+ pageTitle: Resource.msg('confirmation.meta.pagetitle', 'checkout', 'SiteGenesis Checkout Confirmation')
+ });
+ app.getView({
+ Order: order,
+ AdyenGivingConfig: adyenGivingConfig,
+ ContinueURL: URLUtils.https('Account-RegistrationForm') // needed by registration form after anonymous checkouts
+ }).render('checkout/confirmation/confirmation');
+}
+// ### Custom Adyen cartridge end ###
+
+/*
+ * Module exports
+ */
+
+/*
+ * Web exposed methods
+ */
+/** @see module:controllers/COSummary~Start */
+exports.Start = guard.ensure(['https'], start);
+/** @see module:controllers/COSummary~Submit */
+exports.Submit = guard.ensure(['https', 'post', 'csrf'], submit);
+
+/*
+ * Local method
+ */
+exports.ShowConfirmation = showConfirmation;
\ No newline at end of file
diff --git a/cartridges/adyen_controllers_changes/app_storefront_controllers_changes/cartridge/controllers/PaymentInstruments.js b/cartridges/adyen_controllers_changes/app_storefront_controllers_changes/cartridge/controllers/PaymentInstruments.js
new file mode 100644
index 000000000..6d0bc7560
--- /dev/null
+++ b/cartridges/adyen_controllers_changes/app_storefront_controllers_changes/cartridge/controllers/PaymentInstruments.js
@@ -0,0 +1,307 @@
+'use strict';
+
+/**
+ * Controller that displays credit card and other payment information and
+ * lets the user change it.
+ *
+ * @module controllers/PaymentInstruments
+ */
+
+/* API includes */
+var PaymentInstrument = require('dw/order/PaymentInstrument');
+var PaymentMgr = require('dw/order/PaymentMgr');
+var PaymentStatusCodes = require('dw/order/PaymentStatusCodes');
+var Status = require('dw/system/Status');
+var Transaction = require('dw/system/Transaction');
+var URLUtils = require('dw/web/URLUtils');
+
+/* Script Modules */
+var app = require('~/cartridge/scripts/app');
+var guard = require('~/cartridge/scripts/guard');
+var constants = require("*/cartridge/adyenConstants/constants");
+// ### Custom Adyen cartridge start ###
+var AdyenHelper = require('int_adyen_overlay/cartridge/scripts/util/adyenHelper');
+var AdyenConfigs = require('int_adyen_overlay/cartridge/scripts/util/adyenConfigs');
+var adyenSessions = require('int_adyen_overlay/cartridge/scripts/adyenSessions');
+var adyenSaveCreditCard = require("*/cartridge/scripts/adyenSaveCreditCard");
+var AdyenLogs = require("int_adyen_overlay/cartridge/scripts/adyenCustomLogs");
+// ### Custom Adyen cartridge end ###
+
+/**
+ * Displays a list of customer payment instruments.
+ *
+ * Gets customer payment instrument information. Clears the paymentinstruments form and adds the customer
+ * payment information to it. Updates the page metadata.
+ * Renders a list of the saved credit card payment instruments of the current
+ * customer (account/payment/paymentinstrumentlist template).
+ */
+// ### Custom Adyen cartridge start ###
+function list() {
+ // Get the Saved Cards from Adyen to get latest saved cards
+ require('int_adyen_overlay/cartridge/scripts/updateSavedCards').updateSavedCards({
+ CurrentCustomer: customer
+ });
+ var paymentInstruments = getAdyenPaymentInstruments();
+ var pageMeta = require('~/cartridge/scripts/meta');
+ var paymentForm = app.getForm('paymentinstruments');
+ paymentForm.clear();
+ paymentForm.get('creditcards.storedcards').copyFrom(paymentInstruments);
+ pageMeta.update(dw.content.ContentMgr.getContent('myaccount-paymentsettings'));
+ app.getView({
+ PaymentInstruments: paymentInstruments
+ }).render('account/payment/paymentinstrumentlist');
+}
+// ### Custom Adyen cartridge end ##
+
+// ### Custom Adyen cartridge start ##
+function getSessionData() {
+ var sessionsResponse = adyenSessions.createSession(null, customer, '');
+ return {
+ id: sessionsResponse.id,
+ sessionData: sessionsResponse.sessionData
+ };
+}
+// ### Custom Adyen cartridge end ##
+
+/**
+ * Adds a new credit card payment instrument to the saved payment instruments of the current customer.
+ * Sets the ContinueURL to PaymentInstruments-PaymentForm and renders the payment instrument details page
+ * (account/payment/paymentinstrumentdetails template).
+ * __Note:__this function is called by the {@link module:controllers/PaymentInstruments~handlePaymentForm|handlePaymentForm} function.
+ * @param {boolean} clearForm true or missing clears the form before displaying the page, false skips it
+ */
+function add(clearForm) {
+ var paymentForm = app.getForm('paymentinstruments');
+ if (clearForm !== false) {
+ paymentForm.clear();
+ }
+ paymentForm.get('creditcards.newcreditcard.type').setOptions(dw.order.PaymentMgr.getPaymentMethod(dw.order.PaymentInstrument.METHOD_CREDIT_CARD).activePaymentCards.iterator());
+ app.getView({
+ ContinueURL: URLUtils.https('PaymentInstruments-PaymentForm'),
+ SessionData: JSON.stringify(getSessionData())
+ }).render('account/payment/paymentinstrumentdetails');
+}
+
+/**
+ * Form handler for the paymentinstruments form. Handles the following actions:
+ * - __create__ - calls the {@link module:controllers/PaymentInstruments~create|create} function to create a payment instrument
+ * and redirects to {@link module:controllers/PaymentInstruments~list|list}. If the
+ * creation fails, calls the {@link module:controllers/PaymentInstruments~add|add} function with a clearform value of false.
+ * - __error__ - calls the {@link module:controllers/PaymentInstruments~add|add} function with a clearform value of false.
+ */
+function handlePaymentForm() {
+ var paymentForm = app.getForm('paymentinstruments');
+ paymentForm.handleAction({
+ create: function () {
+ if (!create()) {
+ response.redirect(URLUtils.https('PaymentInstruments-List', 'error', 'AuthorisationFailed'));
+ return;
+ } else {
+ response.redirect(URLUtils.https('PaymentInstruments-List'));
+ }
+ },
+ error: function () {
+ add(false);
+ }
+ });
+}
+/**
+ * Saves a customer credit card payment instrument.
+ * @param {Object} params
+ * @param {dw.customer.CustomerPaymentInstrument} params.PaymentInstrument - credit card object.
+ * @param {dw.web.FormGroup} params.CreditCardFormFields - new credit card form.
+ */
+function save(params) {
+ var saveCustomerCreditCard = require('app_storefront_core/cartridge/scripts/checkout/SaveCustomerCreditCard');
+ var result = saveCustomerCreditCard.save(params);
+ if (result === PIPELET_ERROR) {
+ throw new Error('Problem saving credit card');
+ }
+}
+
+/**
+ * Creates a new payment instrument. Verifies the credit card and checks if it is a duplicate of
+ * a card already in the current customer's payment instruments. In a transaction, the function
+ * attempts to save the credit card to the customer's payment instruments. If a duplicate card was
+ * detected, the original card is removed after the new card is created. If the card cannot be created
+ * successfully, the transaction is rolled back. Whether successful or not, the paymentinstruments
+ * form is cleared.
+ *
+ * @transaction
+ * @return {boolean} true if the credit card can be verified, false otherwise
+ */
+// ### Custom Adyen cartridge start ###
+function create() {
+ if (getAdyenPaymentInstruments()) {
+ return adyenSaveCreditCard.create();
+ }
+ var paymentForm = app.getForm('paymentinstruments');
+ var newCreditCardForm = paymentForm.get('creditcards.newcreditcard');
+ var ccNumber = newCreditCardForm.get('number').value();
+ var wallet = customer.getProfile().getWallet();
+ var paymentInstruments = wallet.getPaymentInstruments(dw.order.PaymentInstrument.METHOD_CREDIT_CARD);
+ if (AdyenConfigs.getAdyenRecurringPaymentsEnabled()) {
+ var createRecurringPaymentAccountResult = AdyenHelper.createRecurringPaymentAccount({
+ Customer: customer
+ });
+ if (createRecurringPaymentAccountResult.error) {
+ return false;
+ }
+ pspReference = 'PspReference' in createRecurringPaymentAccountResult && !empty(createRecurringPaymentAccountResult.PspReference) ? createRecurringPaymentAccountResult.PspReference : '';
+ tokenID = 'TokenID' in createRecurringPaymentAccountResult && !empty(createRecurringPaymentAccountResult.TokenID) ? createRecurringPaymentAccountResult.TokenID : '';
+ try {
+ Transaction.wrap(function () {
+ /* var newCreditCard = customer.getProfile().getWallet().createPaymentInstrument(PaymentInstrument.METHOD_CREDIT_CARD);
+ * // copy the credit card details to the payment instrument
+ * newCreditCard.setCreditCardHolder(
+ newCreditCard.setCreditCardNumber(
+ newCreditCard.setCreditCardType(
+ newCreditCard.setCreditCardToken(tokenID);
+ newCreditCard.custom.AdyenPspReference = pspReference; */
+ require('int_adyen_overlay/cartridge/scripts/updateSavedCards').updateSavedCards({
+ CurrentCustomer: customer,
+ PaymentsMap: createRecurringPaymentAccountResult.PaymentsMap
+ });
+ });
+ } catch (e) {
+ AdyenLogs.error_log(`${e}: ${e.stack}`);
+ return false;
+ }
+ return true;
+ }
+ var isDuplicateCard = false;
+ var oldCard;
+ for (var i = 0; i < paymentInstruments.length; i++) {
+ var card = paymentInstruments[i];
+ if (card.creditCardNumber === ccNumber) {
+ isDuplicateCard = true;
+ oldCard = card;
+ break;
+ }
+ }
+ Transaction.begin();
+ var paymentInstrument = wallet.createPaymentInstrument(dw.order.PaymentInstrument.METHOD_CREDIT_CARD);
+ try {
+ save({
+ PaymentInstrument: paymentInstrument,
+ CreditCardFormFields: newCreditCardForm.object
+ });
+ } catch (err) {
+ Transaction.rollback();
+ return false;
+ }
+ if (isDuplicateCard) {
+ wallet.removePaymentInstrument(oldCard);
+ }
+ Transaction.commit();
+ paymentForm.clear();
+ return true;
+}
+// ### Custom Adyen cartridge end ###
+
+/**
+ * Form handler for the paymentinstruments form. Handles the following actions:
+ * - __remove__ - uses the form and action supplied by the FormModel to remove a customer payment instrument
+ * in a transaction.
+ * - __error__ - does nothing.
+ *
+ * In either case, redirects to the {@link module:controllers/PaymentInstruments~list|List} function.
+ * @transaction
+ * @TODO Should be moved into handlePaymentForm
+ * @FIXME Inner method should be lowercase.error action should do something
+ */
+// ### Custom Adyen cartridge start ###
+function Delete() {
+ var paymentForm = app.getForm('paymentinstruments');
+ paymentForm.handleAction({
+ remove: function (formGroup, action) {
+ Transaction.wrap(function () {
+ var wallet = customer.getProfile().getWallet();
+ var paymentInstrument = action.object;
+ if (!empty(paymentInstrument)) {
+ if (AdyenConfigs.getAdyenRecurringPaymentsEnabled() && !empty(paymentInstrument.getCreditCardToken())) {
+ var result = require('int_adyen_overlay/cartridge/scripts/adyenDeleteRecurringPayment').deleteRecurringPayment({
+ Customer: customer,
+ RecurringDetailReference: paymentInstrument.getCreditCardToken()
+ });
+ if (result == PIPELET_NEXT) {
+ wallet.removePaymentInstrument(paymentInstrument);
+ }
+ } else {
+ wallet.removePaymentInstrument(paymentInstrument);
+ }
+ }
+ });
+ },
+ error: function () {
+ // @TODO When could this happen
+ }
+ });
+ response.redirect(URLUtils.https('PaymentInstruments-List'));
+}
+// ### Custom Adyen cartridge end ###
+
+/*
+ * Private helpers
+ */
+// ### Custom Adyen cartridge start ###
+function getAdyenPaymentInstruments() {
+ var wallet = customer.getProfile().getWallet();
+ return wallet.getPaymentInstruments(constants.METHOD_ADYEN_COMPONENT);
+}
+// ### Custom Adyen cartridge start ###
+
+/**
+ * Verifies if the entered credit card details are valid.
+ *
+ * @returns {boolean} true in case of success, otherwise false.
+ */
+// ### Custom Adyen cartridge start ###
+function verifyCreditCard() {
+ var newCreditCardForm = app.getForm('paymentinstruments.creditcards.newcreditcard');
+ if (getAdyenPaymentInstruments()) {
+ return true;
+ }
+ var expirationMonth = newCreditCardForm.get('expiration.month').value();
+ var expirationYear = newCreditCardForm.get('expiration.year').value();
+ var cardNumber = newCreditCardForm.get('number').value();
+ var paymentCard = PaymentMgr.getPaymentCard(newCreditCardForm.get('type').value());
+ var verifyPaymentCardResult = paymentCard.verify(expirationMonth, expirationYear, cardNumber);
+ if (verifyPaymentCardResult.error === true) {
+ if (!newCreditCardForm.isValid()) {
+ return false;
+ }
+ if (verifyPaymentCardResult.code === Status.OK) {
+ return true;
+ }
+
+ // Invalidate the payment card form elements.
+ for (var i = 0; i < verifyPaymentCardResult.items.length; i++) {
+ if (verifyPaymentCardResult.items[i].code === PaymentStatusCodes.CREDITCARD_INVALID_CARD_NUMBER) {
+ newCreditCardForm.get('number').invalidate();
+ } else if (verifyPaymentCardResult.items[i].code === PaymentStatusCodes.CREDITCARD_INVALID_EXPIRATION_DATE) {
+ newCreditCardForm.get('expiration.month').invalidate();
+ newCreditCardForm.get('expiration.year').invalidate();
+ }
+ }
+ return false;
+ }
+ return true;
+}
+// ### Custom Adyen cartridge end ###
+
+/*
+ * Web exposed methods
+ */
+/** Renders a list of the saved credit card payment instruments of the current customer.
+ * @see module:controllers/PaymentInstruments~list */
+exports.List = guard.ensure(['https', 'get', 'loggedIn'], list);
+/** Adds a new credit card payment instrument to the saved payment instruments of the current customer.
+ * @see module:controllers/PaymentInstruments~add */
+exports.Add = guard.ensure(['https', 'get', 'loggedIn'], add);
+/** Handles the submitted form for creating payment instruments.
+ * @see module:controllers/PaymentInstruments~handlePaymentForm */
+exports.PaymentForm = guard.ensure(['https', 'post', 'loggedIn', 'csrf'], handlePaymentForm);
+/** Deletes a saved credit card payment instrument.
+ * @see module:controllers/PaymentInstruments~Delete */
+exports.Delete = guard.ensure(['https', 'loggedIn'], Delete);
\ No newline at end of file
diff --git a/cartridges/adyen_controllers_changes/app_storefront_controllers_changes/package.json b/cartridges/adyen_controllers_changes/app_storefront_controllers_changes/package.json
new file mode 100644
index 000000000..a9de38453
--- /dev/null
+++ b/cartridges/adyen_controllers_changes/app_storefront_controllers_changes/package.json
@@ -0,0 +1,3 @@
+{
+ "hooks": "./cartridge/scripts/hooks.json"
+}
diff --git a/cartridges/adyen_controllers_changes/app_storefront_core_changes/cartridge/forms/default/billing.xml b/cartridges/adyen_controllers_changes/app_storefront_core_changes/cartridge/forms/default/billing.xml
new file mode 100644
index 000000000..88f5bfe1d
--- /dev/null
+++ b/cartridges/adyen_controllers_changes/app_storefront_core_changes/cartridge/forms/default/billing.xml
@@ -0,0 +1,77 @@
+
+
\ No newline at end of file
diff --git a/cartridges/adyen_controllers_changes/app_storefront_core_changes/cartridge/forms/default/creditcard.xml b/cartridges/adyen_controllers_changes/app_storefront_core_changes/cartridge/forms/default/creditcard.xml
new file mode 100644
index 000000000..69198b20a
--- /dev/null
+++ b/cartridges/adyen_controllers_changes/app_storefront_core_changes/cartridge/forms/default/creditcard.xml
@@ -0,0 +1,74 @@
+
+
diff --git a/cartridges/adyen_controllers_changes/app_storefront_core_changes/cartridge/scripts/util/Resource.ds b/cartridges/adyen_controllers_changes/app_storefront_core_changes/cartridge/scripts/util/Resource.ds
new file mode 100644
index 000000000..b360d2ce6
--- /dev/null
+++ b/cartridges/adyen_controllers_changes/app_storefront_core_changes/cartridge/scripts/util/Resource.ds
@@ -0,0 +1,253 @@
+/**
+ * Resource helper
+ *
+ */
+var Currency = require('dw/util/Currency');
+var Site = require('dw/system/Site');
+var ContentMgr = require('dw/content/ContentMgr');
+var ProductAvailabilityModel = require('dw/catalog/ProductAvailabilityModel');
+
+/* Script Modules */
+var AdyenHelper = require ("int_adyen_overlay/cartridge/scripts/util/adyenHelper");
+
+function ResourceHelper() {}
+/**
+ * Get the client-side constants
+ * @param pageContext
+ * @returns {Object} An objects key key-value pairs holding the constants
+ */
+ResourceHelper.getConstants = function(pageContext) {
+ return {
+ AVAIL_STATUS_IN_STOCK : ProductAvailabilityModel.AVAILABILITY_STATUS_IN_STOCK,
+ AVAIL_STATUS_PREORDER : ProductAvailabilityModel.AVAILABILITY_STATUS_PREORDER,
+ AVAIL_STATUS_BACKORDER : ProductAvailabilityModel.AVAILABILITY_STATUS_BACKORDER,
+ AVAIL_STATUS_NOT_AVAILABLE : ProductAvailabilityModel.AVAILABILITY_STATUS_NOT_AVAILABLE
+ };
+}
+/**
+ * Get the client-side resources of a given page
+ * @param pageContext
+ * @returns {Object} An objects key key-value pairs holding the resources
+ */
+ResourceHelper.getResources = function(pageContext) {
+ var Resource = require('dw/web/Resource');
+
+ // application resources
+ var resources = {
+ // Common
+ I_AGREE : Resource.msg('global.i_agree', 'locale', null),
+ TRACKING_CONSENT : Resource.msg('global.tracking_consent', 'locale', null),
+ TRACKING_NO_CONSENT : Resource.msg('global.tracking_no_consent', 'locale', null),
+ CLOSE : Resource.msg('global.close', 'locale', null),
+ NO_THANKS : Resource.msg('global.nothanks', 'locale', null),
+ OK : Resource.msg('global.ok', 'locale', null),
+ ARE_YOU_HUMAN : Resource.msg('global.captcha.areyouhuman', 'locale', null),
+
+ // Checkout
+ SHIP_QualifiesFor : Resource.msg('shipment.qualifiesfor', 'checkout', null),
+ CC_LOAD_ERROR : Resource.msg('billing.creditcardloaderror', 'checkout', null),
+ COULD_NOT_SAVE_ADDRESS : Resource.msg('multishippingaddresses.couldnotsaveaddress', 'checkout', null),
+
+ // Registry resources
+ REG_ADDR_ERROR : Resource.msg('global.couldntloadaddress', 'locale', null),
+
+ // bonus products messages
+ BONUS_PRODUCT : Resource.msg('product.bonusproduct', 'product', null),
+ BONUS_PRODUCTS : Resource.msg('product.bonusproducts', 'product', null),
+ SELECT_BONUS_PRODUCTS : Resource.msg('product.selectbonusproducts', 'product', null),
+ SELECT_BONUS_PRODUCT : Resource.msg('product.selectbonusproduct', 'product', null),
+ BONUS_PRODUCT_MAX : Resource.msg('product.bonusproductsmax', 'product', null),
+ BONUS_PRODUCT_TOOMANY : Resource.msg('product.bonusproductstoomany', 'product', null),
+ SIMPLE_SEARCH : Resource.msg('simplesearch.searchtext', 'search', null),
+ SUBSCRIBE_EMAIL_DEFAULT : Resource.msg('subscribe.email.default', 'forms', 'Email Address'),
+
+ CURRENCY_SYMBOL : Currency.getCurrency(Site.current.getDefaultCurrency()).symbol,
+ MISSINGVAL : Resource.msg('global.missingval', 'locale', null),
+ SERVER_ERROR : Resource.msg('global.servererror', 'locale', null),
+ MISSING_LIB : Resource.msg('global.missinglib', 'locale', null),
+ BAD_RESPONSE : Resource.msg('global.badresponse', 'locale', null),
+ INVALID_PHONE : Resource.msg('global.invalidphone', 'locale', null),
+ REMOVE : Resource.msg('global.remove', 'locale', null),
+ QTY : Resource.msg('global.qty', 'locale', null),
+ EMPTY_IMG_ALT : Resource.msg('global.remove', 'locale', null),
+ COMPARE_BUTTON_LABEL : Resource.msg('productcomparewidget.compareitemsbutton', 'search', null),
+ COMPARE_CONFIRMATION : Resource.msg('productcomparewidget.maxproducts', 'search', null),
+ COMPARE_REMOVE_FAIL : Resource.msg('productcomparewidget.removefail', 'search', null),
+ COMPARE_ADD_FAIL : Resource.msg('productcomparewidget.addfail', 'search', null),
+ ADD_TO_CART_FAIL : Resource.msg('cart.unableToAdd', 'checkout', null),
+ REGISTRY_SEARCH_ADVANCED_CLOSE : Resource.msg('account.giftregistry.closeadvanced', 'account', null),
+ GIFT_CERT_INVALID : Resource.msg('billing.giftcertinvalid', 'checkout', null),
+ GIFT_CERT_BALANCE : Resource.msg('billing.giftcertbalance', 'checkout', null),
+ GIFT_CERT_AMOUNT_INVALID : Resource.msg('giftcert.amountvalueerror', 'forms', null),
+ GIFT_CERT_MISSING : Resource.msg('billing.giftcertidmissing', 'checkout', null),
+ INVALID_OWNER : Resource.msg('billing.ownerparseerror', 'checkout', null),
+ COUPON_CODE_MISSING : Resource.msg('cart.COUPON_CODE_MISSING', 'checkout', null),
+ COOKIES_DISABLED : Resource.msg('global.browsertoolscheck.cookies', 'locale', null),
+ BML_AGREE_TO_TERMS : Resource.msg('bml.termserror', 'forms', null),
+ CHAR_LIMIT_MSG : Resource.msg('character.limit', 'forms', null),
+ CONFIRM_DELETE : Resource.msg('confirm.delete', 'forms', null),
+ TITLE_GIFTREGISTRY : Resource.msg('title.giftregistry', 'forms', null),
+ TITLE_ADDRESS : Resource.msg('title.address', 'forms', null),
+ TITLE_CREDITCARD : Resource.msg('title.creditcard', 'forms', null),
+ SERVER_CONNECTION_ERROR : Resource.msg('global.serverconnection', 'locale', 'Server connection failed!'),
+ IN_STOCK_DATE : Resource.msg('global.inStockDate', 'locale', null),
+ ITEM_STATUS_NOTAVAILABLE : Resource.msg('global.allnotavailable', 'locale', null),
+ INIFINITESCROLL : Resource.msg('paginginformation.infinite-scroll', 'search', null),
+ STORE_NEAR_YOU : Resource.msg('storelist.lightbox.whatsavailable', 'storepickup', 'What\'s available at a store near you'),
+ SELECT_STORE : Resource.msg('storelist.lightbox.selectstore', 'storepickup', null),
+ SELECTED_STORE : Resource.msg('storelist.lightbox.selectedstore', 'storepickup', null),
+ PREFERRED_STORE : Resource.msg('storelist.lightbox.preferredstore', 'storepickup', null),
+ SET_PREFERRED_STORE : Resource.msg('storelist.lightbox.setpreferredstore', 'storepickup', null),
+ ENTER_ZIP : Resource.msg('storelist.lightbox.enterzip', 'storepickup', null),
+ INVALID_ZIP : Resource.msg('storelist.lightbox.invalidpostalcode', 'storepickup', null),
+ SEARCH : Resource.msg('global.search', 'locale', null),
+ CHANGE_LOCATION : Resource.msg('storelist.lightbox.changelocation', 'storepickup', null),
+ CONTINUE_WITH_STORE : Resource.msg('storelist.lightbox.continuewithstore', 'storepickup', null),
+ CONTINUE : Resource.msg('global.continue', 'locale', null),
+ SEE_MORE : Resource.msg('storelist.lightbox.seemore', 'storepickup', null),
+ SEE_LESS : Resource.msg('storelist.lightbox.seeless', 'storepickup', null),
+ QUICK_VIEW : Resource.msg('product.quickview', 'product', null),
+ QUICK_VIEW_POPUP : Resource.msg('product.quickview.popup', 'product', null),
+ TLS_WARNING : Resource.msg('global.browsertoolscheck.tls', 'locale', null),
+ CSRF_TOKEN_MISMATCH : Resource.msg('global.csrf.failed.error', 'locale', null),
+
+ // Validation messages
+ VALIDATE_REQUIRED : Resource.msg('validate.required', 'forms', null),
+ VALIDATE_REMOTE : Resource.msg('validate.remote', 'forms', null),
+ VALIDATE_EMAIL : Resource.msg('validate.email', 'forms', null),
+ VALIDATE_URL : Resource.msg('validate.url', 'forms', null),
+ VALIDATE_DATE : Resource.msg('validate.date', 'forms', null),
+ VALIDATE_DATEISO : Resource.msg('validate.dateISO', 'forms', null),
+ VALIDATE_NUMBER : Resource.msg('validate.number', 'forms', null),
+ VALIDATE_DIGITS : Resource.msg('validate.digits', 'forms', null),
+ VALIDATE_CREDITCARD : Resource.msg('validate.creditcard', 'forms', null),
+ VALIDATE_EQUALTO : Resource.msg('validate.equalTo', 'forms', null),
+ VALIDATE_MAXLENGTH : Resource.msg('validate.maxlength', 'forms', null),
+ VALIDATE_MINLENGTH : Resource.msg('validate.minlength', 'forms', null),
+ VALIDATE_RANGELENGTH : Resource.msg('validate.rangelength', 'forms', null),
+ VALIDATE_RANGE : Resource.msg('validate.range', 'forms', null),
+ VALIDATE_MAX : Resource.msg('validate.max', 'forms', null),
+ VALIDATE_MIN : Resource.msg('validate.min', 'forms', null),
+ ADYEN_CC_VALIDATE : Resource.msg('adyen.creditcard', 'adyen', null)
+
+ };
+
+ // additional resources
+ resources[ProductAvailabilityModel.AVAILABILITY_STATUS_IN_STOCK] = Resource.msg('global.instock', 'locale', null);
+ resources["QTY_" + ProductAvailabilityModel.AVAILABILITY_STATUS_IN_STOCK] = Resource.msg('global.quantityinstock', 'locale', null);
+ resources[ProductAvailabilityModel.AVAILABILITY_STATUS_PREORDER] = Resource.msg('global.allpreorder', 'locale', null);
+ resources["QTY_" + ProductAvailabilityModel.AVAILABILITY_STATUS_PREORDER] = Resource.msg('global.quantitypreorder', 'locale', null);
+ resources["REMAIN_" + ProductAvailabilityModel.AVAILABILITY_STATUS_PREORDER] = Resource.msg('global.remainingpreorder', 'locale', null);
+ resources[ProductAvailabilityModel.AVAILABILITY_STATUS_BACKORDER] = Resource.msg('global.allbackorder', 'locale', null);
+ resources["QTY_" + ProductAvailabilityModel.AVAILABILITY_STATUS_BACKORDER] = Resource.msg('global.quantitybackorder', 'locale', null);
+ resources["REMAIN_" + ProductAvailabilityModel.AVAILABILITY_STATUS_BACKORDER] = Resource.msg('global.remainingbackorder', 'locale', null);
+ resources[ProductAvailabilityModel.AVAILABILITY_STATUS_NOT_AVAILABLE] = Resource.msg('global.allnotavailable', 'locale', null);
+ resources["REMAIN_" + ProductAvailabilityModel.AVAILABILITY_STATUS_NOT_AVAILABLE] = Resource.msg('global.remainingnotavailable', 'locale', null);
+
+ return resources;
+}
+
+/**
+ * Get the client-side URLs of a given page
+ * @returns {Object} An objects key key-value pairs holding the URLs
+ */
+ResourceHelper.getUrls = function(pageContext) {
+ var URLUtils = require('dw/web/URLUtils');
+ var Resource = require('dw/web/Resource');
+
+ // application urls
+ var urls = {
+ appResources : URLUtils.url('Resources-Load').toString(),
+ pageInclude : URLUtils.url('Page-Include').toString(),
+ continueUrl : request.isHttpSecure() ? URLUtils.httpsContinue().toString() : URLUtils.httpContinue().toString(),
+ staticPath : URLUtils.staticURL("/").toString(),
+ addGiftCert : URLUtils.url('GiftCert-Purchase').toString(),
+ minicartGC : URLUtils.url('GiftCert-ShowMiniCart').toString(),
+ addProduct : URLUtils.url('Cart-AddProduct').toString(),
+ minicart : URLUtils.url('Cart-MiniAddProduct').toString(),
+ cartShow : URLUtils.url('Cart-Show').toString(),
+ giftRegAdd : URLUtils.https('Address-GetAddressDetails', 'addressID', '').toString(),
+ paymentsList : URLUtils.https('PaymentInstruments-List').toString(),
+ addressesList : URLUtils.https('Address-List').toString(),
+ wishlistAddress : URLUtils.https('Wishlist-SetShippingAddress').toString(),
+ deleteAddress : URLUtils.url('Address-Delete').toString(),
+ getProductUrl : URLUtils.url('Product-Show').toString(),
+ getBonusProducts : URLUtils.url('Product-GetBonusProducts').toString(),
+ addBonusProduct : URLUtils.url('Cart-AddBonusProduct').toString(),
+ getSetItem : URLUtils.url('Product-GetSetItem').toString(),
+ productDetail : URLUtils.url('Product-Detail').toString(),
+ getAvailability : URLUtils.url('Product-GetAvailability').toString(),
+ removeImg : URLUtils.staticURL('/images/icon_remove.gif').toString(),
+ searchsuggest : URLUtils.url('Search-GetSuggestions').toString(),
+ productNav : URLUtils.url('Product-Productnav').toString(),
+ summaryRefreshURL : URLUtils.url('COBilling-UpdateSummary').toString(),
+ billingSelectCC : URLUtils.https('COBilling-SelectCreditCard').toString(),
+ updateAddressDetails : URLUtils.https('COShipping-UpdateAddressDetails').toString(),
+ updateAddressDetailsBilling : URLUtils.https('COBilling-UpdateAddressDetails').toString(),
+ shippingMethodsJSON : URLUtils.https('COShipping-GetApplicableShippingMethodsJSON').toString(),
+ shippingMethodsList : URLUtils.https('COShipping-UpdateShippingMethodList').toString(),
+ selectShippingMethodsList : URLUtils.https('COShipping-SelectShippingMethod').toString(),
+ resetPaymentForms : URLUtils.url('COBilling-ResetPaymentForms').toString(),
+ compareShow : URLUtils.url('Compare-Show').toString(),
+ compareAdd : URLUtils.url('Compare-AddProduct').toString(),
+ compareRemove : URLUtils.url('Compare-RemoveProduct').toString(),
+ compareEmptyImage : URLUtils.staticURL('/images/comparewidgetempty.png').toString(),
+ giftCardCheckBalance : URLUtils.https('COBilling-GetGiftCertificateBalance').toString(),
+ redeemGiftCert : URLUtils.https('COBilling-RedeemGiftCertificateJson').toString(),
+ addCoupon : URLUtils.https('Cart-AddCouponJson').toString(),
+ storesInventory : URLUtils.url('StoreInventory-Inventory').toString(),
+ setPreferredStore : URLUtils.url('StoreInventory-SetPreferredStore').toString(),
+ getPreferredStore : URLUtils.url('StoreInventory-GetPreferredStore').toString(),
+ setStorePickup : URLUtils.url('StoreInventory-SetStore').toString(),
+ setZipCode : URLUtils.url('StoreInventory-SetZipCode').toString(),
+ getZipCode : URLUtils.url('StoreInventory-GetZipCode').toString(),
+ billing : URLUtils.url('COBilling-Start').toString(),
+ setSessionCurrency : URLUtils.url('Currency-SetSessionCurrency').toString(),
+ addEditAddress : URLUtils.url('COShippingMultiple-AddEditAddressJSON').toString(),
+ cookieHint : URLUtils.url('Page-Show', 'cid', 'cookie_hint').toString(),
+ consentTracking : URLUtils.url('Page-Show', 'cid', 'consent_tracking_hint').toString(),
+ consentTrackingSetSession : URLUtils.url('Account-ConsentTracking').toString(),
+ rateLimiterReset : URLUtils.url('RateLimiter-HideCaptcha').toString(),
+ csrffailed : URLUtils.url('CSRF-Failed').toString()
+ };
+ return urls;
+}
+/**
+ * Get the client-side preferences of a given page
+ * @returns {Object} An objects key key-value pairs holding the preferences
+ */
+ResourceHelper.getPreferences = function(pageContext) {
+ var cookieHintAsset = ContentMgr.getContent('cookie_hint');
+ var consentTrackingHintAsset = ContentMgr.getContent('consent_tracking_hint');
+ return {
+ LISTING_INFINITE_SCROLL: (Site.getCurrent().getCustomPreferenceValue('enableInfiniteScroll') ? true : false),
+ LISTING_REFINE_SORT: true,
+ STORE_PICKUP: Site.getCurrent().getCustomPreferenceValue('enableStorePickUp'),
+ COOKIE_HINT: (cookieHintAsset && cookieHintAsset.online) || false,
+ CONSENT_TRACKING_HINT: (consentTrackingHintAsset && consentTrackingHintAsset.online) || false,
+ CHECK_TLS: Site.getCurrent().getCustomPreferenceValue('checkTLS'),
+ ADYEN_SF_ENABLED : dw.order.PaymentMgr.getPaymentMethod('AdyenComponent').isActive()
+ || (dw.order.PaymentMgr.getPaymentMethod('CREDIT_CARD') && ['Adyen_Component', 'ADYEN_CREDIT'].indexOf(dw.order.PaymentMgr.getPaymentMethod('CREDIT_CARD').getPaymentProcessor().getID()) > -1)
+ };
+}
+/**
+ * Get the client-side preferences of a given page
+ * @returns {Object} An objects key key-value pairs holding the preferences
+ */
+ResourceHelper.getSessionAttributes = function(pageContext) {
+ return {
+ SHOW_CAPTCHA: session.privacy.showCaptcha
+ };
+}
+/**
+ * Get the client-side user settings
+ * @returns {Object} An objects key key-value pairs holding the settings
+ */
+ResourceHelper.getUserSettings = function(pageContext) {
+ var ProductAvailabilityModel = require('dw/catalog/ProductAvailabilityModel');
+ var AdyenHelper = require ("int_adyen_overlay/cartridge/scripts/util/adyenHelper");
+ return {
+ zip: session.custom.zipcode == "null" ? null : session.custom.zipcode,
+ storeId: session.custom.storeId == "null" ? null : session.custom.storeId
+ };
+}
diff --git a/cartridges/adyen_controllers_changes/app_storefront_core_changes/cartridge/static/default/js/app.js b/cartridges/adyen_controllers_changes/app_storefront_core_changes/cartridge/static/default/js/app.js
new file mode 100644
index 000000000..f4f220cf1
--- /dev/null
+++ b/cartridges/adyen_controllers_changes/app_storefront_core_changes/cartridge/static/default/js/app.js
@@ -0,0 +1,11072 @@
+(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a;}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r);},p,p.exports,r,e,n,t);}return n[i].exports;}for(var u="function"==typeof require&&require,i=0;i{if(paypalTerminatedEarly){paymentFromComponent({cancelTransaction:true,merchantReference:document.querySelector('#merchantReference').value});paypalTerminatedEarly=false;return actions.resolve();}paypalTerminatedEarly=true;$('#dwfrm_billing').trigger('submit');if(formErrorsExist){paypalTerminatedEarly=false;return actions.reject();}},onSubmit:(state,component)=>{assignPaymentMethodValue();paymentFromComponent(state.data,component);document.querySelector('#adyenStateData').value=JSON.stringify(state.data);},onCancel:(data,component)=>{paypalTerminatedEarly=false;paymentFromComponent({cancelTransaction:true,merchantReference:document.querySelector('#merchantReference').value},component);},onError:(/* error, component */)=>{paypalTerminatedEarly=false;$('#dwfrm_billing').trigger('submit');},onAdditionalDetails:(state/* , component */)=>{paypalTerminatedEarly=false;document.querySelector('#paymentFromComponentStateData').value=JSON.stringify(state.data);$('#dwfrm_billing').trigger('submit');}},mbway:{showPayButton:true,onSubmit:(state,component)=>{$('#dwfrm_billing').trigger('submit');assignPaymentMethodValue();if(formErrorsExist){return false;}document.getElementById('component_mbway').querySelector('button').disabled=true;paymentFromComponent(state.data,component);document.querySelector('#adyenStateData').value=JSON.stringify(state.data);},onError:(/* error, component */)=>{$('#dwfrm_billing').trigger('submit');},onAdditionalDetails:(state/* , component */)=>{document.querySelector('#paymentFromComponentStateData').value=JSON.stringify(state.data);$('#dwfrm_billing').trigger('submit');}},swish:getQRCodeConfig(),bcmc_mobile:getQRCodeConfig(),wechatpayQR:getQRCodeConfig(),pix:getQRCodeConfig(),amazonpay:getAmazonpayConfig()};if(window.googleMerchantID!=='null'&&window.Configuration.environment==='live'){checkoutConfiguration.paymentMethodsConfiguration.paywithgoogle.configuration.merchantIdentifier=window.googleMerchantID;checkoutConfiguration.paymentMethodsConfiguration.googlepay.configuration.merchantIdentifier=window.googleMerchantID;}if(window.cardholderNameBool!=='null'){checkoutConfiguration.paymentMethodsConfiguration.card.hasHolderName=true;checkoutConfiguration.paymentMethodsConfiguration.card.holderNameRequired=true;}checkoutConfiguration.session={id:window.sessionsResponse.id,sessionData:window.sessionsResponse.sessionData};checkout=await AdyenCheckout(checkoutConfiguration);paymentMethodsResponse=checkout.paymentMethodsResponse;document.querySelector('#paymentMethodsList').innerHTML='';renderGenericComponent();}}function zeroAuth(data,checkout){$.ajax({url:window.zeroAuthURL,type:'POST',contentType:'application/; charset=utf-8',data:JSON.stringify(data),async:false,success:function(data){if(data.zeroAuthResult.action){document.querySelector('#buttonsContainer').style.display='none';checkout.createFromAction(data.zeroAuthResult.action).mount('#newCard');}if(data.zeroAuthResult.resultCode==='Authorised'){window.location.href=window.paymentInstrumentsList;}else if(data.zeroAuthResult.resultCode==='Refused'){window.location.href=window.paymentInstrumentsListError;}}});}function paymentsDetails(state){$.ajax({type:'post',url:window.paymentsDetails,data:JSON.stringify({data:state.data}),contentType:'application/; charset=utf-8',async:false,success(data){if(data.response.isSuccessful){window.location.href=window.paymentInstrumentsList;}else if(!data.response.isFinal&&typeof data.response.action==='object'){checkout.createFromAction(data.action).mount('#action-container');}else{window.location.href=window.paymentInstrumentsListError;}}});}/**
+ * @function
+ * @description Initializes Adyen Checkout My Account events
+ */async function initializeAccountEvents(){checkoutConfiguration=window.Configuration;checkoutConfiguration.onAdditionalDetails=function(state){paymentsDetails(state);};checkoutConfiguration.session=window.sessionData;checkout=await AdyenCheckout(checkoutConfiguration);var newCard=document.getElementById('newCard');var adyenStateData;var isValid=false;var node=checkout.create('card',{hasHolderName:true,holderNameRequired:true,onChange:function(state){adyenStateData=state.data;isValid=state.isValid;}}).mount(newCard);$('#applyBtn').on('click',function(e){e.preventDefault();if(!isValid){node.showValidation();return false;}document.querySelector('#adyenStateData').value=JSON.stringify(adyenStateData);zeroAuth(adyenStateData,checkout);});}function assignPaymentMethodValue(){var adyenPaymentMethod=document.querySelector('#adyenPaymentMethodName');adyenPaymentMethod.value=document.querySelector(`#lb_${selectedMethod}`).innerHTML;}/**
+ * To avoid re-rendering components twice, unmounts existing components from payment methods list
+ */function unmountComponents(){var promises=Object.entries(componentsObj).map(function([key,val]){delete componentsObj[key];return resolveUnmount(key,val);});return Promise.all(promises);}function resolveUnmount(key,val){try{return Promise.resolve(val.node.unmount(`component_${key}`));}catch(e){// try/catch block for val.unmount
+return Promise.resolve(false);}}function displaySelectedMethod(type){selectedMethod=type;resetPaymentMethod();if(['paypal','paywithgoogle','googlepay','mbway','amazonpay',...qrCodeMethods].indexOf(type)>-1){document.querySelector('#billing-submit').disabled=true;}else{document.querySelector('#billing-submit').disabled=false;}document.querySelector(`#component_${type}`).setAttribute('style','display:block');}function resetPaymentMethod(){$('.additionalFields').hide();}function showValidation(){if(componentsObj[selectedMethod]&&!componentsObj[selectedMethod].isValid){componentsObj[selectedMethod].node.showValidation();return false;}return true;}/**
+ * Assigns stateData value to the hidden stateData input field
+ * so it's sent to the backend for processing
+ */function validateComponents(){var stateData;if(componentsObj[selectedMethod]&&componentsObj[selectedMethod].stateData){stateData=componentsObj[selectedMethod].stateData;}else{var type=document.querySelector(`#component_${selectedMethod} .type`)?document.querySelector(`#component_${selectedMethod} .type`).value:selectedMethod;stateData={paymentMethod:{type:type}};var brandElm=document.querySelector(`#component_${selectedMethod} .brand`);if(brandElm&&brandElm.value){stateData.paymentMethod.brand=brandElm.value;}}document.querySelector('#adyenStateData').value=JSON.stringify(stateData);}/**
+ * Contains fallback components for payment methods that don't have an Adyen web component yet
+ */function getFallback(paymentMethod){var fallback={giftcard:`
+
+ `};return fallback[paymentMethod.type];}/**
+ * Renders all payment methods (including card component) retrieved from Adyen session
+ */async function renderGenericComponent(){if(Object.keys(componentsObj).length){await unmountComponents();}checkoutConfiguration.paymentMethodsResponse=paymentMethodsResponse.paymentMethods;if(sessionsResponse.amount){checkoutConfiguration.amount=sessionsResponse.amount;checkoutConfiguration.paymentMethodsConfiguration.paypal.amount=sessionsResponse.amount;checkoutConfiguration.paymentMethodsConfiguration.amazonpay.amount=sessionsResponse.amount;setInstallments(sessionsResponse.amount);}if(sessionsResponse.countryCode){checkoutConfiguration.countryCode=sessionsResponse.countryCode;}var amazonpay=paymentMethodsResponse.paymentMethods.find(paymentMethod=>paymentMethod.type==='amazonpay');if(amazonpay){checkoutConfiguration.paymentMethodsConfiguration.amazonpay.configuration=amazonpay.configuration;}if(paymentMethodsResponse.storedPaymentMethods){for(var i=0;i{renderPaymentMethod(pm,false,sessionsResponse.imagePath);});var firstPaymentMethod=document.querySelector('input[type=radio][name=brandCode]');firstPaymentMethod.checked=true;displaySelectedMethod(firstPaymentMethod.value);}function getPaymentMethodID(isStored,paymentMethod){if(isStored){return`storedCard${paymentMethod.id}`;}if(paymentMethod.brand){// gift cards all share the same type. Brand is used to differentiate between them
+return`${paymentMethod.type}_${paymentMethod.brand}`;}return paymentMethod.type;}function renderPaymentMethod(paymentMethod,storedPaymentMethodBool,path){var paymentMethodsUI=document.querySelector('#paymentMethodsList');var li=document.createElement('li');var paymentMethodID=getPaymentMethodID(storedPaymentMethodBool,paymentMethod);var isSchemeNotStored=paymentMethod.type==='scheme'&&!storedPaymentMethodBool;var paymentMethodImage=storedPaymentMethodBool?`${path}${paymentMethod.brand}.png`:`${path}${paymentMethod.type}.png`;var cardImage=`${path}card.png`;var imagePath=isSchemeNotStored?cardImage:paymentMethodImage;var label=storedPaymentMethodBool?`${paymentMethod.name} ${MASKED_CC_PREFIX}${paymentMethod.lastFour}`:`${paymentMethod.name}`;var liContents=`
+
+
+
+ `;var container=document.createElement('div');li.innerHTML=liContents;li.classList.add('paymentMethod');var node=renderCheckoutComponent(storedPaymentMethodBool,checkout,paymentMethod,container,paymentMethodID);container.classList.add('additionalFields');container.setAttribute('id',`component_${paymentMethodID}`);container.setAttribute('style','display:none');li.append(container);paymentMethodsUI.append(li);if(paymentMethod.type!=='paywithgoogle'){node&&node.mount(container);}else{node.isAvailable().then(()=>{node.mount(container);}).catch(()=>{});// eslint-disable-line no-empty
+}var input=document.querySelector(`#rb_${paymentMethodID}`);input.onchange=async function(event){if(document.querySelector('.adyen-checkout__qr-loader')&&qrCodeMethods.indexOf(selectedMethod)>-1||paypalTerminatedEarly){paypalTerminatedEarly=false;paymentFromComponent({cancelTransaction:true,merchantReference:document.querySelector('#merchantReference').value});}displaySelectedMethod(event.target.value);};if(paymentMethodID==='giropay'){container.innerHTML='';}if(componentsObj[paymentMethodID]&&!container.childNodes[0]&&['bcmc','scheme'].indexOf(paymentMethodID)===-1){componentsObj[paymentMethodID].isValid=true;}}function renderCheckoutComponent(storedPaymentMethodBool,checkout,paymentMethod,container,paymentMethodID){if(storedPaymentMethodBool){return createCheckoutComponent(checkout,paymentMethod,container,paymentMethodID);}var fallback=getFallback(paymentMethod);if(fallback){var template=document.createElement('template');template.innerHTML=fallback;container.append(template.content);return;}return createCheckoutComponent(checkout,paymentMethod,container,paymentMethodID);}function getPersonalDetails(){const shippingAddress=sessionsResponse.shippingAddress;return{firstName:shippingAddress.firstName,lastName:shippingAddress.lastName,telephoneNumber:shippingAddress.phone};}function createCheckoutComponent(checkout,paymentMethod,container,paymentMethodID){try{var nodeData=Object.assign(paymentMethod,{data:Object.assign(getPersonalDetails(),{personalDetails:getPersonalDetails()}),visibility:{personalDetails:'editable',billingAddress:'hidden',deliveryAddress:'hidden'}});var node=checkout.create(paymentMethod.type,nodeData);if(!componentsObj[paymentMethodID]){componentsObj[paymentMethodID]={};}componentsObj[paymentMethodID].node=node;return node;}catch(e){}// eslint-disable-line no-empty
+return false;}/**
+ * Makes an ajax call to the controller function PaymentFromComponent.
+ * Used by certain payment methods like paypal
+ */function paymentFromComponent(data,component){$.ajax({url:window.paymentFromComponentURL,type:'post',data:JSON.stringify(data),contentType:'application/; charset=utf-8',success:function(data){if(data.result&&data.result.orderNo&&data.result.orderToken){document.querySelector('#orderToken').value=data.result.orderToken;document.querySelector('#merchantReference').value=data.result.orderNo;}if(data.result&&data.result.fullResponse&&data.result.fullResponse.action){component.handleAction(data.result.fullResponse.action);}else{document.querySelector('#paymentFromComponentStateData').value=JSON.stringify('null');$('#dwfrm_billing').trigger('submit');}}}).fail(function/* xhr, textStatus */(){});}$('#dwfrm_billing').submit(function(e){if(['paypal','mbway','amazonpay',...qrCodeMethods].indexOf(selectedMethod)>-1&&!document.querySelector('#paymentFromComponentStateData').value){e.preventDefault();var form=$(this);var url=form.attr('action');$.ajax({type:'POST',url:url,data:form.serialize(),async:false,success:function(data){formErrorsExist=data.fieldErrors;}});}});function getQRCodeConfig(){return{showPayButton:true,onSubmit:(state,component)=>{$('#dwfrm_billing').trigger('submit');if(formErrorsExist){return;}assignPaymentMethodValue();document.querySelector('#adyenStateData').value=JSON.stringify(state.data);paymentFromComponent(state.data,component);},onAdditionalDetails:(state/* , component */)=>{document.querySelector('#paymentFromComponentStateData').value=JSON.stringify(state.data);$('#dwfrm_billing').trigger('submit');}};}function getCardConfig(){return{enableStoreDetails:showStoreDetails,onBrand:function(brandObject){$('#cardType').val(brandObject.brand);},onFieldValid:function(data){if(data.endDigits){maskedCardNumber=MASKED_CC_PREFIX+data.endDigits;$('#cardNumber').val(maskedCardNumber);}},onChange:function(state){isValid=state.isValid;var methodToUpdate=state.data.paymentMethod.storedPaymentMethodId?`storedCard${state.data.paymentMethod.storedPaymentMethodId}`:selectedMethod;$('#browserInfo').val(JSON.stringify(state.data.browserInfo));componentsObj[methodToUpdate].isValid=isValid;componentsObj[methodToUpdate].stateData=state.data;}};}function getGooglePayConfig(){return{environment:window.Configuration.environment,onSubmit:()=>{assignPaymentMethodValue();document.querySelector('#billing-submit').disabled=false;document.querySelector('#billing-submit').click();},configuration:{gatewayMerchantId:window.merchantAccount},showPayButton:true,buttonColor:'white'};}function getAmazonpayConfig(){return{showPayButton:true,productType:'PayAndShip',checkoutMode:'ProcessOrder',locale:window.Configuration.locale,returnUrl:window.returnURL,addressDetails:{name:sessionsResponse.shippingAddress.firstName+' '+sessionsResponse.shippingAddress.lastName,addressLine1:sessionsResponse.shippingAddress.address1,city:sessionsResponse.shippingAddress.city,stateOrRegion:sessionsResponse.shippingAddress.city,postalCode:sessionsResponse.shippingAddress.postalCode,countryCode:sessionsResponse.shippingAddress.country,phoneNumber:sessionsResponse.shippingAddress.phone},onClick:(resolve,reject)=>{$('#dwfrm_billing').trigger('submit');if(formErrorsExist){reject();}else{assignPaymentMethodValue();resolve();}},onError:()=>{}};}function getInstallmentValues(maxValue){const values=[];for(let i=1;i<=maxValue;i+=1){values.push(i);}return values;}function setInstallments(amount){try{if(installmentLocales.indexOf(window.Configuration.locale)<0){return;}const[minAmount,numOfInstallments]=window.installments?window.installments.replace(/\[|]/g,'').split(','):[null,null];if(minAmount<=amount.value){checkoutConfiguration.paymentMethodsConfiguration.card.installmentOptions={card:{}};// eslint-disable-next-line max-len
+checkoutConfiguration.paymentMethodsConfiguration.card.installmentOptions.card.values=getInstallmentValues(numOfInstallments);checkoutConfiguration.paymentMethodsConfiguration.card.showInstallmentAmounts=true;}}catch(e){}// eslint-disable-line no-empty
+}/**
+ * @function
+ * @description Initializes Adyen CSE billing events
+ */exports.initBilling=function(){initializeBillingEvents();};exports.initAccount=function(){initializeAccountEvents();};exports.renderGenericComponent=function(){renderGenericComponent();};},{"./adyen-giving":2,"./amazon":4,"./summary":50}],2:[function(require,module,exports){let donation;function handleOnDonate(state,component){if(!state.isValid){return;}const selectedAmount=state.data.amount;const donationData={amountValue:selectedAmount.value,amountCurrency:selectedAmount.currency,orderNo:window.orderNo,pspReference:window.pspReference};$.ajax({url:window.donateURL,type:'post',data:JSON.stringify(donationData),contentType:'application/; charset=utf-8',success(){component.setStatus('success');}});}function handleOnCancel(){const adyenGiving=document.getElementById('adyenGiving');adyenGiving.style.transition='all 3s ease-in-out';adyenGiving.style.display='none';donation.unmount();}if(document.querySelector('.adyen-payment-details')&&window.adyenGivingAvailable){const adyenGivingNode=document.getElementById('donate-container');let amounts;try{amounts=JSON.parse(window.donationAmounts);}catch(e){amounts=[];}const donationConfig={amounts,backgroundUrl:window.adyenGivingBackgroundUrl,description:window.charityDescription,logoUrl:window.adyenGivingLogoUrl,name:window.charityName,url:window.charityWebsite,showCancelButton:true,onDonate:handleOnDonate,onCancel:handleOnCancel};AdyenCheckout(window.Configuration).then(checkout=>{checkout.create('donation',donationConfig).mount(adyenGivingNode);});}},{}],3:[function(require,module,exports){'use strict';var progress=require('./progress'),util=require('./util');var currentRequests=[];/**
+ * @function
+ * @description Ajax request to get json response
+ * @param {Boolean} async Asynchronous or not
+ * @param {String} url URI for the request
+ * @param {Object} data Name/Value pair data request
+ * @param {Function} callback Callback function to be called
+ */var getJson=function(options){options.url=util.toAbsoluteUrl(options.url);// return if no url exists or url matches a current request
+if(!options.url||currentRequests[options.url]){return;}currentRequests[options.url]=true;// make the server call
+$.ajax({dataType:'json',url:options.url,async:typeof options.async==='undefined'||options.async===null?true:options.async,data:options.data||{}})// success
+.done(function(response){if(options.callback){options.callback(response);}})// failed
+.fail(function(xhr,textStatus){if(textStatus==='parsererror'){window.alert(Resources.BAD_RESPONSE);}if(options.callback){options.callback(null);}})// executed on success or fail
+.always(function(){// remove current request from hash
+if(currentRequests[options.url]){delete currentRequests[options.url];}});};/**
+ * @function
+ * @description ajax request to load html response in a given container
+ * @param {String} url URI for the request
+ * @param {Object} data Name/Value pair data request
+ * @param {Function} callback Callback function to be called
+ * @param {Object} target Selector or element that will receive content
+ */var load=function(options){options.url=util.toAbsoluteUrl(options.url);// return if no url exists or url matches a current request
+if(!options.url||currentRequests[options.url]){return;}currentRequests[options.url]=true;// make the server call
+$.ajax({dataType:'html',url:util.appendParamToURL(options.url,'format','ajax'),data:options.data,xhrFields:{withCredentials:true}}).done(function(response){// success
+if(options.target){$(options.target).empty().html(response);}if(options.callback){options.callback(response);}}).fail(function(xhr,textStatus){// failed
+if(textStatus==='parsererror'){window.alert(Resources.BAD_RESPONSE);}options.callback(null,textStatus);}).always(function(){progress.hide();// remove current request from hash
+if(currentRequests[options.url]){delete currentRequests[options.url];}});};exports.getJson=getJson;exports.load=load;},{"./progress":42,"./util":53}],4:[function(require,module,exports){if(window.amazonCheckoutSessionId){window.sessionsResponse=null;const amazonPayNode=document.getElementById('amazonContainerSG');function handleAuthorised(response){document.querySelector('#result').value=JSON.stringify({pspReference:response.fullResponse.pspReference,resultCode:response.fullResponse.resultCode,paymentMethod:response.fullResponse.paymentMethod?response.fullResponse.paymentMethod:response.fullResponse.additionalData.paymentMethod});document.querySelector('#paymentFromComponentStateData').value=JSON.stringify(response);document.querySelector('#showConfirmationForm').submit();}function handleError(){document.querySelector('#result').value=JSON.stringify({error:true});document.querySelector('#paymentFromComponentStateData').value=JSON.stringify({error:true});document.querySelector('#showConfirmationForm').submit();}function handleAmazonResponse(response,component){if(response.fullResponse&&response.fullResponse.action){component.handleAction(response.fullResponse.action);}else if(response.resultCode===window.resultCodeAuthorised){handleAuthorised(response);}else{// first try the amazon decline flow
+component.handleDeclineFlow();// if this does not trigger a redirect, try the regular handleError flow
+handleError();}}function paymentFromComponent(data,component){$.ajax({url:window.paymentFromComponentURL,type:'post',contentType:'application/; charset=utf-8',data:JSON.stringify(data),success(response){if(response.result&&response.result.orderNo&&response.result.orderToken){document.querySelector('#orderToken').value=response.result.orderToken;document.querySelector('#merchantReference').value=response.result.orderNo;}handleAmazonResponse(response.result,component);}});}const amazonConfig={showOrderButton:false,returnUrl:window.returnURL,configuration:{merchantId:window.amazonMerchantID,storeId:window.amazonStoreID,publicKeyId:window.amazonPublicKeyID},amazonCheckoutSessionId:window.amazonCheckoutSessionId,onSubmit:(state,component)=>{document.querySelector('#adyenStateData').value=JSON.stringify(state.data);paymentFromComponent(state.data,component);},onAdditionalDetails:state=>{state.data.paymentMethod='amazonpay';$.ajax({type:'post',url:window.paymentsDetailsURL,data:JSON.stringify({data:state.data,orderToken:document.querySelector('#orderToken').value}),contentType:'application/; charset=utf-8',success(data){if(data.response.isSuccessful){handleAuthorised(data.response);}else if(!data.response.isFinal&&typeof data.response.action==='object'){checkout.createFromAction(data.action).mount('#amazonContainerSG');}else{handleError();}}});}};async function mountAmazonPayComponent(){const checkout=await AdyenCheckout(window.Configuration);const amazonPayComponent=checkout.create('amazonpay',amazonConfig).mount(amazonPayNode);amazonPayComponent.submit();}mountAmazonPayComponent();}},{}],5:[function(require,module,exports){/**
+ * (c) 2009-2014 Demandware Inc.
+ * Subject to standard usage terms and conditions
+ * For all details and documentation:
+ * https://bitbucket.com/demandware/sitegenesis
+ */'use strict';var countries=require('./countries'),dialog=require('./dialog'),minicart=require('./minicart'),page=require('./page'),rating=require('./rating'),searchplaceholder=require('./searchplaceholder'),searchsuggest=require('./searchsuggest'),tooltip=require('./tooltip'),util=require('./util'),validator=require('./validator'),tls=require('./tls'),consentTracking=require('./consentTracking');// if jQuery has not been loaded, load from google cdn
+if(!window.jQuery){var s=document.createElement('script');s.setAttribute('src','https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js');s.setAttribute('type','text/javascript');document.getElementsByTagName('head')[0].appendChild(s);}require('./jquery-ext')();require('./cookieprivacy')();consentTracking.init();require('./captcha')();function initializeEvents(){var controlKeys=['8','13','46','45','36','35','38','37','40','39'];$('body').on('keydown','textarea[data-character-limit]',function(e){var text=$.trim($(this).val()),charsLimit=$(this).data('character-limit'),charsUsed=text.length;if(charsUsed>=charsLimit&&controlKeys.indexOf(e.which.toString())<0){e.preventDefault();}}).on('change keyup mouseup','textarea[data-character-limit]',function(){var text=$.trim($(this).val()),charsLimit=$(this).data('character-limit'),charsUsed=text.length,charsRemain=charsLimit-charsUsed;if(charsRemain<0){$(this).val(text.slice(0,charsRemain));charsRemain=0;}$(this).next('div.char-count').find('.char-remain-count').html(charsRemain);});/**
+ * initialize search suggestions, pending the value of the site preference(enhancedSearchSuggestions)
+ * this will either init the legacy(false) or the beta versions(true) of the the search suggest feature.
+ * */var $searchContainer=$('#navigation .header-search');searchsuggest.init($searchContainer,Resources.SIMPLE_SEARCH);// add show/hide navigation elements
+$('.secondary-navigation .toggle').click(function(){$(this).toggleClass('expanded').next('ul').toggle();});// add generic toggle functionality
+$('.toggle').next('.toggle-content').hide();$('.toggle').click(function(){$(this).toggleClass('expanded').next('.toggle-content').toggle();});// subscribe email box
+var $subscribeEmail=$('.subscribe-email');if($subscribeEmail.length>0){$subscribeEmail.focus(function(){var val=$(this.val());if(val.length>0&&val!==Resources.SUBSCRIBE_EMAIL_DEFAULT){return;// do not animate when contains non-default value
+}$(this).animate({color:'#999999'},500,'linear',function(){$(this).val('').css('color','#333333');});}).blur(function(){var val=$.trim($(this.val()));if(val.length>0){return;// do not animate when contains value
+}$(this).val(Resources.SUBSCRIBE_EMAIL_DEFAULT).css('color','#999999').animate({color:'#333333'},500,'linear');});}$('.privacy-policy').on('click',function(e){e.preventDefault();dialog.open({url:$(e.target).attr('href'),options:{height:600}});});$('.consent-tracking-policy').on('click',function(e){e.preventDefault();consentTracking.show();});// main menu toggle
+$('.menu-toggle').on('click',function(){$('#wrapper').toggleClass('menu-active');});$('.menu-category li .menu-item-toggle').on('click',function(e){e.preventDefault();var $parentLi=$(e.target).closest('li');$parentLi.siblings('li').removeClass('active').find('.menu-item-toggle').removeClass('fa-chevron-up active').addClass('fa-chevron-right');$parentLi.toggleClass('active');$(e.target).toggleClass('fa-chevron-right fa-chevron-up active');});$('.user-account').on('click',function(e){e.preventDefault();$(this).parent('.user-info').toggleClass('active');});}/**
+ * @private
+ * @function
+ * @description Adds class ('js') to html for css targeting and loads js specific styles.
+ */function initializeDom(){// add class to html for css targeting
+$('html').addClass('js');if(SitePreferences.LISTING_INFINITE_SCROLL){$('html').addClass('infinite-scroll');}// load js specific styles
+util.limitCharacters();}var pages={account:require('./pages/account'),cart:require('./pages/cart'),checkout:require('./pages/checkout'),compare:require('./pages/compare'),product:require('./pages/product'),registry:require('./pages/registry'),search:require('./pages/search'),storefront:require('./pages/storefront'),wishlist:require('./pages/wishlist'),storelocator:require('./pages/storelocator')};var app={init:function(){if(document.cookie.length===0){$('').addClass('browser-compatibility-alert').append($('').addClass('browser-error').html(Resources.COOKIES_DISABLED)).appendTo('#browser-check');}initializeDom();initializeEvents();// init specific global components
+countries.init();tooltip.init();minicart.init();validator.init();rating.init();searchplaceholder.init();// execute page specific initializations
+$.extend(page,window.pageContext);var ns=page.ns;if(ns&&pages[ns]&&pages[ns].init){pages[ns].init();}// Check TLS status if indicated by site preference
+if(SitePreferences.CHECK_TLS===true){tls.getUserAgent();}}};// general extension functions
+(function(){String.format=function(){var s=arguments[0];var i,len=arguments.length-1;for(i=0;i\n';attributes+=''+attr.displayName+': ';attributes+=''+attr.displayValue+'\n';attributes+='';}attributes+='
'].join('\n');};// hide swatches that are not selected or not part of a Product Variation Group
+var hideSwatches=function(){$('.bonus-product-item:not([data-producttype="master"]) .swatches li').not('.selected').not('.variation-group-value').hide();// prevent unselecting the selected variant
+$('.bonus-product-item .swatches .selected').on('click',function(){return false;});};/**
+ * @private
+ * @function
+ * @description Updates the summary page with the selected bonus product
+ */function updateSummary(){var $bonusProductList=$('#bonus-product-list');if(!selectedList.length){$bonusProductList.find('li.selected-bonus-item').remove();}else{var ulList=$bonusProductList.find('ul.selected-bonus-items').first();var i,len;for(i=0,len=selectedList.length;i=maxItems){$bonusProductList.find('.select-bonus-item').attr('disabled','disabled');}var cartItems=$bonusProductList.find('.selected-bonus-item');cartItems.each(function(){var ci=$(this);var product={uuid:ci.data('uuid'),pid:ci.data('pid'),qty:ci.find('.item-qty').text(),name:ci.find('.item-name').html(),attributes:{}};var attributes=ci.find('ul.item-attributes li');attributes.each(function(){var li=$(this);product.attributes[li.data('attributeId')]={displayName:li.children('.display-name').html(),displayValue:li.children('.display-value').html()};});selectedList.push(product);});$bonusProductList.on('click','.bonus-product-item a[href].swatchanchor',function(e){e.preventDefault();var url=this.href,$this=$(this);url=util.appendParamsToUrl(url,{'source':'bonus','format':'ajax'});$.ajax({url:url,success:function(response){$this.closest('.bonus-product-item').empty().html(response);hideSwatches();}});}).on('change','.input-text',function(){$bonusProductList.find('.select-bonus-item').removeAttr('disabled');$(this).closest('.bonus-product-form').find('.quantity-error').text('');}).on('click','.select-bonus-item',function(e){e.preventDefault();if(selectedList.length>=maxItems){$bonusProductList.find('.select-bonus-item').attr('disabled','disabled');$bonusProductList.find('.bonus-items-available').text('0');return;}var form=$(this).closest('.bonus-product-form'),detail=$(this).closest('.product-detail'),uuid=form.find('input[name="productUUID"]').val(),qtyVal=form.find('input[name="Quantity"]').val(),qty=isNaN(qtyVal)?1:+qtyVal;if(qty>maxItems){$bonusProductList.find('.select-bonus-item').attr('disabled','disabled');form.find('.quantity-error').text(Resources.BONUS_PRODUCT_TOOMANY);return;}var product={uuid:uuid,pid:form.find('input[name="pid"]').val(),qty:qty,name:detail.find('.product-name').text(),attributes:detail.find('.product-variations').data('attributes'),options:[]};var optionSelects=form.find('.product-option');optionSelects.each(function(){product.options.push({name:this.name,value:$(this).val(),display:$(this).children(':selected').first().html()});});selectedList.push(product);updateSummary();}).on('click','.remove-link',function(e){e.preventDefault();var container=$(this).closest('.selected-bonus-item');if(!container.data('uuid')){return;}var uuid=container.data('uuid');var i,len=selectedList.length;for(i=0;imaxItems){bonusProducts.bonusproducts[0].product.qty=maxItems;}// make the server call
+$.ajax({type:'POST',dataType:'json',cache:false,contentType:'application/json',url:url,data:JSON.stringify(bonusProducts)}).done(function(){// success
+page.refresh();}).fail(function(xhr,textStatus){// failed
+if(textStatus==='parsererror'){window.alert(Resources.BAD_RESPONSE);}else{window.alert(Resources.SERVER_CONNECTION_ERROR);}}).always(function(){$bonusProduct.dialog('close');});}).on('click','#more-bonus-products',function(e){e.preventDefault();var uuid=$('#bonus-product-list').data().lineItemDetail.uuid;//get the next page of choice of bonus products
+var lineItemDetail=JSON.parse($('#bonus-product-list').attr('data-line-item-detail'));lineItemDetail.pageStart=lineItemDetail.pageStart+lineItemDetail.pageSize;$('#bonus-product-list').attr('data-line-item-detail',JSON.stringify(lineItemDetail));var url=util.appendParamsToUrl(Urls.getBonusProducts,{bonusDiscountLineItemUUID:uuid,format:'ajax',lazyLoad:'true',pageStart:lineItemDetail.pageStart,pageSize:$('#bonus-product-list').data().lineItemDetail.pageSize,bonusProductsTotal:$('#bonus-product-list').data().lineItemDetail.bpTotal});$.ajax({type:'GET',cache:false,contentType:'application/json',url:url}).done(function(data){//add the new page to DOM and remove 'More' link if it is the last page of results
+$('#more-bonus-products').before(data);if(lineItemDetail.pageStart+lineItemDetail.pageSize>=$('#bonus-product-list').data().lineItemDetail.bpTotal){$('#more-bonus-products').remove();}}).fail(function(xhr,textStatus){if(textStatus==='parsererror'){window.alert(Resources.BAD_RESPONSE);}else{window.alert(Resources.SERVER_CONNECTION_ERROR);}});});}var bonusProductsView={/**
+ * @function
+ * @description Open the list of bonus products selection dialog
+ */show:function(url){var $bonusProduct=$('#bonus-product-dialog');// create the dialog
+dialog.open({target:$bonusProduct,url:url,options:{width:795,title:Resources.BONUS_PRODUCTS},callback:function(){initializeGrid();hideSwatches();}});},/**
+ * @function
+ * @description Open bonus product promo prompt dialog
+ */loadBonusOption:function(){var self=this,bonusDiscountContainer=document.querySelector('.bonus-discount-container');if(!bonusDiscountContainer){return;}// get the html from minicart, then trash it
+var bonusDiscountContainerHtml=bonusDiscountContainer.outerHTML;bonusDiscountContainer.parentNode.removeChild(bonusDiscountContainer);dialog.open({html:bonusDiscountContainerHtml,options:{width:400,title:Resources.BONUS_PRODUCT,buttons:[{text:Resources.SELECT_BONUS_PRODUCTS,click:function(){var uuid=$('.bonus-product-promo').data('lineitemid'),url=util.appendParamsToUrl(Urls.getBonusProducts,{bonusDiscountLineItemUUID:uuid,source:'bonus',format:'ajax',lazyLoad:'false',pageStart:0,pageSize:10,bonusProductsTotal:-1});$(this).dialog('close');self.show(url);}},{text:Resources.NO_THANKS,click:function(){$(this).dialog('close');}}]},callback:function(){// show hide promo details
+$('.show-promo-details').on('click',function(){$('.promo-details').toggleClass('visible');});}});}};module.exports=bonusProductsView;},{"./dialog":12,"./page":18,"./util":53}],7:[function(require,module,exports){'use strict';var dialog=require('./dialog');var util=require('./util');var SessionAttributes=window.SessionAttributes;/**
+ * @function captcha Used to display/control the scrim containing the simulated captcha code
+ **/module.exports=function(){/**
+ * if the session.privacy.ratelimited element is present then show the notification
+ * NOTE: You will probably want to replace this with a call to an actual CAPTCHA system to replace the simple one here
+ */if(SessionAttributes.SHOW_CAPTCHA){dialog.open({html:'
'+Resources.ARE_YOU_HUMAN+'
',options:{closeOnEscape:false,dialogClass:'no-close',buttons:[{text:Resources.OK,click:function(){var url=util.appendParamsToUrl(Urls.rateLimiterReset,{format:'ajax'});$.ajax({url:url});$(this).dialog('close');}}]}});}};},{"./dialog":12,"./util":53}],8:[function(require,module,exports){'use strict';var page=require('./page'),util=require('./util'),TPromise=require('promise');var _currentCategory='',MAX_ACTIVE=6;/**
+ * @private
+ * @function
+ * @description Verifies the number of elements in the compare container and updates it with sequential classes for ui targeting
+ */function refreshContainer(){var $compareContainer=$('.compare-items');var $compareItems=$compareContainer.find('.compare-item');var numActive=$compareItems.filter('.active').length;if(numActive<2){$('#compare-items-button').attr('disabled','disabled');}else{$('#compare-items-button').removeAttr('disabled');}$compareContainer.toggle(numActive>0);}/**
+ * @private
+ * @function
+ * @description Adds an item to the compare container and refreshes it
+ */function addToList(data){// get the first compare-item not currently active
+var $item=$('.compare-items .compare-item').not('.active').first(),$productTile=$('#'+data.uuid);if($item.length===0){if($productTile.length>0){$productTile.find('.compare-check')[0].checked=false;}window.alert(Resources.COMPARE_ADD_FAIL);return;}// if already added somehow, return
+if($('[data-uuid="'+data.uuid+'"]').length>0){return;}// set as active item
+$item.addClass('active').attr('data-uuid',data.uuid).attr('data-itemid',data.itemid).data('uuid',data.uuid).data('itemid',data.itemid).append($(data.img).clone().addClass('compare-item-image'));}/**
+ * @private
+ * @function
+ * description Removes an item from the compare container and refreshes it
+ */function removeFromList($item){if($item.length===0){return;}// remove class, data and id from item
+$item.removeClass('active').removeAttr('data-uuid').removeAttr('data-itemid').data('uuid','').data('itemid','')// remove the image
+.find('.compare-item-image').remove();}function addProductAjax(args){var promise=new TPromise(function(resolve,reject){$.ajax({url:Urls.compareAdd,data:{pid:args.itemid,category:_currentCategory},dataType:'json'}).done(function(response){if(!response||!response.success){reject(new Error(Resources.COMPARE_ADD_FAIL));}else{resolve(response);}}).fail(function(jqxhr,status,err){reject(new Error(err));});});return promise;}function removeProductAjax(args){var promise=new TPromise(function(resolve,reject){$.ajax({url:Urls.compareRemove,data:{pid:args.itemid,category:_currentCategory},dataType:'json'}).done(function(response){if(!response||!response.success){reject(new Error(Resources.COMPARE_REMOVE_FAIL));}else{resolve(response);}}).fail(function(jqxhr,status,err){reject(new Error(err));});});return promise;}function shiftImages(){return new TPromise(function(resolve){var $items=$('.compare-items .compare-item');$items.each(function(i,item){var $item=$(item);// last item
+if(i===$items.length-1){return removeFromList($item);}var $next=$items.eq(i+1);if($next.hasClass('active')){// remove its own image
+$next.find('.compare-item-image').detach().appendTo($item);$item.addClass('active').attr('data-uuid',$next.data('uuid')).attr('data-itemid',$next.data('itemid')).data('uuid',$next.data('uuid')).data('itemid',$next.data('itemid'));}});resolve();});}/**
+ * @function
+ * @description Adds product to the compare table
+ */function addProduct(args){var promise;var $items=$('.compare-items .compare-item');var $cb=$(args.cb);var numActive=$items.filter('.active').length;if(numActive===MAX_ACTIVE){if(!window.confirm(Resources.COMPARE_CONFIRMATION)){$cb[0].checked=false;return;}// remove product using id
+var $firstItem=$items.first();promise=removeItem($firstItem).then(function(){return shiftImages();});}else{promise=TPromise.resolve(0);}return promise.then(function(){return addProductAjax(args).then(function(){addToList(args);if($cb&&$cb.length>0){$cb[0].checked=true;}refreshContainer();});}).then(null,function(){if($cb&&$cb.length>0){$cb[0].checked=false;}});}/**
+ * @function
+ * @description Removes product from the compare table
+ * @param {object} args - the arguments object should have the following properties: itemid, uuid and cb (checkbox)
+ */function removeProduct(args){var $cb=args.cb?$(args.cb):null;return removeProductAjax(args).then(function(){var $item=$('[data-uuid="'+args.uuid+'"]');removeFromList($item);if($cb&&$cb.length>0){$cb[0].checked=false;}refreshContainer();},function(){if($cb&&$cb.length>0){$cb[0].checked=true;}});}function removeItem($item){var uuid=$item.data('uuid'),$productTile=$('#'+uuid);return removeProduct({itemid:$item.data('itemid'),uuid:uuid,cb:$productTile.length===0?null:$productTile.find('.compare-check')});}/**
+ * @private
+ * @function
+ * @description Initializes the DOM-Object of the compare container
+ */function initializeDom(){var $compareContainer=$('.compare-items');_currentCategory=$compareContainer.data('category')||'';var $active=$compareContainer.find('.compare-item').filter('.active');$active.each(function(){var $productTile=$('#'+$(this).data('uuid'));if($productTile.length===0){return;}$productTile.find('.compare-check')[0].checked=true;});// set container state
+refreshContainer();}/**
+ * @private
+ * @function
+ * @description Initializes the events on the compare container
+ */function initializeEvents(){// add event to buttons to remove products
+$('.compare-item').on('click','.compare-item-remove',function(){removeItem($(this).closest('.compare-item'));});// Button to go to compare page
+$('#compare-items-button').on('click',function(){page.redirect(util.appendParamToURL(Urls.compareShow,'category',_currentCategory));});// Button to clear all compared items
+// rely on refreshContainer to take care of hiding the container
+$('#clear-compared-items').on('click',function(){$('.compare-items .active').each(function(){removeItem($(this));});});}exports.init=function(){initializeDom();initializeEvents();};exports.addProduct=addProduct;exports.removeProduct=removeProduct;},{"./page":18,"./util":53,"promise":70}],9:[function(require,module,exports){'use strict';var dialog=require('./dialog');var util=require('./util');/**
+ * @function getConsent Used to display/control of the modal containing the consent management message
+ **/function getConsent(){dialog.open({url:Urls.consentTracking,options:{closeOnEscape:false,dialogClass:'no-close',buttons:[{text:Resources.TRACKING_CONSENT,click:function(){$(this).dialog('close');$.ajax({type:'GET',url:util.appendParamToURL(Urls.consentTrackingSetSession,'consentTracking',true),success:function(){showPrivacyDialog();},error:function(){showPrivacyDialog();}});}},{text:Resources.TRACKING_NO_CONSENT,click:function(){$(this).dialog('close');$.ajax({type:'GET',url:util.appendParamToURL(Urls.consentTrackingSetSession,'consentTracking',false),success:function(){showPrivacyDialog();},error:function(){showPrivacyDialog();}});}}]}});}function enablePrivacyCookies(){if(document.cookie.indexOf('dw=1')<0){document.cookie='dw=1; path=/';}if(document.cookie.indexOf('dw_cookies_accepted')<0){document.cookie='dw_cookies_accepted=1; path=/';}}function showPrivacyDialog(){if(SitePreferences.COOKIE_HINT===true&&document.cookie.indexOf('dw_cookies_accepted')<0){// check for privacy policy page
+if($('.privacy-policy').length===0){dialog.open({url:Urls.cookieHint,options:{closeOnEscape:false,dialogClass:'no-close',buttons:[{text:Resources.I_AGREE,click:function(){$(this).dialog('close');enablePrivacyCookies();}}]}});}}else{// Otherwise, we don't need to show the asset, just enable the cookies
+enablePrivacyCookies();}}var consentTracking={init:function(){if(consent==null&&SitePreferences.CONSENT_TRACKING_HINT){// eslint-disable-line no-undef
+getConsent();}if(consent!=null&&SitePreferences.CONSENT_TRACKING_HINT){// eslint-disable-line no-undef
+showPrivacyDialog();}},show:function(){getConsent();}};module.exports=consentTracking;},{"./dialog":12,"./util":53}],10:[function(require,module,exports){'use strict';var dialog=require('./dialog');/**
+ * @function cookieprivacy Used to display/control the scrim containing the cookie privacy code
+ **/module.exports=function(){/**
+ * If we have not accepted cookies AND we're not on the Privacy Policy page, then show the notification
+ * NOTE: You will probably want to adjust the Privacy Page test to match your site's specific privacy / cookie page
+ */if(!SitePreferences.CONSENT_TRACKING_HINT&&SitePreferences.COOKIE_HINT===true&&document.cookie.indexOf('dw_cookies_accepted')<0){// check for privacy policy page
+if($('.privacy-policy').length===0){dialog.open({url:Urls.cookieHint,options:{closeOnEscape:false,dialogClass:'no-close',buttons:[{text:Resources.I_AGREE,click:function(){$(this).dialog('close');enableCookies();}}]}});}}else{// Otherwise, we don't need to show the asset, just enable the cookies
+enableCookies();}function enableCookies(){if(document.cookie.indexOf('dw=1')<0){document.cookie='dw=1; path=/';}if(document.cookie.indexOf('dw_cookies_accepted')<0){document.cookie='dw_cookies_accepted=1; path=/';}}};},{"./dialog":12}],11:[function(require,module,exports){'use strict';exports.init=function init(){$('.country-selector .current-country').on('click',function(){$('.country-selector .selector').toggleClass('active');$(this).toggleClass('selector-active');});// set currency first before reload
+$('.country-selector .selector .locale').on('click',function(e){e.preventDefault();var url=this.href;var currency=this.getAttribute('data-currency');$.ajax({dataType:'json',url:Urls.setSessionCurrency,data:{format:'ajax',currencyMnemonic:currency}}).done(function(response){if(!response.success){throw new Error('Unable to set currency');}window.location.href=url;});});};},{}],12:[function(require,module,exports){'use strict';var ajax=require('./ajax'),util=require('./util'),_=require('lodash'),imagesLoaded=require('imagesloaded');var dialog={/**
+ * @function
+ * @description Appends a dialog to a given container (target)
+ * @param {Object} params params.target can be an id selector or an jquery object
+ */create:function(params){var $target,id;if(_.isString(params.target)){if(params.target.charAt(0)==='#'){$target=$(params.target);}else{$target=$('#'+params.target);}}else if(params.target instanceof jQuery){$target=params.target;}else{$target=$('#dialog-container');}// if no element found, create one
+if($target.length===0){if($target.selector&&$target.selector.charAt(0)==='#'){id=$target.selector.substr(1);$target=$('
').attr('id',id).addClass('dialog-content').appendTo('body');}}// create the dialog
+this.$container=$target;this.$container.dialog(_.merge({},this.settings,params.options||{}));},/**
+ * @function
+ * @description Opens a dialog using the given url (params.url) or html (params.html)
+ * @param {Object} params
+ * @param {Object} params.url should contain the url
+ * @param {String} params.html contains the html of the dialog content
+ */open:function(params){// close any open dialog
+this.close();this.create(params);this.replace(params);},/**
+ * @description populate the dialog with html content, then open it
+ **/openWithContent:function(params){var content,position,callback;if(!this.$container){return;}content=params.content||params.html;if(!content){return;}this.$container.empty().html(content);if(!this.$container.dialog('isOpen')){this.$container.dialog('open');}if(params.options){position=params.options.position;}if(!position){position=this.settings.position;}imagesLoaded(this.$container).on('done',function(){this.$container.dialog('option','position',position);}.bind(this));callback=typeof params.callback==='function'?params.callback:function(){};callback();},/**
+ * @description Replace the content of current dialog
+ * @param {object} params
+ * @param {string} params.url - If the url property is provided, an ajax call is performed to get the content to replace
+ * @param {string} params.html - If no url property is provided, use html provided to replace
+ */replace:function(params){if(!this.$container){return;}if(params.url){params.url=util.appendParamToURL(params.url,'format','ajax');ajax.load({url:params.url,data:params.data,callback:function(response){params.content=response;this.openWithContent(params);}.bind(this)});}else if(params.html){this.openWithContent(params);}},/**
+ * @function
+ * @description Closes the dialog
+ */close:function(){if(!this.$container){return;}this.$container.dialog('close');},exists:function(){return this.$container&&this.$container.length>0;},isActive:function(){return this.exists()&&this.$container.children.length>0;},settings:{autoOpen:false,height:'auto',modal:true,overlay:{opacity:0.5,background:'black'},resizable:false,title:'',width:'800',close:function(){$(this).dialog('close');},position:{my:'center',at:'center',of:window,collision:'flipfit'}}};module.exports=dialog;},{"./ajax":3,"./util":53,"imagesloaded":67,"lodash":68}],13:[function(require,module,exports){'use strict';var ajax=require('./ajax'),util=require('./util');/**
+ * @function
+ * @description Load details to a given gift certificate
+ * @param {String} id The ID of the gift certificate
+ * @param {Function} callback A function to called
+ */exports.checkBalance=function(id,callback){// load gift certificate details
+var url=util.appendParamToURL(Urls.giftCardCheckBalance,'giftCertificateID',id);ajax.getJson({url:url,callback:callback});};},{"./ajax":3,"./util":53}],14:[function(require,module,exports){'use strict';var ajax=require('./ajax'),minicart=require('./minicart'),util=require('./util');var setAddToCartHandler=function(e){e.preventDefault();var form=$(this).closest('form');var options={url:util.ajaxUrl(form.attr('action')),method:'POST',cache:false,data:form.serialize()};$.ajax(options).done(function(response){if(response.success){ajax.load({url:Urls.minicartGC,data:{lineItemId:response.result.lineItemId},callback:function(response){minicart.show(response);form.find('input,textarea').val('');}});}else{form.find('span.error').hide();for(var id in response.errors.FormErrors){var $errorEl=$('#'+id).addClass('error').removeClass('valid').next('.error');if(!$errorEl||$errorEl.length===0){$errorEl=$('');$('#'+id).after($errorEl);}$errorEl.text(response.errors.FormErrors[id].replace(/\\'/g,'\'')).show();}}}).fail(function(xhr,textStatus){// failed
+if(textStatus==='parsererror'){window.alert(Resources.BAD_RESPONSE);}else{window.alert(Resources.SERVER_CONNECTION_ERROR);}});};exports.init=function(){$('#AddToBasketButton').on('click',setAddToCartHandler);};},{"./ajax":3,"./minicart":17,"./util":53}],15:[function(require,module,exports){'use strict';// jQuery extensions
+module.exports=function(){// params
+// toggleClass - required
+// triggerSelector - optional. the selector for the element that triggers the event handler. defaults to the child elements of the list.
+// eventName - optional. defaults to 'click'
+$.fn.toggledList=function(options){if(!options.toggleClass){return this;}var list=this;return list.on(options.eventName||'click',options.triggerSelector||list.children(),function(e){e.preventDefault();var classTarget=options.triggerSelector?$(this).parent():$(this);classTarget.toggleClass(options.toggleClass);// execute callback if exists
+if(options.callback){options.callback();}});};$.fn.syncHeight=function(){var arr=$.makeArray(this);arr.sort(function(a,b){return $(a).height()-$(b).height();});return this.height($(arr[arr.length-1]).height());};};},{}],16:[function(require,module,exports){'use strict';var dialog=require('./dialog'),page=require('./page'),validator=require('./validator');var login={/**
+ * @private
+ * @function
+ * @description init events for the loginPage
+ */init:function(){//o-auth binding for which icon is clicked
+$('.oAuthIcon').bind('click',function(){$('#OAuthProvider').val(this.id);});//toggle the value of the rememberme checkbox
+$('#dwfrm_login_rememberme').bind('change',function(){if($('#dwfrm_login_rememberme').attr('checked')){$('#rememberme').val('true');}else{$('#rememberme').val('false');}});$('#password-reset').on('click',function(e){e.preventDefault();dialog.open({url:$(e.target).attr('href'),options:{open:function(){validator.init();var $requestPasswordForm=$('[name$="_requestpassword"]');var $submit=$requestPasswordForm.find('[name$="_requestpassword_send"]');$($submit).on('click',function(e){if(!$requestPasswordForm.valid()){return;}e.preventDefault();var data=$requestPasswordForm.serialize();// add form action to data
+data+='&'+$submit.attr('name')+'=';// make sure the server knows this is an ajax request
+if(data.indexOf('ajax')===-1){data+='&format=ajax';}$.ajax({type:'POST',url:$requestPasswordForm.attr('action'),data:data,success:function(response){if(typeof response==='object'&&!response.success&&response.error===Resources.CSRF_TOKEN_MISMATCH){page.redirect(Urls.csrffailed);}else if(typeof response==='string'){dialog.$container.html(response);}},failure:function(){dialog.$container.html('
'+Resources.SERVER_ERROR+'
');}});});}}});});}};module.exports=login;},{"./dialog":12,"./page":18,"./validator":54}],17:[function(require,module,exports){'use strict';var util=require('./util'),bonusProductsView=require('./bonus-products-view');var timer={id:null,clear:function(){if(this.id){window.clearTimeout(this.id);delete this.id;}},start:function(duration,callback){this.id=setTimeout(callback,duration);}};var minicart={init:function(){this.$el=$('#mini-cart');this.$content=this.$el.find('.mini-cart-content');$('.mini-cart-product').eq(0).find('.mini-cart-toggle').addClass('fa-caret-down');$('.mini-cart-product').not(':first').addClass('collapsed').find('.mini-cart-toggle').addClass('fa-caret-right');$('.mini-cart-toggle').on('click',function(){$(this).toggleClass('fa-caret-down fa-caret-right');$(this).closest('.mini-cart-product').toggleClass('collapsed');});// events
+this.$el.find('.mini-cart-total').on('mouseenter',function(){if(this.$content.not(':visible')){this.slide();}}.bind(this));this.$content.on('mouseenter',function(){timer.clear();}).on('mouseleave',function(){timer.clear();timer.start(30,this.close.bind(this));}.bind(this));},/**
+ * @function
+ * @description Shows the given content in the mini cart
+ * @param {String} A HTML string with the content which will be shown
+ */show:function(html){this.$el.html(html);util.scrollBrowser(0);this.init();this.slide();bonusProductsView.loadBonusOption();},/**
+ * @function
+ * @description Slides down and show the contents of the mini cart
+ */slide:function(){timer.clear();// show the item
+this.$content.slideDown('slow');// after a time out automatically close it
+timer.start(6000,this.close.bind(this));},/**
+ * @function
+ * @description Closes the mini cart with given delay
+ * @param {Number} delay The delay in milliseconds
+ */close:function(delay){timer.clear();this.$content.slideUp(delay);}};module.exports=minicart;},{"./bonus-products-view":6,"./util":53}],18:[function(require,module,exports){'use strict';var util=require('./util');var page={title:'',type:'',params:util.getQueryStringParams(window.location.search.substr(1)),redirect:function(newURL){setTimeout(function(){window.location.href=newURL;},0);},refresh:function(){setTimeout(function(){window.location.assign(window.location.href);},500);}};module.exports=page;},{"./util":53}],19:[function(require,module,exports){'use strict';var giftcert=require('../giftcert'),tooltip=require('../tooltip'),util=require('../util'),dialog=require('../dialog'),page=require('../page'),login=require('../login'),validator=require('../validator'),adyenCheckout=require('../adyen-checkout');/**
+ * @function
+ * @description Initializes the events on the address form (apply, cancel, delete)
+ * @param {Element} form The form which will be initialized
+ */function initializeAddressForm(){var $form=$('#edit-address-form');$form.find('input[name="format"]').remove();tooltip.init();//$("").attr({type:"hidden", name:"format", value:"ajax"}).appendTo(form);
+$form.on('click','.apply-button',function(e){e.preventDefault();if(!$form.valid()){return false;}var url=util.appendParamToURL($form.attr('action'),'format','ajax');var applyName=$form.find('.apply-button').attr('name');var options={url:url,data:$form.serialize()+'&'+applyName+'=x',type:'POST'};$.ajax(options).done(function(data){if(typeof data!=='string'){if(data.success){dialog.close();page.refresh();}else if(data.error){page.redirect(Urls.csrffailed);}else{window.alert(data.message);return false;}}else{$('#dialog-container').html(data);account.init();tooltip.init();}});}).on('click','.cancel-button, .close-button',function(e){e.preventDefault();dialog.close();}).on('click','.delete-button',function(e){e.preventDefault();if(window.confirm(String.format(Resources.CONFIRM_DELETE,Resources.TITLE_ADDRESS))){var url=util.appendParamsToUrl(Urls.deleteAddress,{AddressID:$form.find('#addressid').val(),format:'ajax'});$.ajax({url:url,method:'POST',dataType:'json'}).done(function(data){if(data.status.toLowerCase()==='ok'){dialog.close();page.refresh();}else if(data.message.length>0){window.alert(data.message);return false;}else{dialog.close();page.refresh();}});}});validator.init();}/**
+ * @private
+ * @function
+ * @description Toggles the list of Orders
+ */function toggleFullOrder(){$('.order-items').find('li.hidden:first').prev('li').append('View All').children('.toggle').click(function(){$(this).parent().siblings('li.hidden').show();$(this).remove();});}/**
+ * @private
+ * @function
+ * @description Binds the events on the address form (edit, create, delete)
+ */function initAddressEvents(){var addresses=$('#addresses');if(addresses.length===0){return;}addresses.on('click','.address-edit, .address-create',function(e){e.preventDefault();dialog.open({url:this.href,options:{open:initializeAddressForm}});}).on('click','.delete',function(e){e.preventDefault();if(window.confirm(String.format(Resources.CONFIRM_DELETE,Resources.TITLE_ADDRESS))){$.ajax({url:util.appendParamToURL($(this).attr('href'),'format','ajax'),dataType:'json'}).done(function(data){if(data.status.toLowerCase()==='ok'){page.redirect(Urls.addressesList);}else if(data.message.length>0){window.alert(data.message);}else{page.refresh();}});}});}/**
+ * @private
+ * @function
+ * @description Binds the events of the payment methods list (delete card)
+ */function initPaymentEvents(){$('.add-card').on('click',function(e){e.preventDefault();dialog.open({url:$(e.target).attr('href'),options:{open:initializePaymentForm}});});var paymentList=$('.payment-list');if(paymentList.length===0){return;}util.setDeleteConfirmation(paymentList,String.format(Resources.CONFIRM_DELETE,Resources.TITLE_CREDITCARD));$('form[name="payment-remove"]').on('submit',function(e){e.preventDefault();// override form submission in order to prevent refresh issues
+var button=$(this).find('.delete');$('').attr({type:'hidden',name:button.attr('name'),value:button.attr('value')||'delete card'}).appendTo($(this));var data=$(this).serialize();$.ajax({type:'POST',url:$(this).attr('action'),data:data}).done(function(){page.redirect(Urls.paymentsList);});});}function initializePaymentForm(){$('#CreditCardForm').on('click','.cancel-button',function(e){e.preventDefault();dialog.close();});if(SitePreferences.ADYEN_SF_ENABLED){adyenCheckout.initAccount();}}/**
+ * @private
+ * @function
+ * @description Binds the events of the order, address and payment pages
+ */function initializeEvents(){toggleFullOrder();initAddressEvents();initPaymentEvents();login.init();}var account={init:function(){initializeEvents();giftcert.init();},initCartLogin:function(){login.init();}};module.exports=account;},{"../adyen-checkout":1,"../dialog":12,"../giftcert":14,"../login":16,"../page":18,"../tooltip":52,"../util":53,"../validator":54}],20:[function(require,module,exports){'use strict';var account=require('./account'),bonusProductsView=require('../bonus-products-view'),quickview=require('../quickview'),cartStoreInventory=require('../storeinventory/cart');/**
+ * @private
+ * @function
+ * @description Binds events to the cart page (edit item's details, bonus item's actions, coupon code entry)
+ */function initializeEvents(){$('#cart-table').on('click','.item-edit-details a',function(e){e.preventDefault();quickview.show({url:e.target.href,source:'cart'});}).on('click','.bonus-item-actions a, .item-details .bonusproducts a',function(e){e.preventDefault();bonusProductsView.show(this.href);});// override enter key for coupon code entry
+$('form input[name$="_couponCode"]').on('keydown',function(e){if(e.which===13&&$(this).val().length===0){return false;}});//to prevent multiple submissions of the form when removing a product from the cart
+var removeItemEvent=false;$('button[name$="deleteProduct"]').on('click',function(e){if(removeItemEvent){e.preventDefault();}else{removeItemEvent=true;}});}exports.init=function(){initializeEvents();if(SitePreferences.STORE_PICKUP){cartStoreInventory.init();}account.initCartLogin();};},{"../bonus-products-view":6,"../quickview":43,"../storeinventory/cart":47,"./account":19}],21:[function(require,module,exports){'use strict';var util=require('../../util');var shipping=require('./shipping');/**
+ * @function
+ * @description Selects the first address from the list of addresses
+ */exports.init=function(){var $form=$('.address');// select address from list
+$('select[name$="_addressList"]',$form).on('change',function(){var selected=$(this).children(':selected').first();var selectedAddress=$(selected).data('address');if(!selectedAddress){return;}util.fillAddressFields(selectedAddress,$form);shipping.updateShippingMethodList();// re-validate the form
+$form.validate().form();});};},{"../../util":53,"./shipping":26}],22:[function(require,module,exports){/*eslint-disable */'use strict';var ajax=require('../../ajax'),formPrepare=require('./formPrepare'),giftcard=require('../../giftcard'),util=require('../../util'),adyenCheckout=require('../../adyen-checkout');/**
+ * @function
+ * @description Fills the Credit Card form with the passed data-parameter and clears the former cvn input
+ * @param {Object} data The Credit Card data (holder, type, masked number, expiration month/year)
+ */function setCCFields(data){var $creditCard=$('[data-method="CREDIT_CARD"]');$creditCard.find('input[name$="creditCard_owner"]').val(data.holder).trigger('change');$creditCard.find('select[name$="_type"]').val(data.type).trigger('change');$creditCard.find('input[name*="_creditCard_number"]').val(data.maskedNumber).trigger('change');$creditCard.find('[name$="_month"]').val(data.expirationMonth).trigger('change');$creditCard.find('[name$="_year"]').val(data.expirationYear).trigger('change');$creditCard.find('input[name$="_cvn"]').val('').trigger('change');$creditCard.find('[name$="creditCard_selectedCardID"]').val(data.selectedCardID).trigger('change');}/**
+ * @function
+ * @description Updates the credit card form with the attributes of a given card
+ * @param {String} cardID the credit card ID of a given card
+ */function populateCreditCardForm(cardID){// load card details
+var url=util.appendParamToURL(Urls.billingSelectCC,'creditCardUUID',cardID);ajax.getJson({url:url,callback:function(data){if(!data){window.alert(Resources.CC_LOAD_ERROR);return false;}setCCFields(data);}});}$('input[name="brandCode"]').on('change',function(e){$("#dwfrm_adyPaydata_issuer").val("");$('.checkoutComponent').hide();$('#component_'+$(this).val()).show();});/**
+ * @function
+ * @description Changes the payment method form depending on the passed paymentMethodID
+ * @param {String} paymentMethodID the ID of the payment method, to which the payment method form should be changed to
+ */function updatePaymentMethod(paymentMethodID){var $paymentMethods=$('.payment-method');$paymentMethods.removeClass('payment-method-expanded');var $selectedPaymentMethod=$paymentMethods.filter('[data-method="'+paymentMethodID+'"]');if($selectedPaymentMethod.length===0){$selectedPaymentMethod=$('[data-method="Custom"]');}$selectedPaymentMethod.addClass('payment-method-expanded');// ensure checkbox of payment method is checked
+$('input[name$="_selectedPaymentMethodID"]').removeAttr('checked');$('input[value='+paymentMethodID+']').prop('checked','checked');formPrepare.validateForm();}/**
+ * @function
+ * @description Changes the payment type or issuerId of the selected payment method
+ * @param {String, Boolean} value of payment type or issuerId and a test value to see which one it is, to which the payment type or issuerId should be changed to
+ */function updatePaymentType(selectedPayType,issuerType){if(issuerType){$('#dwfrm_adyPaydata_issuer').val(selectedPayType);}else{$('input[name="brandCode"]').removeAttr('checked');$('input[value='+selectedPayType+']').prop('checked','checked');}// if the payment type has hidden fields reveal it
+$('#component_'+selectedPayType).show();formPrepare.validateForm();}/**
+ * @function
+ * @description Adyen - Initializes the visibility of HPP fields
+ */function initializeHPPFields(){if($('[name="brandCode"]:checked').hasClass('openInvoice')){$('.additionalfield').hide().find('input').val('');$('.additionalfield.'+$('.checkout-billing').find('select.country').val()).show();}else{$('.additionalfield').hide().find('input').val('');}}/**
+ * @function
+ * @description loads billing address, Gift Certificates, Coupon and Payment methods
+ */exports.init=function(){var $checkoutForm=$('.checkout-billing');var $addGiftCert=$('#add-giftcert');var $giftCertCode=$('input[name$="_giftCertCode"]');var $addCoupon=$('#add-coupon');var $couponCode=$('input[name$="_couponCode"]');var $selectPaymentMethod=$('.payment-method-options');var selectedPaymentMethod=$selectPaymentMethod.find(':checked').val();var $payType=$('[name="brandCode"]');var $issuer=$('.issuer');var selectedPayType=$payType.find(':checked').val();formPrepare.init({formSelector:'form[id$="billing"]',continueSelector:'[name$="billing_save"]'});// default payment method to 'CREDIT_CARD'
+updatePaymentMethod(selectedPaymentMethod?selectedPaymentMethod:'CREDIT_CARD');$selectPaymentMethod.on('click','input[type="radio"]',function(){updatePaymentMethod($(this).val());if($(this).val()=='Adyen'&&$payType.length>0){//set payment type of Adyen to the first one
+updatePaymentType(selectedPayType?selectedPayType:$payType[0].value,false);}else{$payType.removeAttr('checked');}});$issuer.on('change',function(){updatePaymentType($(this).val(),true);});$payType.on('change',function(){$('#selectedIssuer').val("");$issuer.hide();$('.checkoutComponent').hide();$('#component_'+$(this).val()).show();if($(this).siblings(".issuer").length>0){$('#selectedIssuer').val($(this).siblings(".issuer").val());$(this).siblings('.issuer').show();}});// select credit card from list
+$('#creditCardList').on('change',function(){var cardUUID=$(this).val();if(!cardUUID){return;}populateCreditCardForm(cardUUID);// remove server side error
+$('.required.error').removeClass('error');$('.error-message').remove();});$('#check-giftcert').on('click',function(e){e.preventDefault();var $balance=$('.balance');if($giftCertCode.length===0||$giftCertCode.val().length===0){var error=$balance.find('span.error');if(error.length===0){error=$('').addClass('error').appendTo($balance);}error.html(Resources.GIFT_CERT_MISSING);return;}giftcard.checkBalance($giftCertCode.val(),function(data){if(!data||!data.giftCertificate){$balance.html(Resources.GIFT_CERT_INVALID).removeClass('success').addClass('error');return;}$balance.html(Resources.GIFT_CERT_BALANCE+' '+data.giftCertificate.balance).removeClass('error').addClass('success');});});$addGiftCert.on('click',function(e){e.preventDefault();var code=$giftCertCode.val(),$error=$checkoutForm.find('.giftcert-error');if(code.length===0){$error.html(Resources.GIFT_CERT_MISSING);return;}var url=util.appendParamsToUrl(Urls.redeemGiftCert,{giftCertCode:code,format:'ajax'});$.getJSON(url,function(data){var fail=false;var msg='';if(!data){msg=Resources.BAD_RESPONSE;fail=true;}else if(!data.success){msg=data.message.split('<').join('<').split('>').join('>');fail=true;}if(fail){$error.html(msg);return;}else{window.location.assign(Urls.billing);}});});$addCoupon.on('click',function(e){e.preventDefault();var $error=$checkoutForm.find('.coupon-error'),code=$couponCode.val();if(code.length===0){$error.html(Resources.COUPON_CODE_MISSING);return;}var url=util.appendParamsToUrl(Urls.addCoupon,{couponCode:code,format:'ajax'});$.getJSON(url,function(data){var fail=false;var msg='';if(!data){msg=Resources.BAD_RESPONSE;fail=true;}else if(!data.success){msg=data.message.split('<').join('<').split('>').join('>');fail=true;}if(fail){$error.html(msg);return;}//basket check for displaying the payment section, if the adjusted total of the basket is 0 after applying the coupon
+//this will force a page refresh to display the coupon message based on a parameter message
+if(data.success&&data.baskettotal===0){window.location.assign(Urls.billing);}});});// trigger events on enter
+$couponCode.on('keydown',function(e){if(e.which===13){e.preventDefault();$addCoupon.click();}});$giftCertCode.on('keydown',function(e){if(e.which===13){e.preventDefault();$addGiftCert.click();}});if(SitePreferences.ADYEN_SF_ENABLED){adyenCheckout.initBilling();}};},{"../../adyen-checkout":1,"../../ajax":3,"../../giftcard":13,"../../util":53,"./formPrepare":23}],23:[function(require,module,exports){'use strict';var _=require('lodash');var $form,$continue,$requiredInputs,validator;var hasEmptyRequired=function(){// filter out only the visible fields
+var requiredValues=$requiredInputs.filter(':visible').map(function(){return $(this).val();});return _(requiredValues).includes('');};var validateForm=function(){// only validate form when all required fields are filled to avoid
+// throwing errors on empty form
+if(!validator){return;}if(!hasEmptyRequired()){if(validator.form()){$continue.removeAttr('disabled');}}else{$continue.attr('disabled','disabled');}};var validateEl=function(){if($(this).val()===''){$continue.attr('disabled','disabled');}else{// enable continue button on last required field that is valid
+// only validate single field
+if(validator.element(this)&&!hasEmptyRequired()){$continue.removeAttr('disabled');}else{$continue.attr('disabled','disabled');}}};var init=function(opts){if(!opts.formSelector||!opts.continueSelector){throw new Error('Missing form and continue action selectors.');}$form=$(opts.formSelector);$continue=$(opts.continueSelector);validator=$form.validate();$requiredInputs=$('.required',$form).find(':input');validateForm();// start listening
+$requiredInputs.on('change',validateEl);$requiredInputs.filter('input').on('keyup',_.debounce(validateEl,200));};exports.init=init;exports.validateForm=validateForm;exports.validateEl=validateEl;},{"lodash":68}],24:[function(require,module,exports){'use strict';var address=require('./address'),billing=require('./billing'),multiship=require('./multiship'),shipping=require('./shipping');/**
+ * @function Initializes the page events depending on the checkout stage (shipping/billing)
+ */exports.init=function(){address.init();if($('.checkout-shipping').length>0){shipping.init();}else if($('.checkout-multi-shipping').length>0){multiship.init();}else{billing.init();}//if on the order review page and there are products that are not available diable the submit order button
+if($('.order-summary-footer').length>0){if($('.notavailable').length>0){$('.order-summary-footer .submit-order .button-fancy-large').attr('disabled','disabled');}}};},{"./address":21,"./billing":22,"./multiship":25,"./shipping":26}],25:[function(require,module,exports){'use strict';var address=require('./address'),formPrepare=require('./formPrepare'),dialog=require('../../dialog'),util=require('../../util'),validator=require('../../validator');/**
+ * @function
+ * @description Initializes gift message box for multiship shipping, the message box starts off as hidden and this will display it if the radio button is checked to yes, also added event handler to listen for when a radio button is pressed to display the message box
+ */function initMultiGiftMessageBox(){$.each($('.item-list'),function(){var $this=$(this);var $giftMessage=$this.find('.gift-message-text');//handle initial load
+$giftMessage.toggleClass('hidden',$('input[name$="_isGift"]:checked',this).val()!=='true');//set event listeners
+$this.on('change',function(){$giftMessage.toggleClass('hidden',$('input[name$="_isGift"]:checked',this).val()!=='true');});});}/**
+ * @function
+ * @description capture add edit adddress form events
+ */function addEditAddress(target){var $addressForm=$('form[name$="multishipping_editAddress"]'),$addressDropdown=$addressForm.find('select[name$=_addressList]'),$addressList=$addressForm.find('.address-list'),add=true,originalUUID,resetOptionValue=false,selectedAddressUUID=$(target).parent().siblings('.select-address').val();$addressDropdown.on('change',function(e){e.preventDefault();var selectedAddress=$addressList.find('select').val();if(selectedAddress!=='newAddress'){selectedAddress=$.grep($addressList.data('addresses'),function(add){return add.UUID===selectedAddress;})[0];add=false;resetOptionValue=false;// proceed to fill the form with the selected address
+util.fillAddressFields(selectedAddress,$addressForm);}else if(selectedAddress==='newAddress'){add=true;resetOptionValue=true;$addressForm.find('.input-text, .input-select').val('');}else{//reset the form if the value of the option is not a UUID
+$addressForm.find('.input-text, .input-select').val('');}});$addressForm.on('click','.cancel',function(e){e.preventDefault();dialog.close();});$addressForm.on('submit',function(e){e.preventDefault();if(!$addressForm.valid()){return false;}$.getJSON(Urls.addEditAddress,$addressForm.serialize(),function(response){if(!response.success){$('#multiaddresserror').html(Resources.COULD_NOT_SAVE_ADDRESS);return;}$('#multiaddresserror').toggleClass('hidden',response.success);var address=response.address,$shippingAddress=$(target).closest('.shippingaddress'),$select=$shippingAddress.find('.select-address'),$selected=$select.find('option:selected'),newOption='';dialog.close();if(address.UUID!==originalUUID){resetOptionValue=true;}if(add){$('.shippingaddress select').removeClass('no-option').append(newOption);$('.no-address').hide();}else{$('.shippingaddress select').find('option[value="'+address.UUID+'"]').html(newOption);}// if there's no previously selected option, select it
+if($selected.length===0||$selected.val()===''||resetOptionValue){$select.find('option[value="'+address.UUID+'"]').prop('selected','selected').trigger('change');}});});//preserve the uuid of the option for the hop up form
+if(selectedAddressUUID){//update the form with selected address
+$addressList.find('option').each(function(){//check the values of the options
+if($(this).attr('value')===selectedAddressUUID){$(this).prop('selected','selected');$addressDropdown.trigger('change');}});originalUUID=selectedAddressUUID;}validator.init();}/**
+ * @function
+ * @description shows gift message box in multiship, and if the page is the multi shipping address page it will call initmultishipshipaddress() to initialize the form
+ */exports.init=function(){initMultiGiftMessageBox();if($('.cart-row .shippingaddress .select-address').length>0){formPrepare.init({continueSelector:'[name$="addressSelection_save"]',formSelector:'[id$="multishipping_addressSelection"]'});}$('.edit-address').on('click','span',function(e){dialog.open({url:this.attributes.href.value,options:{open:function(){address.init();addEditAddress(e.target);}}});});};},{"../../dialog":12,"../../util":53,"../../validator":54,"./address":21,"./formPrepare":23}],26:[function(require,module,exports){'use strict';var ajax=require('../../ajax'),formPrepare=require('./formPrepare'),progress=require('../../progress'),tooltip=require('../../tooltip'),util=require('../../util');var shippingMethods;/**
+ * @function
+ * @description Initializes gift message box, if shipment is gift
+ */function giftMessageBox(){// show gift message box, if shipment is gift
+$('.gift-message-text').toggleClass('hidden',$('input[name$="_shippingAddress_isGift"]:checked').val()!=='true');}/**
+ * @function
+ * @description updates the order summary based on a possibly recalculated basket after a shipping promotion has been applied
+ */function updateSummary(){var $summary=$('#secondary.summary');// indicate progress
+progress.show($summary);// load the updated summary area
+$summary.load(Urls.summaryRefreshURL,function(){// hide edit shipping method link
+$summary.fadeIn('fast');$summary.find('.checkout-mini-cart .minishipment .header a').hide();$summary.find('.order-totals-table .order-shipping .label a').hide();});}/**
+ * @function
+ * @description Helper method which constructs a URL for an AJAX request using the
+ * entered address information as URL request parameters.
+ */function getShippingMethodURL(url,extraParams){var $form=$('.address');var params={address1:$form.find('input[name$="_address1"]').val(),address2:$form.find('input[name$="_address2"]').val(),countryCode:$form.find('select[id$="_country"]').val(),stateCode:$form.find('select[id$="_state"]').val(),postalCode:$form.find('input[name$="_postal"]').val(),city:$form.find('input[name$="_city"]').val()};return util.appendParamsToUrl(url,$.extend(params,extraParams));}/**
+ * @function
+ * @description selects a shipping method for the default shipment and updates the summary section on the right hand side
+ * @param
+ */function selectShippingMethod(shippingMethodID){// nothing entered
+if(!shippingMethodID){return;}// attempt to set shipping method
+var url=getShippingMethodURL(Urls.selectShippingMethodsList,{shippingMethodID:shippingMethodID});ajax.getJson({url:url,callback:function(data){updateSummary();if(!data||!data.shippingMethodID){window.alert('Couldn\'t select shipping method.');return false;}// display promotion in UI and update the summary section,
+// if some promotions were applied
+$('.shippingpromotions').empty();// TODO the for loop below isn't doing anything?
+// if (data.shippingPriceAdjustments && data.shippingPriceAdjustments.length > 0) {
+// var len = data.shippingPriceAdjustments.length;
+// for (var i=0; i < len; i++) {
+// var spa = data.shippingPriceAdjustments[i];
+// }
+// }
+}});}/**
+ * @function
+ * @description Make an AJAX request to the server to retrieve the list of applicable shipping methods
+ * based on the merchandise in the cart and the currently entered shipping address
+ * (the address may be only partially entered). If the list of applicable shipping methods
+ * has changed because new address information has been entered, then issue another AJAX
+ * request which updates the currently selected shipping method (if needed) and also updates
+ * the UI.
+ */function updateShippingMethodList(){var $shippingMethodList=$('#shipping-method-list');if(!$shippingMethodList||$shippingMethodList.length===0){return;}var url=getShippingMethodURL(Urls.shippingMethodsJSON);ajax.getJson({url:url,callback:function(data){if(!data){window.alert('Couldn\'t get list of applicable shipping methods.');return false;}if(shippingMethods&&shippingMethods.toString()===data.toString()){// No need to update the UI. The list has not changed.
+return true;}// We need to update the UI. The list has changed.
+// Cache the array of returned shipping methods.
+shippingMethods=data;// indicate progress
+progress.show($shippingMethodList);// load the shipping method form
+var smlUrl=getShippingMethodURL(Urls.shippingMethodsList);$shippingMethodList.load(smlUrl,function(){$shippingMethodList.fadeIn('fast');// rebind the radio buttons onclick function to a handler.
+$shippingMethodList.find('[name$="_shippingMethodID"]').click(function(){selectShippingMethod($(this).val());});// update the summary
+updateSummary();progress.hide();tooltip.init();//if nothing is selected in the shipping methods select the first one
+if($shippingMethodList.find('.input-radio:checked').length===0){$shippingMethodList.find('.input-radio:first').prop('checked','checked');}});}});}exports.init=function(){formPrepare.init({continueSelector:'[name$="shippingAddress_save"]',formSelector:'[id$="singleshipping_shippingAddress"]'});$('input[name$="_shippingAddress_isGift"]').on('click',giftMessageBox);$('.address').on('change','input[name$="_addressFields_address1"], input[name$="_addressFields_address2"], select[name$="_addressFields_states_state"], input[name$="_addressFields_city"], input[name$="_addressFields_zip"]',updateShippingMethodList);giftMessageBox();updateShippingMethodList();};exports.updateShippingMethodList=updateShippingMethodList;},{"../../ajax":3,"../../progress":42,"../../tooltip":52,"../../util":53,"./formPrepare":23}],27:[function(require,module,exports){'use strict';var addProductToCart=require('./product/addToCart'),ajax=require('../ajax'),page=require('../page'),productTile=require('../product-tile'),quickview=require('../quickview');/**
+ * @private
+ * @function
+ * @description Binds the click events to the remove-link and quick-view button
+ */function initializeEvents(){$('#compare-table').on('click','.remove-link',function(e){e.preventDefault();ajax.getJson({url:this.href,callback:function(){page.refresh();}});}).on('click','.open-quick-view',function(e){e.preventDefault();var url=$(this).closest('.product').find('.thumb-link').attr('href');quickview.show({url:url,source:'quickview'});});$('#compare-category-list').on('change',function(){$(this).closest('form').submit();});}exports.init=function(){productTile.init();initializeEvents();addProductToCart();};},{"../ajax":3,"../page":18,"../product-tile":41,"../quickview":43,"./product/addToCart":28}],28:[function(require,module,exports){'use strict';var dialog=require('../../dialog'),minicart=require('../../minicart'),page=require('../../page'),util=require('../../util'),Promise=require('promise'),_=require('lodash');/**
+ * @description Make the AJAX request to add an item to cart
+ * @param {Element} form The form element that contains the item quantity and ID data
+ * @returns {Promise}
+ */var addItemToCart=function(form){var $form=$(form),$qty=$form.find('input[name="Quantity"]');if($qty.length===0||isNaN($qty.val())||parseInt($qty.val(),10)===0){$qty.val('1');}return Promise.resolve($.ajax({type:'POST',url:util.ajaxUrl(Urls.addProduct),data:$form.serialize()})).then(function(response){// handle error in the response
+if(response.error){throw new Error(response.error);}else{return response;}});};/**
+ * @description Handler to handle the add to cart event
+ */var addToCart=function(e){e.preventDefault();var $form=$(this).closest('form');addItemToCart($form).then(function(response){var $uuid=$form.find('input[name="uuid"]');if($uuid.length>0&&$uuid.val().length>0){page.refresh();}else{// do not close quickview if adding individual item that is part of product set
+// @TODO should notify the user some other way that the add action has completed successfully
+if(!$(this).hasClass('sub-product-item')){dialog.close();}minicart.show(response);}}.bind(this));};/**
+ * @description Handler to handle the add all items to cart event
+ */var addAllToCart=function(e){e.preventDefault();var $productForms=$('#product-set-list').find('form').toArray();Promise.all(_.map($productForms,addItemToCart)).then(function(responses){dialog.close();// show the final response only, which would include all the other items
+minicart.show(responses[responses.length-1]);});};/**
+ * @function
+ * @description Binds the click event to a given target for the add-to-cart handling
+ */module.exports=function(){$('.add-to-cart[disabled]').attr('title',$('.availability-msg').text());$('.product-detail').on('click','.add-to-cart',addToCart);$('#add-all-to-cart').on('click',addAllToCart);};},{"../../dialog":12,"../../minicart":17,"../../page":18,"../../util":53,"lodash":68,"promise":70}],29:[function(require,module,exports){'use strict';var ajax=require('../../ajax'),util=require('../../util');var updateContainer=function(data){var $availabilityMsg=$('#pdpMain .availability .availability-msg');var message;// this should be lexically scoped, when `let` is supported (ES6)
+if(!data){$availabilityMsg.html(Resources.ITEM_STATUS_NOTAVAILABLE);return;}$availabilityMsg.empty();// Look through levels ... if msg is not empty, then create span el
+if(data.levels.IN_STOCK>0){if(data.levels.PREORDER===0&&data.levels.BACKORDER===0&&data.levels.NOT_AVAILABLE===0){// Just in stock
+message=Resources.IN_STOCK;}else{// In stock with conditions ...
+message=data.inStockMsg;}$availabilityMsg.append('
+ */function initializeEvents(){var $main=$('#main');// compare checked
+$main.on('click','input[type="checkbox"].compare-check',function(){var cb=$(this);var tile=cb.closest('.product-tile');var func=this.checked?compareWidget.addProduct:compareWidget.removeProduct;var itemImg=tile.find('.product-image a img').first();func({itemid:tile.data('itemid'),uuid:tile[0].id,img:itemImg,cb:cb});});// handle toggle refinement blocks
+$main.on('click','.refinement h3',function(){$(this).toggleClass('expanded').siblings('ul').toggle();});// handle events for updating grid
+$main.on('click','.refinements a, .pagination a, .breadcrumb-refinement-value a',function(e){// don't intercept for category and folder refinements, as well as unselectable
+if($(this).parents('.category-refinement').length>0||$(this).parents('.folder-refinement').length>0||$(this).parent().hasClass('unselectable')){return;}e.preventDefault();updateProductListing(this.href);});// handle events item click. append params.
+$main.on('click','.product-tile a:not("#quickviewbutton")',function(){var a=$(this);// get current page refinement values
+var wl=window.location;var qsParams=wl.search.length>1?util.getQueryStringParams(wl.search.substr(1)):{};var hashParams=wl.hash.length>1?util.getQueryStringParams(wl.hash.substr(1)):{};// merge hash params with querystring params
+var params=$.extend(hashParams,qsParams);if(!params.start){params.start=0;}// get the index of the selected item and save as start parameter
+var tile=a.closest('.product-tile');var idx=tile.data('idx')?+tile.data('idx'):0;// convert params.start to integer and add index
+params.start=+params.start+(idx+1);// set the hash and allow normal action to continue
+a[0].hash=$.param(params);});// handle sorting change
+$main.on('change','.sort-by select',function(e){e.preventDefault();updateProductListing($(this).find('option:selected').val());}).on('change','.items-per-page select',function(){var refineUrl=$(this).find('option:selected').val();if(refineUrl==='INFINITE_SCROLL'){$('html').addClass('infinite-scroll').removeClass('disable-infinite-scroll');}else{$('html').addClass('disable-infinite-scroll').removeClass('infinite-scroll');updateProductListing(refineUrl);}});}exports.init=function(){compareWidget.init();if(SitePreferences.LISTING_INFINITE_SCROLL){$(window).on('scroll',infiniteScroll);}productTile.init();initializeEvents();};},{"../compare-widget":8,"../product-tile":41,"../progress":42,"../util":53}],38:[function(require,module,exports){'use strict';exports.init=function(){$('#homepage-slider')// responsive slides
+.on('jcarousel:create jcarousel:reload',function(){var element=$(this),width=element.innerWidth();element.jcarousel('items').css('width',width+'px');}).jcarousel({wrap:'circular'}).jcarouselAutoscroll({interval:5000});$('#homepage-slider .jcarousel-control').on('jcarouselpagination:active','a',function(){$(this).addClass('active');}).on('jcarouselpagination:inactive','a',function(){$(this).removeClass('active');}).jcarouselPagination({item:function(page){return''+page+'';}});$('#vertical-carousel').jcarousel({vertical:true}).jcarouselAutoscroll({interval:5000});$('#vertical-carousel .jcarousel-prev').on('jcarouselcontrol:active',function(){$(this).removeClass('inactive');}).on('jcarouselcontrol:inactive',function(){$(this).addClass('inactive');}).jcarouselControl({target:'-=1'});$('#vertical-carousel .jcarousel-next').on('jcarouselcontrol:active',function(){$(this).removeClass('inactive');}).on('jcarouselcontrol:inactive',function(){$(this).addClass('inactive');}).jcarouselControl({target:'+=1'});};},{}],39:[function(require,module,exports){'use strict';var dialog=require('../dialog');exports.init=function(){$('.store-details-link').on('click',function(e){e.preventDefault();dialog.open({url:$(e.target).attr('href')});});};},{"../dialog":12}],40:[function(require,module,exports){'use strict';var addProductToCart=require('./product/addToCart'),page=require('../page'),login=require('../login'),util=require('../util');exports.init=function(){addProductToCart();$('#editAddress').on('change',function(){page.redirect(util.appendParamToURL(Urls.wishlistAddress,'AddressID',$(this).val()));});//add js logic to remove the , from the qty feild to pass regex expression on client side
+$('.option-quantity-desired input').on('focusout',function(){$(this).val($(this).val().replace(',',''));});login.init();};},{"../login":16,"../page":18,"../util":53,"./product/addToCart":28}],41:[function(require,module,exports){'use strict';var imagesLoaded=require('imagesloaded'),quickview=require('./quickview');function initQuickViewButtons(){$('.tiles-container .product-image').on('mouseenter',function(){var $qvButton=$('#quickviewbutton');if($qvButton.length===0){$qvButton=$(''+Resources.QUICK_VIEW+'');}var $link=$(this).find('.thumb-link');$qvButton.attr({'href':$link.attr('href'),'title':$link.attr('title')}).appendTo(this);$qvButton.on('click',function(e){e.preventDefault();quickview.show({url:$(this).attr('href'),source:'quickview'});});});}function gridViewToggle(){$('.toggle-grid').on('click',function(){$('.search-result-content').toggleClass('wide-tiles');$(this).toggleClass('wide');});}/**
+ * @private
+ * @function
+ * @description Initializes events on the product-tile for the following elements:
+ * - swatches
+ * - thumbnails
+ */function initializeEvents(){initQuickViewButtons();gridViewToggle();$('.swatch-list').on('mouseleave',function(){// Restore current thumb image
+var $tile=$(this).closest('.product-tile'),$thumb=$tile.find('.product-image .thumb-link img').eq(0),data=$thumb.data('current');$thumb.attr({src:data.src,alt:data.alt,title:data.title});});$('.swatch-list .swatch').on('click',function(e){e.preventDefault();if($(this).hasClass('selected')){return;}var $tile=$(this).closest('.product-tile');$(this).closest('.swatch-list').find('.swatch.selected').removeClass('selected');$(this).addClass('selected');$tile.find('.thumb-link').attr('href',$(this).attr('href'));$tile.find('name-link').attr('href',$(this).attr('href'));var data=$(this).children('img').filter(':first').data('thumb');var $thumb=$tile.find('.product-image .thumb-link img').eq(0);var currentAttrs={src:data.src,alt:data.alt,title:data.title};$thumb.attr(currentAttrs);$thumb.data('current',currentAttrs);}).on('mouseenter',function(){// get current thumb details
+var $tile=$(this).closest('.product-tile'),$thumb=$tile.find('.product-image .thumb-link img').eq(0),data=$(this).children('img').filter(':first').data('thumb'),current=$thumb.data('current');// If this is the first time, then record the current img
+if(!current){$thumb.data('current',{src:$thumb[0].src,alt:$thumb[0].alt,title:$thumb[0].title});}// Set the tile image to the values provided on the swatch data attributes
+$thumb.attr({src:data.src,alt:data.alt,title:data.title});});}exports.init=function(){var $tiles=$('.tiles-container .product-tile');if($tiles.length===0){return;}imagesLoaded('.tiles-container').on('done',function(){$tiles.syncHeight().each(function(idx){$(this).data('idx',idx);});});initializeEvents();};},{"./quickview":43,"imagesloaded":67}],42:[function(require,module,exports){'use strict';var $loader;/**
+ * @function
+ * @description Shows an AJAX-loader on top of a given container
+ * @param {Element} container The Element on top of which the AJAX-Loader will be shown
+ */var show=function(container){var target=!container||$(container).length===0?$('body'):$(container);$loader=$loader||$('.loader');if($loader.length===0){$loader=$('').addClass('loader').append($('').addClass('loader-indicator'),$('').addClass('loader-bg'));}return $loader.appendTo(target).show();};/**
+ * @function
+ * @description Hides an AJAX-loader
+ */var hide=function(){if($loader){$loader.hide();}};exports.show=show;exports.hide=hide;},{}],43:[function(require,module,exports){'use strict';var dialog=require('./dialog'),product=require('./pages/product'),util=require('./util'),_=require('lodash');var makeUrl=function(url,source,productListID){if(source){url=util.appendParamToURL(url,'source',source);}if(productListID){url=util.appendParamToURL(url,'productlistid',productListID);}return url;};var removeParam=function(url){if(url.indexOf('?')!==-1){return url.substring(0,url.indexOf('?'));}else{return url;}};var quickview={init:function(){if(!this.exists()){this.$container=$('').attr('id','QuickViewDialog').appendTo(document.body);}this.productLinks=$('#search-result-items .thumb-link').map(function(index,thumbLink){return $(thumbLink).attr('href');});},setup:function(qvUrl){var $btnNext=$('.quickview-next'),$btnPrev=$('.quickview-prev');product.initializeEvents();this.productLinkIndex=_(this.productLinks).findIndex(function(url){return removeParam(url)===removeParam(qvUrl);});// hide the buttons on the compare page or when there are no other products
+if(this.productLinks.length<=1||$('.compareremovecell').length>0){$btnNext.hide();$btnPrev.hide();return;}if(this.productLinkIndex===this.productLinks.length-1){$btnNext.attr('disabled','disabled');}if(this.productLinkIndex===0){$btnPrev.attr('disabled','disabled');}$btnNext.on('click',function(e){e.preventDefault();this.navigateQuickview(1);}.bind(this));$btnPrev.on('click',function(e){e.preventDefault();this.navigateQuickview(-1);}.bind(this));},/**
+ * @param {Number} step - How many products away from current product to navigate to. Negative number means navigate backward
+ */navigateQuickview:function(step){// default step to 0
+this.productLinkIndex+=step?step:0;var url=makeUrl(this.productLinks[this.productLinkIndex],'quickview');dialog.replace({url:url,callback:this.setup.bind(this,url)});},/**
+ * @description show quick view dialog
+ * @param {Object} options
+ * @param {String} options.url - url of the product details
+ * @param {String} options.source - source of the dialog to be appended to URL
+ * @param {String} options.productlistid - to be appended to URL
+ * @param {Function} options.callback - callback once the dialog is opened
+ */show:function(options){var url;if(!this.exists()){this.init();}url=makeUrl(options.url,options.source,options.productlistid);dialog.open({target:this.$container,url:url,options:{width:920,title:Resources.QUICK_VIEW_POPUP,open:function(){this.setup(url);if(typeof options.callback==='function'){options.callback();}}.bind(this)}});},exists:function(){return this.$container&&this.$container.length>0;}};module.exports=quickview;},{"./dialog":12,"./pages/product":31,"./util":53,"lodash":68}],44:[function(require,module,exports){'use strict';/**
+ * copied from https://github.com/darkskyapp/string-hash
+ */function hashFn(str){var hash=5381,i=str.length;while(i){hash=hash*33^str.charCodeAt(--i);}/* JavaScript does bitwise operations (like XOR, above) on 32-bit signed
+ * integers. Since we want the results to be always positive, convert the
+ * signed int to an unsigned by doing an unsigned bitshift. */return hash>>>0;}/**
+ * Create rating based on hash ranging from 2-5
+ * @param pid
+ */function getRating(pid){return hashFn(pid.toString())%30/10+2;}module.exports={init:function(){$('.product-review').each(function(index,review){var pid=$(review).data('pid');if(!pid){return;}// rating range from 2 - 5
+var rating=getRating(pid);var baseRating=Math.floor(rating);var starsCount=0;for(var i=0;i');starsCount++;}// give half star for anything in between
+if(rating>baseRating){$('.rating',review).append('');starsCount++;}if(starsCount<5){for(var j=0;j<5-starsCount;j++){$('.rating',review).append('');}}});}};},{}],45:[function(require,module,exports){'use strict';/**
+ * @private
+ * @function
+ * @description Binds event to the place holder (.blur)
+ */function initializeEvents(){$('#q').focus(function(){var input=$(this);if(input.val()===input.attr('placeholder')){input.val('');}}).blur(function(){var input=$(this);if(input.val()===''||input.val()===input.attr('placeholder')){input.val(input.attr('placeholder'));}}).blur();}exports.init=initializeEvents;},{}],46:[function(require,module,exports){'use strict';var util=require('./util');var currentQuery=null,lastQuery=null,runningQuery=null,listTotal=-1,listCurrent=-1,delay=30,$resultsContainer;/**
+ * @function
+ * @description Handles keyboard's arrow keys
+ * @param keyCode Code of an arrow key to be handled
+ */function handleArrowKeys(keyCode){switch(keyCode){case 38:// keyUp
+listCurrent=listCurrent<=0?listTotal-1:listCurrent-1;break;case 40:// keyDown
+listCurrent=listCurrent>=listTotal-1?0:listCurrent+1;break;default:// reset
+listCurrent=-1;return false;}$resultsContainer.children().removeClass('selected').eq(listCurrent).addClass('selected');$('input[name="q"]').val($resultsContainer.find('.selected .suggestionterm').first().text());return true;}var searchsuggest={/**
+ * @function
+ * @description Configures parameters and required object instances
+ */init:function(container,defaultValue){var $searchContainer=$(container);var $searchForm=$searchContainer.find('form[name="simpleSearch"]');var $searchField=$searchForm.find('input[name="q"]');// disable browser auto complete
+$searchField.attr('autocomplete','off');// on focus listener (clear default value)
+$searchField.focus(function(){if(!$resultsContainer){// create results container if needed
+$resultsContainer=$('').attr('id','search-suggestions').appendTo($searchContainer);}if($searchField.val()===defaultValue){$searchField.val('');}});$(document).on('click',function(e){if(!$searchContainer.is(e.target)){setTimeout(this.clearResults,200);}}.bind(this));// on key up listener
+$searchField.keyup(function(e){// get keyCode (window.event is for IE)
+var keyCode=e.keyCode||window.event.keyCode;// check and treat up and down arrows
+if(handleArrowKeys(keyCode)){return;}// check for an ENTER or ESC
+if(keyCode===13||keyCode===27){this.clearResults();return;}currentQuery=$searchField.val().trim();// no query currently running, init an update
+if(!runningQuery){runningQuery=currentQuery;setTimeout(this.suggest.bind(this),delay);}}.bind(this));},/**
+ * @function
+ * @description trigger suggest action
+ */suggest:function(){// check whether query to execute (runningQuery) is still up to date and had not changed in the meanwhile
+// (we had a little delay)
+if(runningQuery!==currentQuery){// update running query to the most recent search phrase
+runningQuery=currentQuery;}// if it's empty clear the results box and return
+if(runningQuery.length===0){this.clearResults();runningQuery=null;return;}// if the current search phrase is the same as for the last suggestion call, just return
+if(lastQuery===runningQuery){runningQuery=null;return;}// build the request url
+var reqUrl=util.appendParamToURL(Urls.searchsuggest,'q',runningQuery);// execute server call
+$.get(reqUrl,function(data){var suggestionHTML=data,ansLength=suggestionHTML.trim().length;// if there are results populate the results div
+if(ansLength===0){this.clearResults();}else{// update the results div
+$resultsContainer.html(suggestionHTML).fadeIn(200);}// record the query that has been executed
+lastQuery=runningQuery;// reset currently running query
+runningQuery=null;// check for another required update (if current search phrase is different from just executed call)
+if(currentQuery!==lastQuery){// ... and execute immediately if search has changed while this server call was in transit
+runningQuery=currentQuery;setTimeout(this.suggest.bind(this),delay);}this.hideLeftPanel();}.bind(this));},/**
+ * @function
+ * @description
+ */clearResults:function(){if(!$resultsContainer){return;}$resultsContainer.fadeOut(200,function(){$resultsContainer.empty();});},/**
+ * @function
+ * @description
+ */hideLeftPanel:function(){//hide left panel if there is only a matching suggested custom phrase
+if($('.search-suggestion-left-panel-hit').length===1&&$('.search-phrase-suggestion a').text().replace(/(^[\s]+|[\s]+$)/g,'').toUpperCase()===$('.search-suggestion-left-panel-hit a').text().toUpperCase()){$('.search-suggestion-left-panel').css('display','none');$('.search-suggestion-wrapper-full').addClass('search-suggestion-wrapper');$('.search-suggestion-wrapper').removeClass('search-suggestion-wrapper-full');}}};module.exports=searchsuggest;},{"./util":53}],47:[function(require,module,exports){'use strict';var inventory=require('./');var cartInventory={setSelectedStore:function(storeId){var $selectedStore=$('.store-tile.'+storeId),$lineItem=$('.cart-row[data-uuid="'+this.uuid+'"]'),storeAddress=$selectedStore.find('.store-address').html(),storeStatus=$selectedStore.find('.store-status').data('status'),storeStatusText=$selectedStore.find('.store-status').text();this.selectedStore=storeId;$lineItem.find('.instore-delivery .selected-store-address').data('storeId',storeId).attr('data-store-id',storeId).html(storeAddress);$lineItem.find('.instore-delivery .selected-store-availability').data('status',storeStatus).attr('data-status',storeStatus).text(storeStatusText);$lineItem.find('.instore-delivery .delivery-option').removeAttr('disabled').trigger('click');},cartSelectStore:function(selectedStore){var self=this;inventory.getStoresInventory(this.uuid).then(function(stores){inventory.selectStoreDialog({stores:stores,selectedStoreId:selectedStore,selectedStoreText:Resources.SELECTED_STORE,continueCallback:function(){},selectStoreCallback:self.setSelectedStore.bind(self)});}).done();},setDeliveryOption:function(value,storeId){// set loading state
+$('.item-delivery-options').addClass('loading').children().hide();var data={plid:this.uuid,storepickup:value==='store'?true:false};if(value==='store'){data.storepickup=true;data.storeid=storeId;}else{data.storepickup=false;}$.ajax({url:Urls.setStorePickup,data:data,success:function(){// remove loading state
+$('.item-delivery-options').removeClass('loading').children().show();}});},init:function(){var self=this;$('.item-delivery-options .set-preferred-store').on('click',function(e){e.preventDefault();self.uuid=$(this).data('uuid');var selectedStore=$(this).closest('.instore-delivery').find('.selected-store-address').data('storeId');if(!User.zip){inventory.zipPrompt(function(){self.cartSelectStore(selectedStore);});}else{self.cartSelectStore(selectedStore);}});$('.item-delivery-options .delivery-option').on('click',function(){// reset the uuid
+var selectedStore=$(this).closest('.instore-delivery').find('.selected-store-address').data('storeId');self.uuid=$(this).closest('.cart-row').data('uuid');self.setDeliveryOption($(this).val(),selectedStore);});}};module.exports=cartInventory;},{"./":48}],48:[function(require,module,exports){'use strict';var _=require('lodash'),dialog=require('../dialog'),TPromise=require('promise'),util=require('../util');var newLine='\n';var storeTemplate=function(store,selectedStoreId,selectedStoreText){return['
'].join(newLine);};/**
+ * @description test whether zipcode is valid for either US or Canada
+ * @return {Boolean} true if the zipcode is valid for either country, false if it's invalid for both
+ **/var validateZipCode=function(zipCode){var regexes={canada:/^[ABCEGHJKLMNPRSTVXY]\d[ABCEGHJKLMNPRSTVWXYZ]( )?\d[ABCEGHJKLMNPRSTVWXYZ]\d$/i,usa:/^\d{5}(-\d{4})?$/},valid=false;if(!zipCode){return;}_.each(regexes,function(re){var regexp=new RegExp(re);valid=regexp.test(zipCode);});return valid;};var storeinventory={zipPrompt:function(callback){var self=this;dialog.open({html:zipPromptTemplate(),options:{title:Resources.STORE_NEAR_YOU,width:500,buttons:[{text:Resources.SEARCH,click:function(){var zipCode=$('#user-zip').val();if(validateZipCode(zipCode)){self.setUserZip(zipCode);if(callback){callback(zipCode);}}}}],open:function(){$('#user-zip').on('keypress',function(e){if(e.which===13){// trigger the search button
+$('.ui-dialog-buttonset .ui-button').trigger('click');}});}}});},getStoresInventory:function(pid){return TPromise.resolve($.ajax({url:util.appendParamsToUrl(Urls.storesInventory,{pid:pid,zipCode:User.zip}),dataType:'json'}));},/**
+ * @description open the dialog to select store
+ * @param {Array} options.stores
+ * @param {String} options.selectedStoreId
+ * @param {String} options.selectedStoreText
+ * @param {Function} options.continueCallback
+ * @param {Function} options.selectStoreCallback
+ **/selectStoreDialog:function(options){var self=this,stores=options.stores,selectedStoreId=options.selectedStoreId,selectedStoreText=options.selectedStoreText,storeList=storeListTemplate(stores,selectedStoreId,selectedStoreText);dialog.open({html:storeList,options:{title:Resources.SELECT_STORE+' - '+User.zip,buttons:[{text:Resources.CHANGE_LOCATION,click:function(){self.setUserZip(null);// trigger the event to start the process all over again
+$('.set-preferred-store').trigger('click');}.bind(this)},{text:Resources.CONTINUE,click:function(){if(options.continueCallback){options.continueCallback(stores);}dialog.close();}}],open:function(){$('.select-store-button').on('click',function(e){e.preventDefault();var storeId=$(this).data('storeId');// if the store is already selected, don't select again
+if(storeId===selectedStoreId){return;}$('.store-list .store-tile.selected').removeClass('selected').find('.select-store-button').text(Resources.SELECT_STORE);$(this).text(selectedStoreText).closest('.store-tile').addClass('selected');if(options.selectStoreCallback){options.selectStoreCallback(storeId);}});}}});},setUserZip:function(zip){User.zip=zip;$.ajax({type:'POST',url:Urls.setZipCode,data:{zipCode:zip}});},shippingLoad:function(){var $checkoutForm=$('.address');$checkoutForm.off('click');$checkoutForm.on('click','input[name$="_shippingAddress_isGift"]',function(){$(this).parent().siblings('.gift-message-text').toggleClass('hidden',$('input[name$="_shippingAddress_isGift"]:checked').val());});}};module.exports=storeinventory;},{"../dialog":12,"../util":53,"lodash":68,"promise":70}],49:[function(require,module,exports){'use strict';var _=require('lodash'),inventory=require('./');var newLine='\n';var pdpStoreTemplate=function(store){return['
'].join(newLine);}};var storesListing=function(stores){// list all stores on PDP page
+if($('.store-list-pdp-container').length){$('.store-list-pdp-container').remove();}$('.availability-results').append(pdpStoresListingTemplate(stores));};var productInventory={setPreferredStore:function(storeId){User.storeId=storeId;$.ajax({url:Urls.setPreferredStore,type:'POST',data:{storeId:storeId}});},productSelectStore:function(){var self=this;inventory.getStoresInventory(this.pid).then(function(stores){inventory.selectStoreDialog({stores:stores,selectedStoreId:User.storeId,selectedStoreText:Resources.PREFERRED_STORE,continueCallback:storesListing,selectStoreCallback:self.setPreferredStore});}).done();},init:function(){var $availabilityContainer=$('.availability-results'),self=this;this.pid=$('input[name="pid"]').val();$('#product-content .set-preferred-store').on('click',function(e){e.preventDefault();if(!User.zip){inventory.zipPrompt(function(){self.productSelectStore();});}else{self.productSelectStore();}});if($availabilityContainer.length){if(User.storeId){inventory.getStoresInventory(this.pid).then(storesListing);}// See more or less stores in the listing
+$availabilityContainer.on('click','.stores-toggle',function(e){e.preventDefault();$('.store-list-pdp .store-list-item').toggleClass('visible');if($(this).hasClass('collapsed')){$(this).text(Resources.SEE_LESS);}else{$(this).text(Resources.SEE_MORE);}$(this).toggleClass('collapsed');});}}};module.exports=productInventory;},{"./":48,"lodash":68}],50:[function(require,module,exports){// This script is executed only on the checkout summary page
+if((window.location.pathname.includes('COBilling-Billing')||window.location.pathname.includes('Adyen-ShowConfirmation'))&&window.isAdyenPayment){async function handleAction(action){window.Configuration.onAdditionalDetails=onAdditionalDetails;const checkout=await AdyenCheckout(window.Configuration);const actionContainer=document.getElementById('action-container');checkout.createFromAction(action).mount(actionContainer);}// onAdditionalDetails event handler to be included in Adyen Component configuration
+var onAdditionalDetails=function(state){$.ajax({type:'POST',url:'Adyen-PaymentsDetails',data:JSON.stringify({data:state.data,orderToken:window.orderToken}),contentType:'application/json; charset=utf-8',async:false,success:function(data){if(!data.response.isFinal&&typeof data.response.action==='object'){handleAction(data.action);}else{window.location.href=data.response.redirectUrl;}}});};// serializes form data and submits to place order. Then proceeds to handle the result
+function placeOrder(formId){const form=$('#'+formId);$.ajax({method:'POST',url:window.summarySubmitUrl,data:$(form).serialize(),success:function(data){if(data.action){window.orderToken=data.orderToken;document.getElementById('action-modal-SG').style.display="block";handleAction(data.action);}else{window.location.href=data.continueUrl;}},error:function(err){}});}window.addEventListener("load",function(){// Override default submit form behavior
+const formId='submit-order-form';const form=document.getElementById(formId);form.addEventListener("submit",function(event){event.preventDefault();placeOrder(formId);});});}},{}],51:[function(require,module,exports){'use strict';/**
+ * Checks the TLS and displays a warning if appropriate
+ * @function getUserAgent Checks the TLS and displays a warning if appropriate
+ **/function getUserAgent(){// Use an external service to check the TLS of the browser
+// NOTE: this implementation uses https://www.howsmyssl.com
+// you may also wish to consider the API available at https://www.ssllabs.com/projects/ssllabs-apis/index.html
+var url='https://www.howsmyssl.com/a/check';var cookieName='dw_TLSWarning';var cookieValue=getCookie(cookieName);// Test to see if this browser has already been flagged by looking at its cookies
+if(!cookieValue){getTLS(url,function(message){if(message.length>0){showWarning(message[0]);// the browser is bad - set the cookie to true (for 15 minutes)
+setCookie(cookieName,'true',15);}else{// else the browser is good, set the cookie to false (for 30 days) so we don't check again
+setCookie(cookieName,'false',60*24*30);}});}else if(cookieValue==='true'){// if we already know that this is an invalid browser, show the warning
+showWarning(Resources.TLS_WARNING);}}/**
+ * Calls out to the TLS service and calls the callback with a message (if necessary)
+ * @function getTLS
+ *
+ * @param {string} url - URL of external TLS-checking API
+ * @param {function} callback - function to call with response
+ **/function getTLS(url,callback){var message=[];// First, see if the browser is among the suspect browsers to see if a TLS check is necessary
+var userAgent=navigator.userAgent;/** This list derived from https://www.ssllabs.com/ssltest/clients.html **/var badBrowsers=['MSIE 6.0','MSIE 7.0','MSIE 8.0','MSIE 9.0','MSIE 10.0','Android 2.3.7','Android 4.0.4','Android 4.1.1','Android 4.2.2','Android 4.3','Safari 5.1.9 / OS X 10.6.8','Safari 6.0.4 / OS X 10.8.4 '];function checkTLSLevel(data){// If we can determine the TLS level, check to see if it's less than 1.2
+if(parseFloat(data.tls_version.split(' ')[1])<1.1){message.push(Resources.TLS_WARNING);callback(message);//If you want to track statistics on bad TLS hits, include this call
+$.ajax({url:Urls.TLSBadTLS});}}function reportBadBrowser(){// If the TLS level cannot be determined just report that this browser is suspect
+message.push(Resources.TLS_WARNING);callback(message);//If you want to track statistics on deprecated browsers, include this call
+$.ajax({url:Urls.TLSBadBrowser});}for(var i=0;i').addClass('browser-compatibility-alert').append($('').addClass('browser-error').html(message)).appendTo('#browser-check');}/**
+ * @function getCookie
+ *
+ * @param {string} key - The cookie name
+ * @returns {string} value - the value of the cookie if found, null otherwise
+ **/function getCookie(key){var cookies=document.cookie.split(';');for(var i=0;i-1){hash=paramUrl.split('#')[1]||'';paramUrl=paramUrl.split('#')[0];}params=paramUrl.split('&');for(var i=0;iwindow.pageYOffset&&left+width>window.pageXOffset;}if(document.compatMode==='CSS1Compat'){return topwindow.document.documentElement.scrollTop&&left+width>window.document.documentElement.scrollLeft;}},/**
+ * @function
+ * @description Appends the parameter 'format=ajax' to a given path
+ * @param {String} path the relative path
+ */ajaxUrl:function(path){return this.appendParamToURL(path,'format','ajax');},/**
+ * @function
+ * @description
+ * @param {String} url
+ */toAbsoluteUrl:function(url){if(url.indexOf('http')!==0&&url.charAt(0)!=='/'){url='/'+url;}return url;},/**
+ * @function
+ * @description Loads css dynamically from given urls
+ * @param {Array} urls Array of urls from which css will be dynamically loaded.
+ */loadDynamicCss:function(urls){var i,len=urls.length;for(i=0;i').appendTo($('head')).attr({type:'text/css',rel:'stylesheet'}).attr('href',url);// for i.e. <9, href must be added after link has been appended to head
+},// array to keep track of the dynamically loaded CSS files
+loadedCssFiles:[],/**
+ * @function
+ * @description Removes all css files which were dynamically loaded
+ */clearDynamicCss:function(){var i=this.loadedCssFiles.length;while(0>i--){$(this.loadedCssFiles[i]).remove();}this.loadedCssFiles=[];},/**
+ * @function
+ * @description Extracts all parameters from a given query string into an object
+ * @param {String} qs The query string from which the parameters will be extracted
+ */getQueryStringParams:function(qs){if(!qs||qs.length===0){return{};}var params={},unescapedQS=decodeURIComponent(qs);// Use the String::replace method to iterate over each
+// name-value pair in the string.
+unescapedQS.replace(new RegExp('([^?=&]+)(=([^&]*))?','g'),function($0,$1,$2,$3){params[$1]=$3;});return params;},fillAddressFields:function(address,$form){for(var field in address){if(field==='ID'||field==='UUID'||field==='key'){continue;}// if the key in address object ends with 'Code', remove that suffix
+// keys that ends with 'Code' are postalCode, stateCode and countryCode
+$form.find('[name$="'+field.replace('Code','')+'"]').val(address[field]);// update the state fields
+if(field==='countryCode'){$form.find('[name$="country"]').trigger('change');// retrigger state selection after country has changed
+// this results in duplication of the state code, but is a necessary evil
+// for now because sometimes countryCode comes after stateCode
+$form.find('[name$="state"]').val(address.stateCode);}}},/**
+ * @function
+ * @description Updates the number of the remaining character
+ * based on the character limit in a text area
+ */limitCharacters:function(){$('form').find('textarea[data-character-limit]').each(function(){var characterLimit=$(this).data('character-limit');var charCountHtml=String.format(Resources.CHAR_LIMIT_MSG,''+characterLimit+'',''+characterLimit+'');var charCountContainer=$(this).next('div.char-count');if(charCountContainer.length===0){charCountContainer=$('').insertAfter($(this));}charCountContainer.html(charCountHtml);// trigger the keydown event so that any existing character data is calculated
+$(this).change();});},/**
+ * @function
+ * @description Binds the onclick-event to a delete button on a given container,
+ * which opens a confirmation box with a given message
+ * @param {String} container The name of element to which the function will be bind
+ * @param {String} message The message the will be shown upon a click
+ */setDeleteConfirmation:function(container,message){$(container).on('click','.delete',function(){return window.confirm(message);});},/**
+ * @function
+ * @description Scrolls a browser window to a given x point
+ * @param {String} The x coordinate
+ */scrollBrowser:function(xLocation){$('html, body').animate({scrollTop:xLocation},500);},isMobile:function(){var mobileAgentHash=['mobile','tablet','phone','ipad','ipod','android','blackberry','windows ce','opera mini','palm'];var idx=0;var isMobile=false;var userAgent=navigator.userAgent.toLowerCase();while(mobileAgentHash[idx]&&!isMobile){isMobile=userAgent.indexOf(mobileAgentHash[idx])>=0;idx++;}return isMobile;}};module.exports=util;},{"lodash":68}],54:[function(require,module,exports){'use strict';var naPhone=/^\(?([2-9][0-8][0-9])\)?[\-\. ]?([2-9][0-9]{2})[\-\. ]?([0-9]{4})(\s*x[0-9]+)?$/;var regex={phone:{us:naPhone,ca:naPhone,fr:/^0[1-6]{1}(([0-9]{2}){4})|((\s[0-9]{2}){4})|((-[0-9]{2}){4})$/,it:/^(([0-9]{2,4})([-\s\/]{0,1})([0-9]{4,8}))?$/,jp:/^(0\d{1,4}- ?)?\d{1,4}-\d{4}$/,cn:/.*/,gb:/^((\(?0\d{4}\)?\s?\d{3}\s?\d{3})|(\(?0\d{3}\)?\s?\d{3}\s?\d{4})|(\(?0\d{2}\)?\s?\d{4}\s?\d{4}))(\s?\#(\d{4}|\d{3}))?$/},postal:{us:/^\d{5}(-\d{4})?$/,ca:/^[ABCEGHJKLMNPRSTVXY]{1}\d{1}[A-Z]{1} *\d{1}[A-Z]{1}\d{1}$/,fr:/^(F-)?((2[A|B])|[0-9]{2})[0-9]{3}$/,it:/^([0-9]){5}$/,jp:/^([0-9]){3}[-]([0-9]){4}$/,cn:/^([0-9]){6}$/,gb:/^([A-PR-UWYZ0-9][A-HK-Y0-9][AEHMNPRTVXY0-9]?[ABEHMNPRVWXY0-9]? {1,2}[0-9][ABD-HJLN-UW-Z]{2}|GIR 0AA)$/},notCC:/^(?!(([0-9 -]){13,19})).*$/};// global form validator settings
+var settings={errorClass:'error',errorElement:'span',onkeyup:false,onfocusout:function(element){if(!this.checkable(element)){this.element(element);}}};/**
+ * @function
+ * @description Validates a given phone number against the countries phone regex
+ * @param {String} value The phone number which will be validated
+ * @param {String} el The input field
+ */var validatePhone=function(value,el){var country=$(el).closest('form').find('.country');if(country.length===0||country.val().length===0||!regex.phone[country.val().toLowerCase()]){return true;}var rgx=regex.phone[country.val().toLowerCase()];var isOptional=this.optional(el);var isValid=rgx.test($.trim(value));return isOptional||isValid;};/**
+ * @function
+ * @description Validates that a credit card owner is not a Credit card number
+ * @param {String} value The owner field which will be validated
+ * @param {String} el The input field
+ */var validateOwner=function(value){var isValid=regex.notCC.test($.trim(value));return isValid;};/**
+ * Add phone validation method to jQuery validation plugin.
+ * Text fields must have 'phone' css class to be validated as phone
+ */$.validator.addMethod('phone',validatePhone,Resources.INVALID_PHONE);/**
+ * Add CCOwner validation method to jQuery validation plugin.
+ * Text fields must have 'owner' css class to be validated as not a credit card
+ */$.validator.addMethod('owner',validateOwner,Resources.INVALID_OWNER);/**
+ * Add gift cert amount validation method to jQuery validation plugin.
+ * Text fields must have 'gift-cert-amont' css class to be validated
+ */$.validator.addMethod('gift-cert-amount',function(value,el){var isOptional=this.optional(el);var isValid=!isNaN(value)&&parseFloat(value)>=5&&parseFloat(value)<=5000;return isOptional||isValid;},Resources.GIFT_CERT_AMOUNT_INVALID);/**
+ * Add positive number validation method to jQuery validation plugin.
+ * Text fields must have 'positivenumber' css class to be validated as positivenumber
+ */$.validator.addMethod('positivenumber',function(value){if($.trim(value).length===0){return true;}return!isNaN(value)&&Number(value)>=0;},'');// '' should be replaced with error message if needed
+$.extend($.validator.messages,{required:Resources.VALIDATE_REQUIRED,remote:Resources.VALIDATE_REMOTE,email:Resources.VALIDATE_EMAIL,url:Resources.VALIDATE_URL,date:Resources.VALIDATE_DATE,dateISO:Resources.VALIDATE_DATEISO,number:Resources.VALIDATE_NUMBER,digits:Resources.VALIDATE_DIGITS,creditcard:Resources.VALIDATE_CREDITCARD,equalTo:Resources.VALIDATE_EQUALTO,maxlength:$.validator.format(Resources.VALIDATE_MAXLENGTH),minlength:$.validator.format(Resources.VALIDATE_MINLENGTH),rangelength:$.validator.format(Resources.VALIDATE_RANGELENGTH),range:$.validator.format(Resources.VALIDATE_RANGE),max:$.validator.format(Resources.VALIDATE_MAX),min:$.validator.format(Resources.VALIDATE_MIN)});var validator={regex:regex,settings:settings,init:function(){var self=this;$('form:not(.suppress)').each(function(){$(this).validate(self.settings);});},initForm:function(f){$(f).validate(this.settings);}};module.exports=validator;},{}],55:[function(require,module,exports){"use strict";// rawAsap provides everything we need except exception management.
+var rawAsap=require("./raw");// RawTasks are recycled to reduce GC churn.
+var freeTasks=[];// We queue errors to ensure they are thrown in right order (FIFO).
+// Array-as-queue is good enough here, since we are just dealing with exceptions.
+var pendingErrors=[];var requestErrorThrow=rawAsap.makeRequestCallFromTimer(throwFirstError);function throwFirstError(){if(pendingErrors.length){throw pendingErrors.shift();}}/**
+ * Calls a task as soon as possible after returning, in its own event, with priority
+ * over other events like animation, reflow, and repaint. An error thrown from an
+ * event will not interrupt, nor even substantially slow down the processing of
+ * other events, but will be rather postponed to a lower priority event.
+ * @param {{call}} task A callable object, typically a function that takes no
+ * arguments.
+ */module.exports=asap;function asap(task){var rawTask;if(freeTasks.length){rawTask=freeTasks.pop();}else{rawTask=new RawTask();}rawTask.task=task;rawAsap(rawTask);}// We wrap tasks with recyclable task objects. A task object implements
+// `call`, just like a function.
+function RawTask(){this.task=null;}// The sole purpose of wrapping the task is to catch the exception and recycle
+// the task object after its single use.
+RawTask.prototype.call=function(){try{this.task.call();}catch(error){if(asap.onerror){// This hook exists purely for testing purposes.
+// Its name will be periodically randomized to break any code that
+// depends on its existence.
+asap.onerror(error);}else{// In a web browser, exceptions are not fatal. However, to avoid
+// slowing down the queue of pending tasks, we rethrow the error in a
+// lower priority turn.
+pendingErrors.push(error);requestErrorThrow();}}finally{this.task=null;freeTasks[freeTasks.length]=this;}};},{"./raw":56}],56:[function(require,module,exports){(function(global){(function(){"use strict";// Use the fastest means possible to execute a task in its own turn, with
+// priority over other events including IO, animation, reflow, and redraw
+// events in browsers.
+//
+// An exception thrown by a task will permanently interrupt the processing of
+// subsequent tasks. The higher level `asap` function ensures that if an
+// exception is thrown by a task, that the task queue will continue flushing as
+// soon as possible, but if you use `rawAsap` directly, you are responsible to
+// either ensure that no exceptions are thrown from your task, or to manually
+// call `rawAsap.requestFlush` if an exception is thrown.
+module.exports=rawAsap;function rawAsap(task){if(!queue.length){requestFlush();flushing=true;}// Equivalent to push, but avoids a function call.
+queue[queue.length]=task;}var queue=[];// Once a flush has been requested, no further calls to `requestFlush` are
+// necessary until the next `flush` completes.
+var flushing=false;// `requestFlush` is an implementation-specific method that attempts to kick
+// off a `flush` event as quickly as possible. `flush` will attempt to exhaust
+// the event queue before yielding to the browser's own event loop.
+var requestFlush;// The position of the next task to execute in the task queue. This is
+// preserved between calls to `flush` so that it can be resumed if
+// a task throws an exception.
+var index=0;// If a task schedules additional tasks recursively, the task queue can grow
+// unbounded. To prevent memory exhaustion, the task queue will periodically
+// truncate already-completed tasks.
+var capacity=1024;// The flush function processes all tasks that have been scheduled with
+// `rawAsap` unless and until one of those tasks throws an exception.
+// If a task throws an exception, `flush` ensures that its state will remain
+// consistent and will resume where it left off when called again.
+// However, `flush` does not make any arrangements to be called again if an
+// exception is thrown.
+function flush(){while(indexcapacity){// Manually shift all values starting at the index back to the
+// beginning of the queue.
+for(var scan=0,newLength=queue.length-index;scan-1){return callBind(intrinsic);}return intrinsic;};},{"./":59,"get-intrinsic":63}],59:[function(require,module,exports){'use strict';var bind=require('function-bind');var GetIntrinsic=require('get-intrinsic');var $apply=GetIntrinsic('%Function.prototype.apply%');var $call=GetIntrinsic('%Function.prototype.call%');var $reflectApply=GetIntrinsic('%Reflect.apply%',true)||bind.call($call,$apply);var $gOPD=GetIntrinsic('%Object.getOwnPropertyDescriptor%',true);var $defineProperty=GetIntrinsic('%Object.defineProperty%',true);var $max=GetIntrinsic('%Math.max%');if($defineProperty){try{$defineProperty({},'a',{value:1});}catch(e){// IE 8 has a broken defineProperty
+$defineProperty=null;}}module.exports=function callBind(originalFunction){var func=$reflectApply(bind,$call,arguments);if($gOPD&&$defineProperty){var desc=$gOPD(func,'length');if(desc.configurable){// original length, plus the receiver, minus any additional arguments (after the receiver)
+$defineProperty(func,'length',{value:1+$max(0,originalFunction.length-(arguments.length-1))});}}return func;};var applyBind=function applyBind(){return $reflectApply(bind,$apply,arguments);};if($defineProperty){$defineProperty(module.exports,'apply',{value:applyBind});}else{module.exports.apply=applyBind;}},{"function-bind":62,"get-intrinsic":63}],60:[function(require,module,exports){/*!
+ * eventie v1.0.6
+ * event binding helper
+ * eventie.bind( elem, 'click', myFn )
+ * eventie.unbind( elem, 'click', myFn )
+ * MIT license
+ */ /*jshint browser: true, undef: true, unused: true */ /*global define: false, module: false */(function(window){'use strict';var docElem=document.documentElement;var bind=function(){};function getIEEvent(obj){var event=window.event;// add event.target
+event.target=event.target||event.srcElement||obj;return event;}if(docElem.addEventListener){bind=function(obj,type,fn){obj.addEventListener(type,fn,false);};}else if(docElem.attachEvent){bind=function(obj,type,fn){obj[type+fn]=fn.handleEvent?function(){var event=getIEEvent(obj);fn.handleEvent.call(fn,event);}:function(){var event=getIEEvent(obj);fn.call(obj,event);};obj.attachEvent("on"+type,obj[type+fn]);};}var unbind=function(){};if(docElem.removeEventListener){unbind=function(obj,type,fn){obj.removeEventListener(type,fn,false);};}else if(docElem.detachEvent){unbind=function(obj,type,fn){obj.detachEvent("on"+type,obj[type+fn]);try{delete obj[type+fn];}catch(err){// can't delete window object properties
+obj[type+fn]=undefined;}};}var eventie={bind:bind,unbind:unbind};// ----- module definition ----- //
+if(typeof define==='function'&&define.amd){// AMD
+define(eventie);}else if(typeof exports==='object'){// CommonJS
+module.exports=eventie;}else{// browser global
+window.eventie=eventie;}})(window);},{}],61:[function(require,module,exports){'use strict';/* eslint no-invalid-this: 1 */var ERROR_MESSAGE='Function.prototype.bind called on incompatible ';var slice=Array.prototype.slice;var toStr=Object.prototype.toString;var funcType='[object Function]';module.exports=function bind(that){var target=this;if(typeof target!=='function'||toStr.call(target)!==funcType){throw new TypeError(ERROR_MESSAGE+target);}var args=slice.call(arguments,1);var bound;var binder=function(){if(this instanceof bound){var result=target.apply(this,args.concat(slice.call(arguments)));if(Object(result)===result){return result;}return this;}else{return target.apply(that,args.concat(slice.call(arguments)));}};var boundLength=Math.max(0,target.length-args.length);var boundArgs=[];for(var i=0;i1&&typeof allowMissing!=='boolean'){throw new $TypeError('"allowMissing" argument must be a boolean');}var parts=stringToPath(name);var intrinsicBaseName=parts.length>0?parts[0]:'';var intrinsic=getBaseIntrinsic('%'+intrinsicBaseName+'%',allowMissing);var intrinsicRealName=intrinsic.name;var value=intrinsic.value;var skipFurtherCaching=false;var alias=intrinsic.alias;if(alias){intrinsicBaseName=alias[0];$spliceApply(parts,$concat([0,1],alias));}for(var i=1,isOwn=true;i=parts.length){var desc=$gOPD(value,part);isOwn=!!desc;// By convention, when a data property is converted to an accessor
+// property to emulate a data property that does not suffer from
+// the override mistake, that accessor's getter is marked with
+// an `originalValue` property. Here, when we detect this, we
+// uphold the illusion by pretending to see that original data
+// property, i.e., returning the value rather than the getter
+// itself.
+if(isOwn&&'get'in desc&&!('originalValue'in desc.get)){value=desc.get;}else{value=value[part];}}else{isOwn=hasOwn(value,part);value=value[part];}if(isOwn&&!skipFurtherCaching){INTRINSICS[intrinsicRealName]=value;}}}return value;};},{"function-bind":62,"has":66,"has-symbols":64}],64:[function(require,module,exports){'use strict';var origSymbol=typeof Symbol!=='undefined'&&Symbol;var hasSymbolSham=require('./shams');module.exports=function hasNativeSymbols(){if(typeof origSymbol!=='function'){return false;}if(typeof Symbol!=='function'){return false;}if(typeof origSymbol('foo')!=='symbol'){return false;}if(typeof Symbol('bar')!=='symbol'){return false;}return hasSymbolSham();};},{"./shams":65}],65:[function(require,module,exports){'use strict';/* eslint complexity: [2, 18], max-statements: [2, 33] */module.exports=function hasSymbols(){if(typeof Symbol!=='function'||typeof Object.getOwnPropertySymbols!=='function'){return false;}if(typeof Symbol.iterator==='symbol'){return true;}var obj={};var sym=Symbol('test');var symObj=Object(sym);if(typeof sym==='string'){return false;}if(Object.prototype.toString.call(sym)!=='[object Symbol]'){return false;}if(Object.prototype.toString.call(symObj)!=='[object Symbol]'){return false;}// temp disabled per https://github.com/ljharb/object.assign/issues/17
+// if (sym instanceof Symbol) { return false; }
+// temp disabled per https://github.com/WebReflection/get-own-property-symbols/issues/4
+// if (!(symObj instanceof Symbol)) { return false; }
+// if (typeof Symbol.prototype.toString !== 'function') { return false; }
+// if (String(sym) !== Symbol.prototype.toString.call(sym)) { return false; }
+var symVal=42;obj[sym]=symVal;for(sym in obj){return false;}// eslint-disable-line no-restricted-syntax, no-unreachable-loop
+if(typeof Object.keys==='function'&&Object.keys(obj).length!==0){return false;}if(typeof Object.getOwnPropertyNames==='function'&&Object.getOwnPropertyNames(obj).length!==0){return false;}var syms=Object.getOwnPropertySymbols(obj);if(syms.length!==1||syms[0]!==sym){return false;}if(!Object.prototype.propertyIsEnumerable.call(obj,sym)){return false;}if(typeof Object.getOwnPropertyDescriptor==='function'){var descriptor=Object.getOwnPropertyDescriptor(obj,sym);if(descriptor.value!==symVal||descriptor.enumerable!==true){return false;}}return true;};},{}],66:[function(require,module,exports){'use strict';var bind=require('function-bind');module.exports=bind.call(Function.call,Object.prototype.hasOwnProperty);},{"function-bind":62}],67:[function(require,module,exports){/*!
+ * imagesLoaded v3.2.0
+ * JavaScript is all like "You images are done yet or what?"
+ * MIT License
+ */(function(window,factory){'use strict';// universal module definition
+/*global define: false, module: false, require: false */if(typeof define=='function'&&define.amd){// AMD
+define(['eventEmitter/EventEmitter','eventie/eventie'],function(EventEmitter,eventie){return factory(window,EventEmitter,eventie);});}else if(typeof module=='object'&&module.exports){// CommonJS
+module.exports=factory(window,require('wolfy87-eventemitter'),require('eventie'));}else{// browser global
+window.imagesLoaded=factory(window,window.EventEmitter,window.eventie);}})(window,// -------------------------- factory -------------------------- //
+function factory(window,EventEmitter,eventie){'use strict';var $=window.jQuery;var console=window.console;// -------------------------- helpers -------------------------- //
+// extend objects
+function extend(a,b){for(var prop in b){a[prop]=b[prop];}return a;}var objToString=Object.prototype.toString;function isArray(obj){return objToString.call(obj)=='[object Array]';}// turn element or nodeList into an array
+function makeArray(obj){var ary=[];if(isArray(obj)){// use object if already an array
+ary=obj;}else if(typeof obj.length=='number'){// convert nodeList to array
+for(var i=0;i
+ * Copyright OpenJS Foundation and other contributors
+ * Released under MIT license
+ * Based on Underscore.js 1.8.3
+ * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+ */;(function(){/** Used as a safe reference for `undefined` in pre-ES5 environments. */var undefined;/** Used as the semantic version number. */var VERSION='4.17.21';/** Used as the size to enable large array optimizations. */var LARGE_ARRAY_SIZE=200;/** Error message constants. */var CORE_ERROR_TEXT='Unsupported core-js use. Try https://npms.io/search?q=ponyfill.',FUNC_ERROR_TEXT='Expected a function',INVALID_TEMPL_VAR_ERROR_TEXT='Invalid `variable` option passed into `_.template`';/** Used to stand-in for `undefined` hash values. */var HASH_UNDEFINED='__lodash_hash_undefined__';/** Used as the maximum memoize cache size. */var MAX_MEMOIZE_SIZE=500;/** Used as the internal argument placeholder. */var PLACEHOLDER='__lodash_placeholder__';/** Used to compose bitmasks for cloning. */var CLONE_DEEP_FLAG=1,CLONE_FLAT_FLAG=2,CLONE_SYMBOLS_FLAG=4;/** Used to compose bitmasks for value comparisons. */var COMPARE_PARTIAL_FLAG=1,COMPARE_UNORDERED_FLAG=2;/** Used to compose bitmasks for function metadata. */var WRAP_BIND_FLAG=1,WRAP_BIND_KEY_FLAG=2,WRAP_CURRY_BOUND_FLAG=4,WRAP_CURRY_FLAG=8,WRAP_CURRY_RIGHT_FLAG=16,WRAP_PARTIAL_FLAG=32,WRAP_PARTIAL_RIGHT_FLAG=64,WRAP_ARY_FLAG=128,WRAP_REARG_FLAG=256,WRAP_FLIP_FLAG=512;/** Used as default options for `_.truncate`. */var DEFAULT_TRUNC_LENGTH=30,DEFAULT_TRUNC_OMISSION='...';/** Used to detect hot functions by number of calls within a span of milliseconds. */var HOT_COUNT=800,HOT_SPAN=16;/** Used to indicate the type of lazy iteratees. */var LAZY_FILTER_FLAG=1,LAZY_MAP_FLAG=2,LAZY_WHILE_FLAG=3;/** Used as references for various `Number` constants. */var INFINITY=1/0,MAX_SAFE_INTEGER=9007199254740991,MAX_INTEGER=1.7976931348623157e+308,NAN=0/0;/** Used as references for the maximum length and index of an array. */var MAX_ARRAY_LENGTH=4294967295,MAX_ARRAY_INDEX=MAX_ARRAY_LENGTH-1,HALF_MAX_ARRAY_LENGTH=MAX_ARRAY_LENGTH>>>1;/** Used to associate wrap methods with their bit flags. */var wrapFlags=[['ary',WRAP_ARY_FLAG],['bind',WRAP_BIND_FLAG],['bindKey',WRAP_BIND_KEY_FLAG],['curry',WRAP_CURRY_FLAG],['curryRight',WRAP_CURRY_RIGHT_FLAG],['flip',WRAP_FLIP_FLAG],['partial',WRAP_PARTIAL_FLAG],['partialRight',WRAP_PARTIAL_RIGHT_FLAG],['rearg',WRAP_REARG_FLAG]];/** `Object#toString` result references. */var argsTag='[object Arguments]',arrayTag='[object Array]',asyncTag='[object AsyncFunction]',boolTag='[object Boolean]',dateTag='[object Date]',domExcTag='[object DOMException]',errorTag='[object Error]',funcTag='[object Function]',genTag='[object GeneratorFunction]',mapTag='[object Map]',numberTag='[object Number]',nullTag='[object Null]',objectTag='[object Object]',promiseTag='[object Promise]',proxyTag='[object Proxy]',regexpTag='[object RegExp]',setTag='[object Set]',stringTag='[object String]',symbolTag='[object Symbol]',undefinedTag='[object Undefined]',weakMapTag='[object WeakMap]',weakSetTag='[object WeakSet]';var arrayBufferTag='[object ArrayBuffer]',dataViewTag='[object DataView]',float32Tag='[object Float32Array]',float64Tag='[object Float64Array]',int8Tag='[object Int8Array]',int16Tag='[object Int16Array]',int32Tag='[object Int32Array]',uint8Tag='[object Uint8Array]',uint8ClampedTag='[object Uint8ClampedArray]',uint16Tag='[object Uint16Array]',uint32Tag='[object Uint32Array]';/** Used to match empty string literals in compiled template source. */var reEmptyStringLeading=/\b__p \+= '';/g,reEmptyStringMiddle=/\b(__p \+=) '' \+/g,reEmptyStringTrailing=/(__e\(.*?\)|\b__t\)) \+\n'';/g;/** Used to match HTML entities and HTML characters. */var reEscapedHtml=/&(?:amp|lt|gt|quot|#39);/g,reUnescapedHtml=/[&<>"']/g,reHasEscapedHtml=RegExp(reEscapedHtml.source),reHasUnescapedHtml=RegExp(reUnescapedHtml.source);/** Used to match template delimiters. */var reEscape=/<%-([\s\S]+?)%>/g,reEvaluate=/<%([\s\S]+?)%>/g,reInterpolate=/<%=([\s\S]+?)%>/g;/** Used to match property names within property paths. */var reIsDeepProp=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,reIsPlainProp=/^\w*$/,rePropName=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;/**
+ * Used to match `RegExp`
+ * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
+ */var reRegExpChar=/[\\^$.*+?()[\]{}|]/g,reHasRegExpChar=RegExp(reRegExpChar.source);/** Used to match leading whitespace. */var reTrimStart=/^\s+/;/** Used to match a single whitespace character. */var reWhitespace=/\s/;/** Used to match wrap detail comments. */var reWrapComment=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,reWrapDetails=/\{\n\/\* \[wrapped with (.+)\] \*/,reSplitDetails=/,? & /;/** Used to match words composed of alphanumeric characters. */var reAsciiWord=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;/**
+ * Used to validate the `validate` option in `_.template` variable.
+ *
+ * Forbids characters which could potentially change the meaning of the function argument definition:
+ * - "()," (modification of function parameters)
+ * - "=" (default value)
+ * - "[]{}" (destructuring of function parameters)
+ * - "/" (beginning of a comment)
+ * - whitespace
+ */var reForbiddenIdentifierChars=/[()=,{}\[\]\/\s]/;/** Used to match backslashes in property paths. */var reEscapeChar=/\\(\\)?/g;/**
+ * Used to match
+ * [ES template delimiters](http://ecma-international.org/ecma-262/7.0/#sec-template-literal-lexical-components).
+ */var reEsTemplate=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;/** Used to match `RegExp` flags from their coerced string values. */var reFlags=/\w*$/;/** Used to detect bad signed hexadecimal string values. */var reIsBadHex=/^[-+]0x[0-9a-f]+$/i;/** Used to detect binary string values. */var reIsBinary=/^0b[01]+$/i;/** Used to detect host constructors (Safari). */var reIsHostCtor=/^\[object .+?Constructor\]$/;/** Used to detect octal string values. */var reIsOctal=/^0o[0-7]+$/i;/** Used to detect unsigned integer values. */var reIsUint=/^(?:0|[1-9]\d*)$/;/** Used to match Latin Unicode letters (excluding mathematical operators). */var reLatin=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g;/** Used to ensure capturing order of template delimiters. */var reNoMatch=/($^)/;/** Used to match unescaped characters in compiled string literals. */var reUnescapedString=/['\n\r\u2028\u2029\\]/g;/** Used to compose unicode character classes. */var rsAstralRange='\\ud800-\\udfff',rsComboMarksRange='\\u0300-\\u036f',reComboHalfMarksRange='\\ufe20-\\ufe2f',rsComboSymbolsRange='\\u20d0-\\u20ff',rsComboRange=rsComboMarksRange+reComboHalfMarksRange+rsComboSymbolsRange,rsDingbatRange='\\u2700-\\u27bf',rsLowerRange='a-z\\xdf-\\xf6\\xf8-\\xff',rsMathOpRange='\\xac\\xb1\\xd7\\xf7',rsNonCharRange='\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf',rsPunctuationRange='\\u2000-\\u206f',rsSpaceRange=' \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000',rsUpperRange='A-Z\\xc0-\\xd6\\xd8-\\xde',rsVarRange='\\ufe0e\\ufe0f',rsBreakRange=rsMathOpRange+rsNonCharRange+rsPunctuationRange+rsSpaceRange;/** Used to compose unicode capture groups. */var rsApos="['\u2019]",rsAstral='['+rsAstralRange+']',rsBreak='['+rsBreakRange+']',rsCombo='['+rsComboRange+']',rsDigits='\\d+',rsDingbat='['+rsDingbatRange+']',rsLower='['+rsLowerRange+']',rsMisc='[^'+rsAstralRange+rsBreakRange+rsDigits+rsDingbatRange+rsLowerRange+rsUpperRange+']',rsFitz='\\ud83c[\\udffb-\\udfff]',rsModifier='(?:'+rsCombo+'|'+rsFitz+')',rsNonAstral='[^'+rsAstralRange+']',rsRegional='(?:\\ud83c[\\udde6-\\uddff]){2}',rsSurrPair='[\\ud800-\\udbff][\\udc00-\\udfff]',rsUpper='['+rsUpperRange+']',rsZWJ='\\u200d';/** Used to compose unicode regexes. */var rsMiscLower='(?:'+rsLower+'|'+rsMisc+')',rsMiscUpper='(?:'+rsUpper+'|'+rsMisc+')',rsOptContrLower='(?:'+rsApos+'(?:d|ll|m|re|s|t|ve))?',rsOptContrUpper='(?:'+rsApos+'(?:D|LL|M|RE|S|T|VE))?',reOptMod=rsModifier+'?',rsOptVar='['+rsVarRange+']?',rsOptJoin='(?:'+rsZWJ+'(?:'+[rsNonAstral,rsRegional,rsSurrPair].join('|')+')'+rsOptVar+reOptMod+')*',rsOrdLower='\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])',rsOrdUpper='\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])',rsSeq=rsOptVar+reOptMod+rsOptJoin,rsEmoji='(?:'+[rsDingbat,rsRegional,rsSurrPair].join('|')+')'+rsSeq,rsSymbol='(?:'+[rsNonAstral+rsCombo+'?',rsCombo,rsRegional,rsSurrPair,rsAstral].join('|')+')';/** Used to match apostrophes. */var reApos=RegExp(rsApos,'g');/**
+ * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and
+ * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols).
+ */var reComboMark=RegExp(rsCombo,'g');/** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */var reUnicode=RegExp(rsFitz+'(?='+rsFitz+')|'+rsSymbol+rsSeq,'g');/** Used to match complex or compound words. */var reUnicodeWord=RegExp([rsUpper+'?'+rsLower+'+'+rsOptContrLower+'(?='+[rsBreak,rsUpper,'$'].join('|')+')',rsMiscUpper+'+'+rsOptContrUpper+'(?='+[rsBreak,rsUpper+rsMiscLower,'$'].join('|')+')',rsUpper+'?'+rsMiscLower+'+'+rsOptContrLower,rsUpper+'+'+rsOptContrUpper,rsOrdUpper,rsOrdLower,rsDigits,rsEmoji].join('|'),'g');/** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */var reHasUnicode=RegExp('['+rsZWJ+rsAstralRange+rsComboRange+rsVarRange+']');/** Used to detect strings that need a more robust regexp to match words. */var reHasUnicodeWord=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/;/** Used to assign default `context` object properties. */var contextProps=['Array','Buffer','DataView','Date','Error','Float32Array','Float64Array','Function','Int8Array','Int16Array','Int32Array','Map','Math','Object','Promise','RegExp','Set','String','Symbol','TypeError','Uint8Array','Uint8ClampedArray','Uint16Array','Uint32Array','WeakMap','_','clearTimeout','isFinite','parseInt','setTimeout'];/** Used to make template sourceURLs easier to identify. */var templateCounter=-1;/** Used to identify `toStringTag` values of typed arrays. */var typedArrayTags={};typedArrayTags[float32Tag]=typedArrayTags[float64Tag]=typedArrayTags[int8Tag]=typedArrayTags[int16Tag]=typedArrayTags[int32Tag]=typedArrayTags[uint8Tag]=typedArrayTags[uint8ClampedTag]=typedArrayTags[uint16Tag]=typedArrayTags[uint32Tag]=true;typedArrayTags[argsTag]=typedArrayTags[arrayTag]=typedArrayTags[arrayBufferTag]=typedArrayTags[boolTag]=typedArrayTags[dataViewTag]=typedArrayTags[dateTag]=typedArrayTags[errorTag]=typedArrayTags[funcTag]=typedArrayTags[mapTag]=typedArrayTags[numberTag]=typedArrayTags[objectTag]=typedArrayTags[regexpTag]=typedArrayTags[setTag]=typedArrayTags[stringTag]=typedArrayTags[weakMapTag]=false;/** Used to identify `toStringTag` values supported by `_.clone`. */var cloneableTags={};cloneableTags[argsTag]=cloneableTags[arrayTag]=cloneableTags[arrayBufferTag]=cloneableTags[dataViewTag]=cloneableTags[boolTag]=cloneableTags[dateTag]=cloneableTags[float32Tag]=cloneableTags[float64Tag]=cloneableTags[int8Tag]=cloneableTags[int16Tag]=cloneableTags[int32Tag]=cloneableTags[mapTag]=cloneableTags[numberTag]=cloneableTags[objectTag]=cloneableTags[regexpTag]=cloneableTags[setTag]=cloneableTags[stringTag]=cloneableTags[symbolTag]=cloneableTags[uint8Tag]=cloneableTags[uint8ClampedTag]=cloneableTags[uint16Tag]=cloneableTags[uint32Tag]=true;cloneableTags[errorTag]=cloneableTags[funcTag]=cloneableTags[weakMapTag]=false;/** Used to map Latin Unicode letters to basic Latin letters. */var deburredLetters={// Latin-1 Supplement block.
+'\xc0':'A','\xc1':'A','\xc2':'A','\xc3':'A','\xc4':'A','\xc5':'A','\xe0':'a','\xe1':'a','\xe2':'a','\xe3':'a','\xe4':'a','\xe5':'a','\xc7':'C','\xe7':'c','\xd0':'D','\xf0':'d','\xc8':'E','\xc9':'E','\xca':'E','\xcb':'E','\xe8':'e','\xe9':'e','\xea':'e','\xeb':'e','\xcc':'I','\xcd':'I','\xce':'I','\xcf':'I','\xec':'i','\xed':'i','\xee':'i','\xef':'i','\xd1':'N','\xf1':'n','\xd2':'O','\xd3':'O','\xd4':'O','\xd5':'O','\xd6':'O','\xd8':'O','\xf2':'o','\xf3':'o','\xf4':'o','\xf5':'o','\xf6':'o','\xf8':'o','\xd9':'U','\xda':'U','\xdb':'U','\xdc':'U','\xf9':'u','\xfa':'u','\xfb':'u','\xfc':'u','\xdd':'Y','\xfd':'y','\xff':'y','\xc6':'Ae','\xe6':'ae','\xde':'Th','\xfe':'th','\xdf':'ss',// Latin Extended-A block.
+'\u0100':'A','\u0102':'A','\u0104':'A','\u0101':'a','\u0103':'a','\u0105':'a','\u0106':'C','\u0108':'C','\u010a':'C','\u010c':'C','\u0107':'c','\u0109':'c','\u010b':'c','\u010d':'c','\u010e':'D','\u0110':'D','\u010f':'d','\u0111':'d','\u0112':'E','\u0114':'E','\u0116':'E','\u0118':'E','\u011a':'E','\u0113':'e','\u0115':'e','\u0117':'e','\u0119':'e','\u011b':'e','\u011c':'G','\u011e':'G','\u0120':'G','\u0122':'G','\u011d':'g','\u011f':'g','\u0121':'g','\u0123':'g','\u0124':'H','\u0126':'H','\u0125':'h','\u0127':'h','\u0128':'I','\u012a':'I','\u012c':'I','\u012e':'I','\u0130':'I','\u0129':'i','\u012b':'i','\u012d':'i','\u012f':'i','\u0131':'i','\u0134':'J','\u0135':'j','\u0136':'K','\u0137':'k','\u0138':'k','\u0139':'L','\u013b':'L','\u013d':'L','\u013f':'L','\u0141':'L','\u013a':'l','\u013c':'l','\u013e':'l','\u0140':'l','\u0142':'l','\u0143':'N','\u0145':'N','\u0147':'N','\u014a':'N','\u0144':'n','\u0146':'n','\u0148':'n','\u014b':'n','\u014c':'O','\u014e':'O','\u0150':'O','\u014d':'o','\u014f':'o','\u0151':'o','\u0154':'R','\u0156':'R','\u0158':'R','\u0155':'r','\u0157':'r','\u0159':'r','\u015a':'S','\u015c':'S','\u015e':'S','\u0160':'S','\u015b':'s','\u015d':'s','\u015f':'s','\u0161':'s','\u0162':'T','\u0164':'T','\u0166':'T','\u0163':'t','\u0165':'t','\u0167':'t','\u0168':'U','\u016a':'U','\u016c':'U','\u016e':'U','\u0170':'U','\u0172':'U','\u0169':'u','\u016b':'u','\u016d':'u','\u016f':'u','\u0171':'u','\u0173':'u','\u0174':'W','\u0175':'w','\u0176':'Y','\u0177':'y','\u0178':'Y','\u0179':'Z','\u017b':'Z','\u017d':'Z','\u017a':'z','\u017c':'z','\u017e':'z','\u0132':'IJ','\u0133':'ij','\u0152':'Oe','\u0153':'oe','\u0149':"'n",'\u017f':'s'};/** Used to map characters to HTML entities. */var htmlEscapes={'&':'&','<':'<','>':'>','"':'"',"'":'''};/** Used to map HTML entities to characters. */var htmlUnescapes={'&':'&','<':'<','>':'>','"':'"',''':"'"};/** Used to escape characters for inclusion in compiled string literals. */var stringEscapes={'\\':'\\',"'":"'",'\n':'n','\r':'r','\u2028':'u2028','\u2029':'u2029'};/** Built-in method references without a dependency on `root`. */var freeParseFloat=parseFloat,freeParseInt=parseInt;/** Detect free variable `global` from Node.js. */var freeGlobal=typeof global=='object'&&global&&global.Object===Object&&global;/** Detect free variable `self`. */var freeSelf=typeof self=='object'&&self&&self.Object===Object&&self;/** Used as a reference to the global object. */var root=freeGlobal||freeSelf||Function('return this')();/** Detect free variable `exports`. */var freeExports=typeof exports=='object'&&exports&&!exports.nodeType&&exports;/** Detect free variable `module`. */var freeModule=freeExports&&typeof module=='object'&&module&&!module.nodeType&&module;/** Detect the popular CommonJS extension `module.exports`. */var moduleExports=freeModule&&freeModule.exports===freeExports;/** Detect free variable `process` from Node.js. */var freeProcess=moduleExports&&freeGlobal.process;/** Used to access faster Node.js helpers. */var nodeUtil=function(){try{// Use `util.types` for Node.js 10+.
+var types=freeModule&&freeModule.require&&freeModule.require('util').types;if(types){return types;}// Legacy `process.binding('util')` for Node.js < 10.
+return freeProcess&&freeProcess.binding&&freeProcess.binding('util');}catch(e){}}();/* Node.js helper references. */var nodeIsArrayBuffer=nodeUtil&&nodeUtil.isArrayBuffer,nodeIsDate=nodeUtil&&nodeUtil.isDate,nodeIsMap=nodeUtil&&nodeUtil.isMap,nodeIsRegExp=nodeUtil&&nodeUtil.isRegExp,nodeIsSet=nodeUtil&&nodeUtil.isSet,nodeIsTypedArray=nodeUtil&&nodeUtil.isTypedArray;/*--------------------------------------------------------------------------*/ /**
+ * A faster alternative to `Function#apply`, this function invokes `func`
+ * with the `this` binding of `thisArg` and the arguments of `args`.
+ *
+ * @private
+ * @param {Function} func The function to invoke.
+ * @param {*} thisArg The `this` binding of `func`.
+ * @param {Array} args The arguments to invoke `func` with.
+ * @returns {*} Returns the result of `func`.
+ */function apply(func,thisArg,args){switch(args.length){case 0:return func.call(thisArg);case 1:return func.call(thisArg,args[0]);case 2:return func.call(thisArg,args[0],args[1]);case 3:return func.call(thisArg,args[0],args[1],args[2]);}return func.apply(thisArg,args);}/**
+ * A specialized version of `baseAggregator` for arrays.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} setter The function to set `accumulator` values.
+ * @param {Function} iteratee The iteratee to transform keys.
+ * @param {Object} accumulator The initial aggregated object.
+ * @returns {Function} Returns `accumulator`.
+ */function arrayAggregator(array,setter,iteratee,accumulator){var index=-1,length=array==null?0:array.length;while(++index-1;}/**
+ * This function is like `arrayIncludes` except that it accepts a comparator.
+ *
+ * @private
+ * @param {Array} [array] The array to inspect.
+ * @param {*} target The value to search for.
+ * @param {Function} comparator The comparator invoked per element.
+ * @returns {boolean} Returns `true` if `target` is found, else `false`.
+ */function arrayIncludesWith(array,value,comparator){var index=-1,length=array==null?0:array.length;while(++index-1){}return index;}/**
+ * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol
+ * that is not found in the character symbols.
+ *
+ * @private
+ * @param {Array} strSymbols The string symbols to inspect.
+ * @param {Array} chrSymbols The character symbols to find.
+ * @returns {number} Returns the index of the last unmatched string symbol.
+ */function charsEndIndex(strSymbols,chrSymbols){var index=strSymbols.length;while(index--&&baseIndexOf(chrSymbols,strSymbols[index],0)>-1){}return index;}/**
+ * Gets the number of `placeholder` occurrences in `array`.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {*} placeholder The placeholder to search for.
+ * @returns {number} Returns the placeholder count.
+ */function countHolders(array,placeholder){var length=array.length,result=0;while(length--){if(array[length]===placeholder){++result;}}return result;}/**
+ * Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A
+ * letters to basic Latin letters.
+ *
+ * @private
+ * @param {string} letter The matched letter to deburr.
+ * @returns {string} Returns the deburred letter.
+ */var deburrLetter=basePropertyOf(deburredLetters);/**
+ * Used by `_.escape` to convert characters to HTML entities.
+ *
+ * @private
+ * @param {string} chr The matched character to escape.
+ * @returns {string} Returns the escaped character.
+ */var escapeHtmlChar=basePropertyOf(htmlEscapes);/**
+ * Used by `_.template` to escape characters for inclusion in compiled string literals.
+ *
+ * @private
+ * @param {string} chr The matched character to escape.
+ * @returns {string} Returns the escaped character.
+ */function escapeStringChar(chr){return'\\'+stringEscapes[chr];}/**
+ * Gets the value at `key` of `object`.
+ *
+ * @private
+ * @param {Object} [object] The object to query.
+ * @param {string} key The key of the property to get.
+ * @returns {*} Returns the property value.
+ */function getValue(object,key){return object==null?undefined:object[key];}/**
+ * Checks if `string` contains Unicode symbols.
+ *
+ * @private
+ * @param {string} string The string to inspect.
+ * @returns {boolean} Returns `true` if a symbol is found, else `false`.
+ */function hasUnicode(string){return reHasUnicode.test(string);}/**
+ * Checks if `string` contains a word composed of Unicode symbols.
+ *
+ * @private
+ * @param {string} string The string to inspect.
+ * @returns {boolean} Returns `true` if a word is found, else `false`.
+ */function hasUnicodeWord(string){return reHasUnicodeWord.test(string);}/**
+ * Converts `iterator` to an array.
+ *
+ * @private
+ * @param {Object} iterator The iterator to convert.
+ * @returns {Array} Returns the converted array.
+ */function iteratorToArray(iterator){var data,result=[];while(!(data=iterator.next()).done){result.push(data.value);}return result;}/**
+ * Converts `map` to its key-value pairs.
+ *
+ * @private
+ * @param {Object} map The map to convert.
+ * @returns {Array} Returns the key-value pairs.
+ */function mapToArray(map){var index=-1,result=Array(map.size);map.forEach(function(value,key){result[++index]=[key,value];});return result;}/**
+ * Creates a unary function that invokes `func` with its argument transformed.
+ *
+ * @private
+ * @param {Function} func The function to wrap.
+ * @param {Function} transform The argument transform.
+ * @returns {Function} Returns the new function.
+ */function overArg(func,transform){return function(arg){return func(transform(arg));};}/**
+ * Replaces all `placeholder` elements in `array` with an internal placeholder
+ * and returns an array of their indexes.
+ *
+ * @private
+ * @param {Array} array The array to modify.
+ * @param {*} placeholder The placeholder to replace.
+ * @returns {Array} Returns the new array of placeholder indexes.
+ */function replaceHolders(array,placeholder){var index=-1,length=array.length,resIndex=0,result=[];while(++index true
+ * _.isFunction(_.bar);
+ * // => false
+ *
+ * lodash.isFunction(lodash.foo);
+ * // => false
+ * lodash.isFunction(lodash.bar);
+ * // => true
+ *
+ * // Create a suped-up `defer` in Node.js.
+ * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer;
+ */var runInContext=function runInContext(context){context=context==null?root:_.defaults(root.Object(),context,_.pick(root,contextProps));/** Built-in constructor references. */var Array=context.Array,Date=context.Date,Error=context.Error,Function=context.Function,Math=context.Math,Object=context.Object,RegExp=context.RegExp,String=context.String,TypeError=context.TypeError;/** Used for built-in method references. */var arrayProto=Array.prototype,funcProto=Function.prototype,objectProto=Object.prototype;/** Used to detect overreaching core-js shims. */var coreJsData=context['__core-js_shared__'];/** Used to resolve the decompiled source of functions. */var funcToString=funcProto.toString;/** Used to check objects for own properties. */var hasOwnProperty=objectProto.hasOwnProperty;/** Used to generate unique IDs. */var idCounter=0;/** Used to detect methods masquerading as native. */var maskSrcKey=function(){var uid=/[^.]+$/.exec(coreJsData&&coreJsData.keys&&coreJsData.keys.IE_PROTO||'');return uid?'Symbol(src)_1.'+uid:'';}();/**
+ * Used to resolve the
+ * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
+ * of values.
+ */var nativeObjectToString=objectProto.toString;/** Used to infer the `Object` constructor. */var objectCtorString=funcToString.call(Object);/** Used to restore the original `_` reference in `_.noConflict`. */var oldDash=root._;/** Used to detect if a method is native. */var reIsNative=RegExp('^'+funcToString.call(hasOwnProperty).replace(reRegExpChar,'\\$&').replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,'$1.*?')+'$');/** Built-in value references. */var Buffer=moduleExports?context.Buffer:undefined,Symbol=context.Symbol,Uint8Array=context.Uint8Array,allocUnsafe=Buffer?Buffer.allocUnsafe:undefined,getPrototype=overArg(Object.getPrototypeOf,Object),objectCreate=Object.create,propertyIsEnumerable=objectProto.propertyIsEnumerable,splice=arrayProto.splice,spreadableSymbol=Symbol?Symbol.isConcatSpreadable:undefined,symIterator=Symbol?Symbol.iterator:undefined,symToStringTag=Symbol?Symbol.toStringTag:undefined;var defineProperty=function(){try{var func=getNative(Object,'defineProperty');func({},'',{});return func;}catch(e){}}();/** Mocked built-ins. */var ctxClearTimeout=context.clearTimeout!==root.clearTimeout&&context.clearTimeout,ctxNow=Date&&Date.now!==root.Date.now&&Date.now,ctxSetTimeout=context.setTimeout!==root.setTimeout&&context.setTimeout;/* Built-in method references for those with the same name as other `lodash` methods. */var nativeCeil=Math.ceil,nativeFloor=Math.floor,nativeGetSymbols=Object.getOwnPropertySymbols,nativeIsBuffer=Buffer?Buffer.isBuffer:undefined,nativeIsFinite=context.isFinite,nativeJoin=arrayProto.join,nativeKeys=overArg(Object.keys,Object),nativeMax=Math.max,nativeMin=Math.min,nativeNow=Date.now,nativeParseInt=context.parseInt,nativeRandom=Math.random,nativeReverse=arrayProto.reverse;/* Built-in method references that are verified to be native. */var DataView=getNative(context,'DataView'),Map=getNative(context,'Map'),Promise=getNative(context,'Promise'),Set=getNative(context,'Set'),WeakMap=getNative(context,'WeakMap'),nativeCreate=getNative(Object,'create');/** Used to store function metadata. */var metaMap=WeakMap&&new WeakMap();/** Used to lookup unminified function names. */var realNames={};/** Used to detect maps, sets, and weakmaps. */var dataViewCtorString=toSource(DataView),mapCtorString=toSource(Map),promiseCtorString=toSource(Promise),setCtorString=toSource(Set),weakMapCtorString=toSource(WeakMap);/** Used to convert symbols to primitives and strings. */var symbolProto=Symbol?Symbol.prototype:undefined,symbolValueOf=symbolProto?symbolProto.valueOf:undefined,symbolToString=symbolProto?symbolProto.toString:undefined;/*------------------------------------------------------------------------*/ /**
+ * Creates a `lodash` object which wraps `value` to enable implicit method
+ * chain sequences. Methods that operate on and return arrays, collections,
+ * and functions can be chained together. Methods that retrieve a single value
+ * or may return a primitive value will automatically end the chain sequence
+ * and return the unwrapped value. Otherwise, the value must be unwrapped
+ * with `_#value`.
+ *
+ * Explicit chain sequences, which must be unwrapped with `_#value`, may be
+ * enabled using `_.chain`.
+ *
+ * The execution of chained methods is lazy, that is, it's deferred until
+ * `_#value` is implicitly or explicitly called.
+ *
+ * Lazy evaluation allows several methods to support shortcut fusion.
+ * Shortcut fusion is an optimization to merge iteratee calls; this avoids
+ * the creation of intermediate arrays and can greatly reduce the number of
+ * iteratee executions. Sections of a chain sequence qualify for shortcut
+ * fusion if the section is applied to an array and iteratees accept only
+ * one argument. The heuristic for whether a section qualifies for shortcut
+ * fusion is subject to change.
+ *
+ * Chaining is supported in custom builds as long as the `_#value` method is
+ * directly or indirectly included in the build.
+ *
+ * In addition to lodash methods, wrappers have `Array` and `String` methods.
+ *
+ * The wrapper `Array` methods are:
+ * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift`
+ *
+ * The wrapper `String` methods are:
+ * `replace` and `split`
+ *
+ * The wrapper methods that support shortcut fusion are:
+ * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`,
+ * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`,
+ * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray`
+ *
+ * The chainable wrapper methods are:
+ * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`,
+ * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`,
+ * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`,
+ * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`,
+ * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`,
+ * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`,
+ * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`,
+ * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`,
+ * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`,
+ * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`,
+ * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,
+ * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`,
+ * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`,
+ * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`,
+ * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`,
+ * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`,
+ * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`,
+ * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`,
+ * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`,
+ * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`,
+ * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`,
+ * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`,
+ * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`,
+ * `zipObject`, `zipObjectDeep`, and `zipWith`
+ *
+ * The wrapper methods that are **not** chainable by default are:
+ * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`,
+ * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `conformsTo`, `deburr`,
+ * `defaultTo`, `divide`, `each`, `eachRight`, `endsWith`, `eq`, `escape`,
+ * `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, `findLast`,
+ * `findLastIndex`, `findLastKey`, `first`, `floor`, `forEach`, `forEachRight`,
+ * `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `get`, `gt`, `gte`, `has`,
+ * `hasIn`, `head`, `identity`, `includes`, `indexOf`, `inRange`, `invoke`,
+ * `isArguments`, `isArray`, `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`,
+ * `isBoolean`, `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`,
+ * `isEqualWith`, `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`,
+ * `isMap`, `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`,
+ * `isNumber`, `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`,
+ * `isSafeInteger`, `isSet`, `isString`, `isUndefined`, `isTypedArray`,
+ * `isWeakMap`, `isWeakSet`, `join`, `kebabCase`, `last`, `lastIndexOf`,
+ * `lowerCase`, `lowerFirst`, `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`,
+ * `min`, `minBy`, `multiply`, `noConflict`, `noop`, `now`, `nth`, `pad`,
+ * `padEnd`, `padStart`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`,
+ * `repeat`, `result`, `round`, `runInContext`, `sample`, `shift`, `size`,
+ * `snakeCase`, `some`, `sortedIndex`, `sortedIndexBy`, `sortedLastIndex`,
+ * `sortedLastIndexBy`, `startCase`, `startsWith`, `stubArray`, `stubFalse`,
+ * `stubObject`, `stubString`, `stubTrue`, `subtract`, `sum`, `sumBy`,
+ * `template`, `times`, `toFinite`, `toInteger`, `toJSON`, `toLength`,
+ * `toLower`, `toNumber`, `toSafeInteger`, `toString`, `toUpper`, `trim`,
+ * `trimEnd`, `trimStart`, `truncate`, `unescape`, `uniqueId`, `upperCase`,
+ * `upperFirst`, `value`, and `words`
+ *
+ * @name _
+ * @constructor
+ * @category Seq
+ * @param {*} value The value to wrap in a `lodash` instance.
+ * @returns {Object} Returns the new `lodash` wrapper instance.
+ * @example
+ *
+ * function square(n) {
+ * return n * n;
+ * }
+ *
+ * var wrapped = _([1, 2, 3]);
+ *
+ * // Returns an unwrapped value.
+ * wrapped.reduce(_.add);
+ * // => 6
+ *
+ * // Returns a wrapped value.
+ * var squares = wrapped.map(square);
+ *
+ * _.isArray(squares);
+ * // => false
+ *
+ * _.isArray(squares.value());
+ * // => true
+ */function lodash(value){if(isObjectLike(value)&&!isArray(value)&&!(value instanceof LazyWrapper)){if(value instanceof LodashWrapper){return value;}if(hasOwnProperty.call(value,'__wrapped__')){return wrapperClone(value);}}return new LodashWrapper(value);}/**
+ * The base implementation of `_.create` without support for assigning
+ * properties to the created object.
+ *
+ * @private
+ * @param {Object} proto The object to inherit from.
+ * @returns {Object} Returns the new object.
+ */var baseCreate=function(){function object(){}return function(proto){if(!isObject(proto)){return{};}if(objectCreate){return objectCreate(proto);}object.prototype=proto;var result=new object();object.prototype=undefined;return result;};}();/**
+ * The function whose prototype chain sequence wrappers inherit from.
+ *
+ * @private
+ */function baseLodash(){// No operation performed.
+}/**
+ * The base constructor for creating `lodash` wrapper objects.
+ *
+ * @private
+ * @param {*} value The value to wrap.
+ * @param {boolean} [chainAll] Enable explicit method chain sequences.
+ */function LodashWrapper(value,chainAll){this.__wrapped__=value;this.__actions__=[];this.__chain__=!!chainAll;this.__index__=0;this.__values__=undefined;}/**
+ * By default, the template delimiters used by lodash are like those in
+ * embedded Ruby (ERB) as well as ES2015 template strings. Change the
+ * following template settings to use alternative delimiters.
+ *
+ * @static
+ * @memberOf _
+ * @type {Object}
+ */lodash.templateSettings={/**
+ * Used to detect `data` property values to be HTML-escaped.
+ *
+ * @memberOf _.templateSettings
+ * @type {RegExp}
+ */'escape':reEscape,/**
+ * Used to detect code to be evaluated.
+ *
+ * @memberOf _.templateSettings
+ * @type {RegExp}
+ */'evaluate':reEvaluate,/**
+ * Used to detect `data` property values to inject.
+ *
+ * @memberOf _.templateSettings
+ * @type {RegExp}
+ */'interpolate':reInterpolate,/**
+ * Used to reference the data object in the template text.
+ *
+ * @memberOf _.templateSettings
+ * @type {string}
+ */'variable':'',/**
+ * Used to import variables into the compiled template.
+ *
+ * @memberOf _.templateSettings
+ * @type {Object}
+ */'imports':{/**
+ * A reference to the `lodash` function.
+ *
+ * @memberOf _.templateSettings.imports
+ * @type {Function}
+ */'_':lodash}};// Ensure wrappers are instances of `baseLodash`.
+lodash.prototype=baseLodash.prototype;lodash.prototype.constructor=lodash;LodashWrapper.prototype=baseCreate(baseLodash.prototype);LodashWrapper.prototype.constructor=LodashWrapper;/*------------------------------------------------------------------------*/ /**
+ * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.
+ *
+ * @private
+ * @constructor
+ * @param {*} value The value to wrap.
+ */function LazyWrapper(value){this.__wrapped__=value;this.__actions__=[];this.__dir__=1;this.__filtered__=false;this.__iteratees__=[];this.__takeCount__=MAX_ARRAY_LENGTH;this.__views__=[];}/**
+ * Creates a clone of the lazy wrapper object.
+ *
+ * @private
+ * @name clone
+ * @memberOf LazyWrapper
+ * @returns {Object} Returns the cloned `LazyWrapper` object.
+ */function lazyClone(){var result=new LazyWrapper(this.__wrapped__);result.__actions__=copyArray(this.__actions__);result.__dir__=this.__dir__;result.__filtered__=this.__filtered__;result.__iteratees__=copyArray(this.__iteratees__);result.__takeCount__=this.__takeCount__;result.__views__=copyArray(this.__views__);return result;}/**
+ * Reverses the direction of lazy iteration.
+ *
+ * @private
+ * @name reverse
+ * @memberOf LazyWrapper
+ * @returns {Object} Returns the new reversed `LazyWrapper` object.
+ */function lazyReverse(){if(this.__filtered__){var result=new LazyWrapper(this);result.__dir__=-1;result.__filtered__=true;}else{result=this.clone();result.__dir__*=-1;}return result;}/**
+ * Extracts the unwrapped value from its lazy wrapper.
+ *
+ * @private
+ * @name value
+ * @memberOf LazyWrapper
+ * @returns {*} Returns the unwrapped value.
+ */function lazyValue(){var array=this.__wrapped__.value(),dir=this.__dir__,isArr=isArray(array),isRight=dir<0,arrLength=isArr?array.length:0,view=getView(0,arrLength,this.__views__),start=view.start,end=view.end,length=end-start,index=isRight?end:start-1,iteratees=this.__iteratees__,iterLength=iteratees.length,resIndex=0,takeCount=nativeMin(length,this.__takeCount__);if(!isArr||!isRight&&arrLength==length&&takeCount==length){return baseWrapperValue(array,this.__actions__);}var result=[];outer:while(length--&&resIndex-1;}/**
+ * Sets the list cache `key` to `value`.
+ *
+ * @private
+ * @name set
+ * @memberOf ListCache
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns the list cache instance.
+ */function listCacheSet(key,value){var data=this.__data__,index=assocIndexOf(data,key);if(index<0){++this.size;data.push([key,value]);}else{data[index][1]=value;}return this;}// Add methods to `ListCache`.
+ListCache.prototype.clear=listCacheClear;ListCache.prototype['delete']=listCacheDelete;ListCache.prototype.get=listCacheGet;ListCache.prototype.has=listCacheHas;ListCache.prototype.set=listCacheSet;/*------------------------------------------------------------------------*/ /**
+ * Creates a map cache object to store key-value pairs.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */function MapCache(entries){var index=-1,length=entries==null?0:entries.length;this.clear();while(++index=lower?number:lower;}}return number;}/**
+ * The base implementation of `_.clone` and `_.cloneDeep` which tracks
+ * traversed objects.
+ *
+ * @private
+ * @param {*} value The value to clone.
+ * @param {boolean} bitmask The bitmask flags.
+ * 1 - Deep clone
+ * 2 - Flatten inherited properties
+ * 4 - Clone symbols
+ * @param {Function} [customizer] The function to customize cloning.
+ * @param {string} [key] The key of `value`.
+ * @param {Object} [object] The parent object of `value`.
+ * @param {Object} [stack] Tracks traversed objects and their clone counterparts.
+ * @returns {*} Returns the cloned value.
+ */function baseClone(value,bitmask,customizer,key,object,stack){var result,isDeep=bitmask&CLONE_DEEP_FLAG,isFlat=bitmask&CLONE_FLAT_FLAG,isFull=bitmask&CLONE_SYMBOLS_FLAG;if(customizer){result=object?customizer(value,key,object,stack):customizer(value);}if(result!==undefined){return result;}if(!isObject(value)){return value;}var isArr=isArray(value);if(isArr){result=initCloneArray(value);if(!isDeep){return copyArray(value,result);}}else{var tag=getTag(value),isFunc=tag==funcTag||tag==genTag;if(isBuffer(value)){return cloneBuffer(value,isDeep);}if(tag==objectTag||tag==argsTag||isFunc&&!object){result=isFlat||isFunc?{}:initCloneObject(value);if(!isDeep){return isFlat?copySymbolsIn(value,baseAssignIn(result,value)):copySymbols(value,baseAssign(result,value));}}else{if(!cloneableTags[tag]){return object?value:{};}result=initCloneByTag(value,tag,isDeep);}}// Check for circular references and return its corresponding clone.
+stack||(stack=new Stack());var stacked=stack.get(value);if(stacked){return stacked;}stack.set(value,result);if(isSet(value)){value.forEach(function(subValue){result.add(baseClone(subValue,bitmask,customizer,subValue,value,stack));});}else if(isMap(value)){value.forEach(function(subValue,key){result.set(key,baseClone(subValue,bitmask,customizer,key,value,stack));});}var keysFunc=isFull?isFlat?getAllKeysIn:getAllKeys:isFlat?keysIn:keys;var props=isArr?undefined:keysFunc(value);arrayEach(props||value,function(subValue,key){if(props){key=subValue;subValue=value[key];}// Recursively populate clone (susceptible to call stack limits).
+assignValue(result,key,baseClone(subValue,bitmask,customizer,key,value,stack));});return result;}/**
+ * The base implementation of `_.conforms` which doesn't clone `source`.
+ *
+ * @private
+ * @param {Object} source The object of property predicates to conform to.
+ * @returns {Function} Returns the new spec function.
+ */function baseConforms(source){var props=keys(source);return function(object){return baseConformsTo(object,source,props);};}/**
+ * The base implementation of `_.conformsTo` which accepts `props` to check.
+ *
+ * @private
+ * @param {Object} object The object to inspect.
+ * @param {Object} source The object of property predicates to conform to.
+ * @returns {boolean} Returns `true` if `object` conforms, else `false`.
+ */function baseConformsTo(object,source,props){var length=props.length;if(object==null){return!length;}object=Object(object);while(length--){var key=props[length],predicate=source[key],value=object[key];if(value===undefined&&!(key in object)||!predicate(value)){return false;}}return true;}/**
+ * The base implementation of `_.delay` and `_.defer` which accepts `args`
+ * to provide to `func`.
+ *
+ * @private
+ * @param {Function} func The function to delay.
+ * @param {number} wait The number of milliseconds to delay invocation.
+ * @param {Array} args The arguments to provide to `func`.
+ * @returns {number|Object} Returns the timer id or timeout object.
+ */function baseDelay(func,wait,args){if(typeof func!='function'){throw new TypeError(FUNC_ERROR_TEXT);}return setTimeout(function(){func.apply(undefined,args);},wait);}/**
+ * The base implementation of methods like `_.difference` without support
+ * for excluding multiple arrays or iteratee shorthands.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {Array} values The values to exclude.
+ * @param {Function} [iteratee] The iteratee invoked per element.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new array of filtered values.
+ */function baseDifference(array,values,iteratee,comparator){var index=-1,includes=arrayIncludes,isCommon=true,length=array.length,result=[],valuesLength=values.length;if(!length){return result;}if(iteratee){values=arrayMap(values,baseUnary(iteratee));}if(comparator){includes=arrayIncludesWith;isCommon=false;}else if(values.length>=LARGE_ARRAY_SIZE){includes=cacheHas;isCommon=false;values=new SetCache(values);}outer:while(++indexlength?0:length+start;}end=end===undefined||end>length?length:toInteger(end);if(end<0){end+=length;}end=start>end?0:toLength(end);while(start0&&predicate(value)){if(depth>1){// Recursively flatten arrays (susceptible to call stack limits).
+baseFlatten(value,depth-1,predicate,isStrict,result);}else{arrayPush(result,value);}}else if(!isStrict){result[result.length]=value;}}return result;}/**
+ * The base implementation of `baseForOwn` which iterates over `object`
+ * properties returned by `keysFunc` and invokes `iteratee` for each property.
+ * Iteratee functions may exit iteration early by explicitly returning `false`.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {Function} keysFunc The function to get the keys of `object`.
+ * @returns {Object} Returns `object`.
+ */var baseFor=createBaseFor();/**
+ * This function is like `baseFor` except that it iterates over properties
+ * in the opposite order.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {Function} keysFunc The function to get the keys of `object`.
+ * @returns {Object} Returns `object`.
+ */var baseForRight=createBaseFor(true);/**
+ * The base implementation of `_.forOwn` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Object} Returns `object`.
+ */function baseForOwn(object,iteratee){return object&&baseFor(object,iteratee,keys);}/**
+ * The base implementation of `_.forOwnRight` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Object} Returns `object`.
+ */function baseForOwnRight(object,iteratee){return object&&baseForRight(object,iteratee,keys);}/**
+ * The base implementation of `_.functions` which creates an array of
+ * `object` function property names filtered from `props`.
+ *
+ * @private
+ * @param {Object} object The object to inspect.
+ * @param {Array} props The property names to filter.
+ * @returns {Array} Returns the function names.
+ */function baseFunctions(object,props){return arrayFilter(props,function(key){return isFunction(object[key]);});}/**
+ * The base implementation of `_.get` without support for default values.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path of the property to get.
+ * @returns {*} Returns the resolved value.
+ */function baseGet(object,path){path=castPath(path,object);var index=0,length=path.length;while(object!=null&&indexother;}/**
+ * The base implementation of `_.has` without support for deep paths.
+ *
+ * @private
+ * @param {Object} [object] The object to query.
+ * @param {Array|string} key The key to check.
+ * @returns {boolean} Returns `true` if `key` exists, else `false`.
+ */function baseHas(object,key){return object!=null&&hasOwnProperty.call(object,key);}/**
+ * The base implementation of `_.hasIn` without support for deep paths.
+ *
+ * @private
+ * @param {Object} [object] The object to query.
+ * @param {Array|string} key The key to check.
+ * @returns {boolean} Returns `true` if `key` exists, else `false`.
+ */function baseHasIn(object,key){return object!=null&&key in Object(object);}/**
+ * The base implementation of `_.inRange` which doesn't coerce arguments.
+ *
+ * @private
+ * @param {number} number The number to check.
+ * @param {number} start The start of the range.
+ * @param {number} end The end of the range.
+ * @returns {boolean} Returns `true` if `number` is in the range, else `false`.
+ */function baseInRange(number,start,end){return number>=nativeMin(start,end)&&number=120&&array.length>=120)?new SetCache(othIndex&&array):undefined;}array=arrays[0];var index=-1,seen=caches[0];outer:while(++index-1){if(seen!==array){splice.call(seen,fromIndex,1);}splice.call(array,fromIndex,1);}}return array;}/**
+ * The base implementation of `_.pullAt` without support for individual
+ * indexes or capturing the removed elements.
+ *
+ * @private
+ * @param {Array} array The array to modify.
+ * @param {number[]} indexes The indexes of elements to remove.
+ * @returns {Array} Returns `array`.
+ */function basePullAt(array,indexes){var length=array?indexes.length:0,lastIndex=length-1;while(length--){var index=indexes[length];if(length==lastIndex||index!==previous){var previous=index;if(isIndex(index)){splice.call(array,index,1);}else{baseUnset(array,index);}}}return array;}/**
+ * The base implementation of `_.random` without support for returning
+ * floating-point numbers.
+ *
+ * @private
+ * @param {number} lower The lower bound.
+ * @param {number} upper The upper bound.
+ * @returns {number} Returns the random number.
+ */function baseRandom(lower,upper){return lower+nativeFloor(nativeRandom()*(upper-lower+1));}/**
+ * The base implementation of `_.range` and `_.rangeRight` which doesn't
+ * coerce arguments.
+ *
+ * @private
+ * @param {number} start The start of the range.
+ * @param {number} end The end of the range.
+ * @param {number} step The value to increment or decrement by.
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {Array} Returns the range of numbers.
+ */function baseRange(start,end,step,fromRight){var index=-1,length=nativeMax(nativeCeil((end-start)/(step||1)),0),result=Array(length);while(length--){result[fromRight?length:++index]=start;start+=step;}return result;}/**
+ * The base implementation of `_.repeat` which doesn't coerce arguments.
+ *
+ * @private
+ * @param {string} string The string to repeat.
+ * @param {number} n The number of times to repeat the string.
+ * @returns {string} Returns the repeated string.
+ */function baseRepeat(string,n){var result='';if(!string||n<1||n>MAX_SAFE_INTEGER){return result;}// Leverage the exponentiation by squaring algorithm for a faster repeat.
+// See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details.
+do{if(n%2){result+=string;}n=nativeFloor(n/2);if(n){string+=string;}}while(n);return result;}/**
+ * The base implementation of `_.rest` which doesn't validate or coerce arguments.
+ *
+ * @private
+ * @param {Function} func The function to apply a rest parameter to.
+ * @param {number} [start=func.length-1] The start position of the rest parameter.
+ * @returns {Function} Returns the new function.
+ */function baseRest(func,start){return setToString(overRest(func,start,identity),func+'');}/**
+ * The base implementation of `_.sample`.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to sample.
+ * @returns {*} Returns the random element.
+ */function baseSample(collection){return arraySample(values(collection));}/**
+ * The base implementation of `_.sampleSize` without param guards.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to sample.
+ * @param {number} n The number of elements to sample.
+ * @returns {Array} Returns the random elements.
+ */function baseSampleSize(collection,n){var array=values(collection);return shuffleSelf(array,baseClamp(n,0,array.length));}/**
+ * The base implementation of `_.set`.
+ *
+ * @private
+ * @param {Object} object The object to modify.
+ * @param {Array|string} path The path of the property to set.
+ * @param {*} value The value to set.
+ * @param {Function} [customizer] The function to customize path creation.
+ * @returns {Object} Returns `object`.
+ */function baseSet(object,path,value,customizer){if(!isObject(object)){return object;}path=castPath(path,object);var index=-1,length=path.length,lastIndex=length-1,nested=object;while(nested!=null&&++indexlength?0:length+start;}end=end>length?length:end;if(end<0){end+=length;}length=start>end?0:end-start>>>0;start>>>=0;var result=Array(length);while(++index>>1,computed=array[mid];if(computed!==null&&!isSymbol(computed)&&(retHighest?computed<=value:computed=LARGE_ARRAY_SIZE){var set=iteratee?null:createSet(array);if(set){return setToArray(set);}isCommon=false;includes=cacheHas;seen=new SetCache();}else{seen=iteratee?[]:result;}outer:while(++index=length?array:baseSlice(array,start,end);}/**
+ * A simple wrapper around the global [`clearTimeout`](https://mdn.io/clearTimeout).
+ *
+ * @private
+ * @param {number|Object} id The timer id or timeout object of the timer to clear.
+ */var clearTimeout=ctxClearTimeout||function(id){return root.clearTimeout(id);};/**
+ * Creates a clone of `buffer`.
+ *
+ * @private
+ * @param {Buffer} buffer The buffer to clone.
+ * @param {boolean} [isDeep] Specify a deep clone.
+ * @returns {Buffer} Returns the cloned buffer.
+ */function cloneBuffer(buffer,isDeep){if(isDeep){return buffer.slice();}var length=buffer.length,result=allocUnsafe?allocUnsafe(length):new buffer.constructor(length);buffer.copy(result);return result;}/**
+ * Creates a clone of `arrayBuffer`.
+ *
+ * @private
+ * @param {ArrayBuffer} arrayBuffer The array buffer to clone.
+ * @returns {ArrayBuffer} Returns the cloned array buffer.
+ */function cloneArrayBuffer(arrayBuffer){var result=new arrayBuffer.constructor(arrayBuffer.byteLength);new Uint8Array(result).set(new Uint8Array(arrayBuffer));return result;}/**
+ * Creates a clone of `dataView`.
+ *
+ * @private
+ * @param {Object} dataView The data view to clone.
+ * @param {boolean} [isDeep] Specify a deep clone.
+ * @returns {Object} Returns the cloned data view.
+ */function cloneDataView(dataView,isDeep){var buffer=isDeep?cloneArrayBuffer(dataView.buffer):dataView.buffer;return new dataView.constructor(buffer,dataView.byteOffset,dataView.byteLength);}/**
+ * Creates a clone of `regexp`.
+ *
+ * @private
+ * @param {Object} regexp The regexp to clone.
+ * @returns {Object} Returns the cloned regexp.
+ */function cloneRegExp(regexp){var result=new regexp.constructor(regexp.source,reFlags.exec(regexp));result.lastIndex=regexp.lastIndex;return result;}/**
+ * Creates a clone of the `symbol` object.
+ *
+ * @private
+ * @param {Object} symbol The symbol object to clone.
+ * @returns {Object} Returns the cloned symbol object.
+ */function cloneSymbol(symbol){return symbolValueOf?Object(symbolValueOf.call(symbol)):{};}/**
+ * Creates a clone of `typedArray`.
+ *
+ * @private
+ * @param {Object} typedArray The typed array to clone.
+ * @param {boolean} [isDeep] Specify a deep clone.
+ * @returns {Object} Returns the cloned typed array.
+ */function cloneTypedArray(typedArray,isDeep){var buffer=isDeep?cloneArrayBuffer(typedArray.buffer):typedArray.buffer;return new typedArray.constructor(buffer,typedArray.byteOffset,typedArray.length);}/**
+ * Compares values to sort them in ascending order.
+ *
+ * @private
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @returns {number} Returns the sort order indicator for `value`.
+ */function compareAscending(value,other){if(value!==other){var valIsDefined=value!==undefined,valIsNull=value===null,valIsReflexive=value===value,valIsSymbol=isSymbol(value);var othIsDefined=other!==undefined,othIsNull=other===null,othIsReflexive=other===other,othIsSymbol=isSymbol(other);if(!othIsNull&&!othIsSymbol&&!valIsSymbol&&value>other||valIsSymbol&&othIsDefined&&othIsReflexive&&!othIsNull&&!othIsSymbol||valIsNull&&othIsDefined&&othIsReflexive||!valIsDefined&&othIsReflexive||!valIsReflexive){return 1;}if(!valIsNull&&!valIsSymbol&&!othIsSymbol&&value=ordersLength){return result;}var order=orders[index];return result*(order=='desc'?-1:1);}}// Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
+// that causes it, under certain circumstances, to provide the same value for
+// `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247
+// for more details.
+//
+// This also ensures a stable sort in V8 and other engines.
+// See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details.
+return object.index-other.index;}/**
+ * Creates an array that is the composition of partially applied arguments,
+ * placeholders, and provided arguments into a single array of arguments.
+ *
+ * @private
+ * @param {Array} args The provided arguments.
+ * @param {Array} partials The arguments to prepend to those provided.
+ * @param {Array} holders The `partials` placeholder indexes.
+ * @params {boolean} [isCurried] Specify composing for a curried function.
+ * @returns {Array} Returns the new array of composed arguments.
+ */function composeArgs(args,partials,holders,isCurried){var argsIndex=-1,argsLength=args.length,holdersLength=holders.length,leftIndex=-1,leftLength=partials.length,rangeLength=nativeMax(argsLength-holdersLength,0),result=Array(leftLength+rangeLength),isUncurried=!isCurried;while(++leftIndex1?sources[length-1]:undefined,guard=length>2?sources[2]:undefined;customizer=assigner.length>3&&typeof customizer=='function'?(length--,customizer):undefined;if(guard&&isIterateeCall(sources[0],sources[1],guard)){customizer=length<3?undefined:customizer;length=1;}object=Object(object);while(++index-1?iterable[iteratee?collection[index]:index]:undefined;};}/**
+ * Creates a `_.flow` or `_.flowRight` function.
+ *
+ * @private
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {Function} Returns the new flow function.
+ */function createFlow(fromRight){return flatRest(function(funcs){var length=funcs.length,index=length,prereq=LodashWrapper.prototype.thru;if(fromRight){funcs.reverse();}while(index--){var func=funcs[index];if(typeof func!='function'){throw new TypeError(FUNC_ERROR_TEXT);}if(prereq&&!wrapper&&getFuncName(func)=='wrapper'){var wrapper=new LodashWrapper([],true);}}index=wrapper?index:length;while(++index1){args.reverse();}if(isAry&&aryarrLength)){return false;}// Check that cyclic values are equal.
+var arrStacked=stack.get(array);var othStacked=stack.get(other);if(arrStacked&&othStacked){return arrStacked==other&&othStacked==array;}var index=-1,result=true,seen=bitmask&COMPARE_UNORDERED_FLAG?new SetCache():undefined;stack.set(array,other);stack.set(other,array);// Ignore non-index properties.
+while(++index1?'& ':'')+details[lastIndex];details=details.join(length>2?', ':' ');return source.replace(reWrapComment,'{\n/* [wrapped with '+details+'] */\n');}/**
+ * Checks if `value` is a flattenable `arguments` object or array.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.
+ */function isFlattenable(value){return isArray(value)||isArguments(value)||!!(spreadableSymbol&&value&&value[spreadableSymbol]);}/**
+ * Checks if `value` is a valid array-like index.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
+ * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
+ */function isIndex(value,length){var type=typeof value;length=length==null?MAX_SAFE_INTEGER:length;return!!length&&(type=='number'||type!='symbol'&&reIsUint.test(value))&&value>-1&&value%1==0&&value0){if(++count>=HOT_COUNT){return arguments[0];}}else{count=0;}return func.apply(undefined,arguments);};}/**
+ * A specialized version of `_.shuffle` which mutates and sets the size of `array`.
+ *
+ * @private
+ * @param {Array} array The array to shuffle.
+ * @param {number} [size=array.length] The size of `array`.
+ * @returns {Array} Returns `array`.
+ */function shuffleSelf(array,size){var index=-1,length=array.length,lastIndex=length-1;size=size===undefined?length:size;while(++index [['a', 'b'], ['c', 'd']]
+ *
+ * _.chunk(['a', 'b', 'c', 'd'], 3);
+ * // => [['a', 'b', 'c'], ['d']]
+ */function chunk(array,size,guard){if(guard?isIterateeCall(array,size,guard):size===undefined){size=1;}else{size=nativeMax(toInteger(size),0);}var length=array==null?0:array.length;if(!length||size<1){return[];}var index=0,resIndex=0,result=Array(nativeCeil(length/size));while(index [1, 2, 3]
+ */function compact(array){var index=-1,length=array==null?0:array.length,resIndex=0,result=[];while(++index [1, 2, 3, [4]]
+ *
+ * console.log(array);
+ * // => [1]
+ */function concat(){var length=arguments.length;if(!length){return[];}var args=Array(length-1),array=arguments[0],index=length;while(index--){args[index-1]=arguments[index];}return arrayPush(isArray(array)?copyArray(array):[array],baseFlatten(args,1));}/**
+ * Creates an array of `array` values not included in the other given arrays
+ * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons. The order and references of result values are
+ * determined by the first array.
+ *
+ * **Note:** Unlike `_.pullAll`, this method returns a new array.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {...Array} [values] The values to exclude.
+ * @returns {Array} Returns the new array of filtered values.
+ * @see _.without, _.xor
+ * @example
+ *
+ * _.difference([2, 1], [2, 3]);
+ * // => [1]
+ */var difference=baseRest(function(array,values){return isArrayLikeObject(array)?baseDifference(array,baseFlatten(values,1,isArrayLikeObject,true)):[];});/**
+ * This method is like `_.difference` except that it accepts `iteratee` which
+ * is invoked for each element of `array` and `values` to generate the criterion
+ * by which they're compared. The order and references of result values are
+ * determined by the first array. The iteratee is invoked with one argument:
+ * (value).
+ *
+ * **Note:** Unlike `_.pullAllBy`, this method returns a new array.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {...Array} [values] The values to exclude.
+ * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {Array} Returns the new array of filtered values.
+ * @example
+ *
+ * _.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor);
+ * // => [1.2]
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x');
+ * // => [{ 'x': 2 }]
+ */var differenceBy=baseRest(function(array,values){var iteratee=last(values);if(isArrayLikeObject(iteratee)){iteratee=undefined;}return isArrayLikeObject(array)?baseDifference(array,baseFlatten(values,1,isArrayLikeObject,true),getIteratee(iteratee,2)):[];});/**
+ * This method is like `_.difference` except that it accepts `comparator`
+ * which is invoked to compare elements of `array` to `values`. The order and
+ * references of result values are determined by the first array. The comparator
+ * is invoked with two arguments: (arrVal, othVal).
+ *
+ * **Note:** Unlike `_.pullAllWith`, this method returns a new array.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {...Array} [values] The values to exclude.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new array of filtered values.
+ * @example
+ *
+ * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
+ *
+ * _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual);
+ * // => [{ 'x': 2, 'y': 1 }]
+ */var differenceWith=baseRest(function(array,values){var comparator=last(values);if(isArrayLikeObject(comparator)){comparator=undefined;}return isArrayLikeObject(array)?baseDifference(array,baseFlatten(values,1,isArrayLikeObject,true),undefined,comparator):[];});/**
+ * Creates a slice of `array` with `n` elements dropped from the beginning.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.5.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {number} [n=1] The number of elements to drop.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.drop([1, 2, 3]);
+ * // => [2, 3]
+ *
+ * _.drop([1, 2, 3], 2);
+ * // => [3]
+ *
+ * _.drop([1, 2, 3], 5);
+ * // => []
+ *
+ * _.drop([1, 2, 3], 0);
+ * // => [1, 2, 3]
+ */function drop(array,n,guard){var length=array==null?0:array.length;if(!length){return[];}n=guard||n===undefined?1:toInteger(n);return baseSlice(array,n<0?0:n,length);}/**
+ * Creates a slice of `array` with `n` elements dropped from the end.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {number} [n=1] The number of elements to drop.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.dropRight([1, 2, 3]);
+ * // => [1, 2]
+ *
+ * _.dropRight([1, 2, 3], 2);
+ * // => [1]
+ *
+ * _.dropRight([1, 2, 3], 5);
+ * // => []
+ *
+ * _.dropRight([1, 2, 3], 0);
+ * // => [1, 2, 3]
+ */function dropRight(array,n,guard){var length=array==null?0:array.length;if(!length){return[];}n=guard||n===undefined?1:toInteger(n);n=length-n;return baseSlice(array,0,n<0?0:n);}/**
+ * Creates a slice of `array` excluding elements dropped from the end.
+ * Elements are dropped until `predicate` returns falsey. The predicate is
+ * invoked with three arguments: (value, index, array).
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'active': true },
+ * { 'user': 'fred', 'active': false },
+ * { 'user': 'pebbles', 'active': false }
+ * ];
+ *
+ * _.dropRightWhile(users, function(o) { return !o.active; });
+ * // => objects for ['barney']
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.dropRightWhile(users, { 'user': 'pebbles', 'active': false });
+ * // => objects for ['barney', 'fred']
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.dropRightWhile(users, ['active', false]);
+ * // => objects for ['barney']
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.dropRightWhile(users, 'active');
+ * // => objects for ['barney', 'fred', 'pebbles']
+ */function dropRightWhile(array,predicate){return array&&array.length?baseWhile(array,getIteratee(predicate,3),true,true):[];}/**
+ * Creates a slice of `array` excluding elements dropped from the beginning.
+ * Elements are dropped until `predicate` returns falsey. The predicate is
+ * invoked with three arguments: (value, index, array).
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'active': false },
+ * { 'user': 'fred', 'active': false },
+ * { 'user': 'pebbles', 'active': true }
+ * ];
+ *
+ * _.dropWhile(users, function(o) { return !o.active; });
+ * // => objects for ['pebbles']
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.dropWhile(users, { 'user': 'barney', 'active': false });
+ * // => objects for ['fred', 'pebbles']
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.dropWhile(users, ['active', false]);
+ * // => objects for ['pebbles']
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.dropWhile(users, 'active');
+ * // => objects for ['barney', 'fred', 'pebbles']
+ */function dropWhile(array,predicate){return array&&array.length?baseWhile(array,getIteratee(predicate,3),true):[];}/**
+ * Fills elements of `array` with `value` from `start` up to, but not
+ * including, `end`.
+ *
+ * **Note:** This method mutates `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.2.0
+ * @category Array
+ * @param {Array} array The array to fill.
+ * @param {*} value The value to fill `array` with.
+ * @param {number} [start=0] The start position.
+ * @param {number} [end=array.length] The end position.
+ * @returns {Array} Returns `array`.
+ * @example
+ *
+ * var array = [1, 2, 3];
+ *
+ * _.fill(array, 'a');
+ * console.log(array);
+ * // => ['a', 'a', 'a']
+ *
+ * _.fill(Array(3), 2);
+ * // => [2, 2, 2]
+ *
+ * _.fill([4, 6, 8, 10], '*', 1, 3);
+ * // => [4, '*', '*', 10]
+ */function fill(array,value,start,end){var length=array==null?0:array.length;if(!length){return[];}if(start&&typeof start!='number'&&isIterateeCall(array,value,start)){start=0;end=length;}return baseFill(array,value,start,end);}/**
+ * This method is like `_.find` except that it returns the index of the first
+ * element `predicate` returns truthy for instead of the element itself.
+ *
+ * @static
+ * @memberOf _
+ * @since 1.1.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {number} Returns the index of the found element, else `-1`.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'active': false },
+ * { 'user': 'fred', 'active': false },
+ * { 'user': 'pebbles', 'active': true }
+ * ];
+ *
+ * _.findIndex(users, function(o) { return o.user == 'barney'; });
+ * // => 0
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.findIndex(users, { 'user': 'fred', 'active': false });
+ * // => 1
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.findIndex(users, ['active', false]);
+ * // => 0
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.findIndex(users, 'active');
+ * // => 2
+ */function findIndex(array,predicate,fromIndex){var length=array==null?0:array.length;if(!length){return-1;}var index=fromIndex==null?0:toInteger(fromIndex);if(index<0){index=nativeMax(length+index,0);}return baseFindIndex(array,getIteratee(predicate,3),index);}/**
+ * This method is like `_.findIndex` except that it iterates over elements
+ * of `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @since 2.0.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @param {number} [fromIndex=array.length-1] The index to search from.
+ * @returns {number} Returns the index of the found element, else `-1`.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'active': true },
+ * { 'user': 'fred', 'active': false },
+ * { 'user': 'pebbles', 'active': false }
+ * ];
+ *
+ * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; });
+ * // => 2
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.findLastIndex(users, { 'user': 'barney', 'active': true });
+ * // => 0
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.findLastIndex(users, ['active', false]);
+ * // => 2
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.findLastIndex(users, 'active');
+ * // => 0
+ */function findLastIndex(array,predicate,fromIndex){var length=array==null?0:array.length;if(!length){return-1;}var index=length-1;if(fromIndex!==undefined){index=toInteger(fromIndex);index=fromIndex<0?nativeMax(length+index,0):nativeMin(index,length-1);}return baseFindIndex(array,getIteratee(predicate,3),index,true);}/**
+ * Flattens `array` a single level deep.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to flatten.
+ * @returns {Array} Returns the new flattened array.
+ * @example
+ *
+ * _.flatten([1, [2, [3, [4]], 5]]);
+ * // => [1, 2, [3, [4]], 5]
+ */function flatten(array){var length=array==null?0:array.length;return length?baseFlatten(array,1):[];}/**
+ * Recursively flattens `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Array
+ * @param {Array} array The array to flatten.
+ * @returns {Array} Returns the new flattened array.
+ * @example
+ *
+ * _.flattenDeep([1, [2, [3, [4]], 5]]);
+ * // => [1, 2, 3, 4, 5]
+ */function flattenDeep(array){var length=array==null?0:array.length;return length?baseFlatten(array,INFINITY):[];}/**
+ * Recursively flatten `array` up to `depth` times.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.4.0
+ * @category Array
+ * @param {Array} array The array to flatten.
+ * @param {number} [depth=1] The maximum recursion depth.
+ * @returns {Array} Returns the new flattened array.
+ * @example
+ *
+ * var array = [1, [2, [3, [4]], 5]];
+ *
+ * _.flattenDepth(array, 1);
+ * // => [1, 2, [3, [4]], 5]
+ *
+ * _.flattenDepth(array, 2);
+ * // => [1, 2, 3, [4], 5]
+ */function flattenDepth(array,depth){var length=array==null?0:array.length;if(!length){return[];}depth=depth===undefined?1:toInteger(depth);return baseFlatten(array,depth);}/**
+ * The inverse of `_.toPairs`; this method returns an object composed
+ * from key-value `pairs`.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} pairs The key-value pairs.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * _.fromPairs([['a', 1], ['b', 2]]);
+ * // => { 'a': 1, 'b': 2 }
+ */function fromPairs(pairs){var index=-1,length=pairs==null?0:pairs.length,result={};while(++index 1
+ *
+ * _.head([]);
+ * // => undefined
+ */function head(array){return array&&array.length?array[0]:undefined;}/**
+ * Gets the index at which the first occurrence of `value` is found in `array`
+ * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons. If `fromIndex` is negative, it's used as the
+ * offset from the end of `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to search for.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ * @example
+ *
+ * _.indexOf([1, 2, 1, 2], 2);
+ * // => 1
+ *
+ * // Search from the `fromIndex`.
+ * _.indexOf([1, 2, 1, 2], 2, 2);
+ * // => 3
+ */function indexOf(array,value,fromIndex){var length=array==null?0:array.length;if(!length){return-1;}var index=fromIndex==null?0:toInteger(fromIndex);if(index<0){index=nativeMax(length+index,0);}return baseIndexOf(array,value,index);}/**
+ * Gets all but the last element of `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.initial([1, 2, 3]);
+ * // => [1, 2]
+ */function initial(array){var length=array==null?0:array.length;return length?baseSlice(array,0,-1):[];}/**
+ * Creates an array of unique values that are included in all given arrays
+ * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons. The order and references of result values are
+ * determined by the first array.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @returns {Array} Returns the new array of intersecting values.
+ * @example
+ *
+ * _.intersection([2, 1], [2, 3]);
+ * // => [2]
+ */var intersection=baseRest(function(arrays){var mapped=arrayMap(arrays,castArrayLikeObject);return mapped.length&&mapped[0]===arrays[0]?baseIntersection(mapped):[];});/**
+ * This method is like `_.intersection` except that it accepts `iteratee`
+ * which is invoked for each element of each `arrays` to generate the criterion
+ * by which they're compared. The order and references of result values are
+ * determined by the first array. The iteratee is invoked with one argument:
+ * (value).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {Array} Returns the new array of intersecting values.
+ * @example
+ *
+ * _.intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor);
+ * // => [2.1]
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
+ * // => [{ 'x': 1 }]
+ */var intersectionBy=baseRest(function(arrays){var iteratee=last(arrays),mapped=arrayMap(arrays,castArrayLikeObject);if(iteratee===last(mapped)){iteratee=undefined;}else{mapped.pop();}return mapped.length&&mapped[0]===arrays[0]?baseIntersection(mapped,getIteratee(iteratee,2)):[];});/**
+ * This method is like `_.intersection` except that it accepts `comparator`
+ * which is invoked to compare elements of `arrays`. The order and references
+ * of result values are determined by the first array. The comparator is
+ * invoked with two arguments: (arrVal, othVal).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new array of intersecting values.
+ * @example
+ *
+ * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
+ * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
+ *
+ * _.intersectionWith(objects, others, _.isEqual);
+ * // => [{ 'x': 1, 'y': 2 }]
+ */var intersectionWith=baseRest(function(arrays){var comparator=last(arrays),mapped=arrayMap(arrays,castArrayLikeObject);comparator=typeof comparator=='function'?comparator:undefined;if(comparator){mapped.pop();}return mapped.length&&mapped[0]===arrays[0]?baseIntersection(mapped,undefined,comparator):[];});/**
+ * Converts all elements in `array` into a string separated by `separator`.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to convert.
+ * @param {string} [separator=','] The element separator.
+ * @returns {string} Returns the joined string.
+ * @example
+ *
+ * _.join(['a', 'b', 'c'], '~');
+ * // => 'a~b~c'
+ */function join(array,separator){return array==null?'':nativeJoin.call(array,separator);}/**
+ * Gets the last element of `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @returns {*} Returns the last element of `array`.
+ * @example
+ *
+ * _.last([1, 2, 3]);
+ * // => 3
+ */function last(array){var length=array==null?0:array.length;return length?array[length-1]:undefined;}/**
+ * This method is like `_.indexOf` except that it iterates over elements of
+ * `array` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to search for.
+ * @param {number} [fromIndex=array.length-1] The index to search from.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ * @example
+ *
+ * _.lastIndexOf([1, 2, 1, 2], 2);
+ * // => 3
+ *
+ * // Search from the `fromIndex`.
+ * _.lastIndexOf([1, 2, 1, 2], 2, 2);
+ * // => 1
+ */function lastIndexOf(array,value,fromIndex){var length=array==null?0:array.length;if(!length){return-1;}var index=length;if(fromIndex!==undefined){index=toInteger(fromIndex);index=index<0?nativeMax(length+index,0):nativeMin(index,length-1);}return value===value?strictLastIndexOf(array,value,index):baseFindIndex(array,baseIsNaN,index,true);}/**
+ * Gets the element at index `n` of `array`. If `n` is negative, the nth
+ * element from the end is returned.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.11.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {number} [n=0] The index of the element to return.
+ * @returns {*} Returns the nth element of `array`.
+ * @example
+ *
+ * var array = ['a', 'b', 'c', 'd'];
+ *
+ * _.nth(array, 1);
+ * // => 'b'
+ *
+ * _.nth(array, -2);
+ * // => 'c';
+ */function nth(array,n){return array&&array.length?baseNth(array,toInteger(n)):undefined;}/**
+ * Removes all given values from `array` using
+ * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons.
+ *
+ * **Note:** Unlike `_.without`, this method mutates `array`. Use `_.remove`
+ * to remove elements from an array by predicate.
+ *
+ * @static
+ * @memberOf _
+ * @since 2.0.0
+ * @category Array
+ * @param {Array} array The array to modify.
+ * @param {...*} [values] The values to remove.
+ * @returns {Array} Returns `array`.
+ * @example
+ *
+ * var array = ['a', 'b', 'c', 'a', 'b', 'c'];
+ *
+ * _.pull(array, 'a', 'c');
+ * console.log(array);
+ * // => ['b', 'b']
+ */var pull=baseRest(pullAll);/**
+ * This method is like `_.pull` except that it accepts an array of values to remove.
+ *
+ * **Note:** Unlike `_.difference`, this method mutates `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to modify.
+ * @param {Array} values The values to remove.
+ * @returns {Array} Returns `array`.
+ * @example
+ *
+ * var array = ['a', 'b', 'c', 'a', 'b', 'c'];
+ *
+ * _.pullAll(array, ['a', 'c']);
+ * console.log(array);
+ * // => ['b', 'b']
+ */function pullAll(array,values){return array&&array.length&&values&&values.length?basePullAll(array,values):array;}/**
+ * This method is like `_.pullAll` except that it accepts `iteratee` which is
+ * invoked for each element of `array` and `values` to generate the criterion
+ * by which they're compared. The iteratee is invoked with one argument: (value).
+ *
+ * **Note:** Unlike `_.differenceBy`, this method mutates `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to modify.
+ * @param {Array} values The values to remove.
+ * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {Array} Returns `array`.
+ * @example
+ *
+ * var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }];
+ *
+ * _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x');
+ * console.log(array);
+ * // => [{ 'x': 2 }]
+ */function pullAllBy(array,values,iteratee){return array&&array.length&&values&&values.length?basePullAll(array,values,getIteratee(iteratee,2)):array;}/**
+ * This method is like `_.pullAll` except that it accepts `comparator` which
+ * is invoked to compare elements of `array` to `values`. The comparator is
+ * invoked with two arguments: (arrVal, othVal).
+ *
+ * **Note:** Unlike `_.differenceWith`, this method mutates `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.6.0
+ * @category Array
+ * @param {Array} array The array to modify.
+ * @param {Array} values The values to remove.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns `array`.
+ * @example
+ *
+ * var array = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }];
+ *
+ * _.pullAllWith(array, [{ 'x': 3, 'y': 4 }], _.isEqual);
+ * console.log(array);
+ * // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }]
+ */function pullAllWith(array,values,comparator){return array&&array.length&&values&&values.length?basePullAll(array,values,undefined,comparator):array;}/**
+ * Removes elements from `array` corresponding to `indexes` and returns an
+ * array of removed elements.
+ *
+ * **Note:** Unlike `_.at`, this method mutates `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Array
+ * @param {Array} array The array to modify.
+ * @param {...(number|number[])} [indexes] The indexes of elements to remove.
+ * @returns {Array} Returns the new array of removed elements.
+ * @example
+ *
+ * var array = ['a', 'b', 'c', 'd'];
+ * var pulled = _.pullAt(array, [1, 3]);
+ *
+ * console.log(array);
+ * // => ['a', 'c']
+ *
+ * console.log(pulled);
+ * // => ['b', 'd']
+ */var pullAt=flatRest(function(array,indexes){var length=array==null?0:array.length,result=baseAt(array,indexes);basePullAt(array,arrayMap(indexes,function(index){return isIndex(index,length)?+index:index;}).sort(compareAscending));return result;});/**
+ * Removes all elements from `array` that `predicate` returns truthy for
+ * and returns an array of the removed elements. The predicate is invoked
+ * with three arguments: (value, index, array).
+ *
+ * **Note:** Unlike `_.filter`, this method mutates `array`. Use `_.pull`
+ * to pull elements from an array by value.
+ *
+ * @static
+ * @memberOf _
+ * @since 2.0.0
+ * @category Array
+ * @param {Array} array The array to modify.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the new array of removed elements.
+ * @example
+ *
+ * var array = [1, 2, 3, 4];
+ * var evens = _.remove(array, function(n) {
+ * return n % 2 == 0;
+ * });
+ *
+ * console.log(array);
+ * // => [1, 3]
+ *
+ * console.log(evens);
+ * // => [2, 4]
+ */function remove(array,predicate){var result=[];if(!(array&&array.length)){return result;}var index=-1,indexes=[],length=array.length;predicate=getIteratee(predicate,3);while(++index [3, 2, 1]
+ *
+ * console.log(array);
+ * // => [3, 2, 1]
+ */function reverse(array){return array==null?array:nativeReverse.call(array);}/**
+ * Creates a slice of `array` from `start` up to, but not including, `end`.
+ *
+ * **Note:** This method is used instead of
+ * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are
+ * returned.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Array
+ * @param {Array} array The array to slice.
+ * @param {number} [start=0] The start position.
+ * @param {number} [end=array.length] The end position.
+ * @returns {Array} Returns the slice of `array`.
+ */function slice(array,start,end){var length=array==null?0:array.length;if(!length){return[];}if(end&&typeof end!='number'&&isIterateeCall(array,start,end)){start=0;end=length;}else{start=start==null?0:toInteger(start);end=end===undefined?length:toInteger(end);}return baseSlice(array,start,end);}/**
+ * Uses a binary search to determine the lowest index at which `value`
+ * should be inserted into `array` in order to maintain its sort order.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The sorted array to inspect.
+ * @param {*} value The value to evaluate.
+ * @returns {number} Returns the index at which `value` should be inserted
+ * into `array`.
+ * @example
+ *
+ * _.sortedIndex([30, 50], 40);
+ * // => 1
+ */function sortedIndex(array,value){return baseSortedIndex(array,value);}/**
+ * This method is like `_.sortedIndex` except that it accepts `iteratee`
+ * which is invoked for `value` and each element of `array` to compute their
+ * sort ranking. The iteratee is invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The sorted array to inspect.
+ * @param {*} value The value to evaluate.
+ * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {number} Returns the index at which `value` should be inserted
+ * into `array`.
+ * @example
+ *
+ * var objects = [{ 'x': 4 }, { 'x': 5 }];
+ *
+ * _.sortedIndexBy(objects, { 'x': 4 }, function(o) { return o.x; });
+ * // => 0
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.sortedIndexBy(objects, { 'x': 4 }, 'x');
+ * // => 0
+ */function sortedIndexBy(array,value,iteratee){return baseSortedIndexBy(array,value,getIteratee(iteratee,2));}/**
+ * This method is like `_.indexOf` except that it performs a binary
+ * search on a sorted `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to search for.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ * @example
+ *
+ * _.sortedIndexOf([4, 5, 5, 5, 6], 5);
+ * // => 1
+ */function sortedIndexOf(array,value){var length=array==null?0:array.length;if(length){var index=baseSortedIndex(array,value);if(index 4
+ */function sortedLastIndex(array,value){return baseSortedIndex(array,value,true);}/**
+ * This method is like `_.sortedLastIndex` except that it accepts `iteratee`
+ * which is invoked for `value` and each element of `array` to compute their
+ * sort ranking. The iteratee is invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The sorted array to inspect.
+ * @param {*} value The value to evaluate.
+ * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {number} Returns the index at which `value` should be inserted
+ * into `array`.
+ * @example
+ *
+ * var objects = [{ 'x': 4 }, { 'x': 5 }];
+ *
+ * _.sortedLastIndexBy(objects, { 'x': 4 }, function(o) { return o.x; });
+ * // => 1
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.sortedLastIndexBy(objects, { 'x': 4 }, 'x');
+ * // => 1
+ */function sortedLastIndexBy(array,value,iteratee){return baseSortedIndexBy(array,value,getIteratee(iteratee,2),true);}/**
+ * This method is like `_.lastIndexOf` except that it performs a binary
+ * search on a sorted `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to search for.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ * @example
+ *
+ * _.sortedLastIndexOf([4, 5, 5, 5, 6], 5);
+ * // => 3
+ */function sortedLastIndexOf(array,value){var length=array==null?0:array.length;if(length){var index=baseSortedIndex(array,value,true)-1;if(eq(array[index],value)){return index;}}return-1;}/**
+ * This method is like `_.uniq` except that it's designed and optimized
+ * for sorted arrays.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @returns {Array} Returns the new duplicate free array.
+ * @example
+ *
+ * _.sortedUniq([1, 1, 2]);
+ * // => [1, 2]
+ */function sortedUniq(array){return array&&array.length?baseSortedUniq(array):[];}/**
+ * This method is like `_.uniqBy` except that it's designed and optimized
+ * for sorted arrays.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {Function} [iteratee] The iteratee invoked per element.
+ * @returns {Array} Returns the new duplicate free array.
+ * @example
+ *
+ * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor);
+ * // => [1.1, 2.3]
+ */function sortedUniqBy(array,iteratee){return array&&array.length?baseSortedUniq(array,getIteratee(iteratee,2)):[];}/**
+ * Gets all but the first element of `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.tail([1, 2, 3]);
+ * // => [2, 3]
+ */function tail(array){var length=array==null?0:array.length;return length?baseSlice(array,1,length):[];}/**
+ * Creates a slice of `array` with `n` elements taken from the beginning.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {number} [n=1] The number of elements to take.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.take([1, 2, 3]);
+ * // => [1]
+ *
+ * _.take([1, 2, 3], 2);
+ * // => [1, 2]
+ *
+ * _.take([1, 2, 3], 5);
+ * // => [1, 2, 3]
+ *
+ * _.take([1, 2, 3], 0);
+ * // => []
+ */function take(array,n,guard){if(!(array&&array.length)){return[];}n=guard||n===undefined?1:toInteger(n);return baseSlice(array,0,n<0?0:n);}/**
+ * Creates a slice of `array` with `n` elements taken from the end.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {number} [n=1] The number of elements to take.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.takeRight([1, 2, 3]);
+ * // => [3]
+ *
+ * _.takeRight([1, 2, 3], 2);
+ * // => [2, 3]
+ *
+ * _.takeRight([1, 2, 3], 5);
+ * // => [1, 2, 3]
+ *
+ * _.takeRight([1, 2, 3], 0);
+ * // => []
+ */function takeRight(array,n,guard){var length=array==null?0:array.length;if(!length){return[];}n=guard||n===undefined?1:toInteger(n);n=length-n;return baseSlice(array,n<0?0:n,length);}/**
+ * Creates a slice of `array` with elements taken from the end. Elements are
+ * taken until `predicate` returns falsey. The predicate is invoked with
+ * three arguments: (value, index, array).
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'active': true },
+ * { 'user': 'fred', 'active': false },
+ * { 'user': 'pebbles', 'active': false }
+ * ];
+ *
+ * _.takeRightWhile(users, function(o) { return !o.active; });
+ * // => objects for ['fred', 'pebbles']
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.takeRightWhile(users, { 'user': 'pebbles', 'active': false });
+ * // => objects for ['pebbles']
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.takeRightWhile(users, ['active', false]);
+ * // => objects for ['fred', 'pebbles']
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.takeRightWhile(users, 'active');
+ * // => []
+ */function takeRightWhile(array,predicate){return array&&array.length?baseWhile(array,getIteratee(predicate,3),false,true):[];}/**
+ * Creates a slice of `array` with elements taken from the beginning. Elements
+ * are taken until `predicate` returns falsey. The predicate is invoked with
+ * three arguments: (value, index, array).
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'active': false },
+ * { 'user': 'fred', 'active': false },
+ * { 'user': 'pebbles', 'active': true }
+ * ];
+ *
+ * _.takeWhile(users, function(o) { return !o.active; });
+ * // => objects for ['barney', 'fred']
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.takeWhile(users, { 'user': 'barney', 'active': false });
+ * // => objects for ['barney']
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.takeWhile(users, ['active', false]);
+ * // => objects for ['barney', 'fred']
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.takeWhile(users, 'active');
+ * // => []
+ */function takeWhile(array,predicate){return array&&array.length?baseWhile(array,getIteratee(predicate,3)):[];}/**
+ * Creates an array of unique values, in order, from all given arrays using
+ * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @returns {Array} Returns the new array of combined values.
+ * @example
+ *
+ * _.union([2], [1, 2]);
+ * // => [2, 1]
+ */var union=baseRest(function(arrays){return baseUniq(baseFlatten(arrays,1,isArrayLikeObject,true));});/**
+ * This method is like `_.union` except that it accepts `iteratee` which is
+ * invoked for each element of each `arrays` to generate the criterion by
+ * which uniqueness is computed. Result values are chosen from the first
+ * array in which the value occurs. The iteratee is invoked with one argument:
+ * (value).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {Array} Returns the new array of combined values.
+ * @example
+ *
+ * _.unionBy([2.1], [1.2, 2.3], Math.floor);
+ * // => [2.1, 1.2]
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.unionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
+ * // => [{ 'x': 1 }, { 'x': 2 }]
+ */var unionBy=baseRest(function(arrays){var iteratee=last(arrays);if(isArrayLikeObject(iteratee)){iteratee=undefined;}return baseUniq(baseFlatten(arrays,1,isArrayLikeObject,true),getIteratee(iteratee,2));});/**
+ * This method is like `_.union` except that it accepts `comparator` which
+ * is invoked to compare elements of `arrays`. Result values are chosen from
+ * the first array in which the value occurs. The comparator is invoked
+ * with two arguments: (arrVal, othVal).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new array of combined values.
+ * @example
+ *
+ * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
+ * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
+ *
+ * _.unionWith(objects, others, _.isEqual);
+ * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
+ */var unionWith=baseRest(function(arrays){var comparator=last(arrays);comparator=typeof comparator=='function'?comparator:undefined;return baseUniq(baseFlatten(arrays,1,isArrayLikeObject,true),undefined,comparator);});/**
+ * Creates a duplicate-free version of an array, using
+ * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons, in which only the first occurrence of each element
+ * is kept. The order of result values is determined by the order they occur
+ * in the array.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @returns {Array} Returns the new duplicate free array.
+ * @example
+ *
+ * _.uniq([2, 1, 2]);
+ * // => [2, 1]
+ */function uniq(array){return array&&array.length?baseUniq(array):[];}/**
+ * This method is like `_.uniq` except that it accepts `iteratee` which is
+ * invoked for each element in `array` to generate the criterion by which
+ * uniqueness is computed. The order of result values is determined by the
+ * order they occur in the array. The iteratee is invoked with one argument:
+ * (value).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {Array} Returns the new duplicate free array.
+ * @example
+ *
+ * _.uniqBy([2.1, 1.2, 2.3], Math.floor);
+ * // => [2.1, 1.2]
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
+ * // => [{ 'x': 1 }, { 'x': 2 }]
+ */function uniqBy(array,iteratee){return array&&array.length?baseUniq(array,getIteratee(iteratee,2)):[];}/**
+ * This method is like `_.uniq` except that it accepts `comparator` which
+ * is invoked to compare elements of `array`. The order of result values is
+ * determined by the order they occur in the array.The comparator is invoked
+ * with two arguments: (arrVal, othVal).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new duplicate free array.
+ * @example
+ *
+ * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];
+ *
+ * _.uniqWith(objects, _.isEqual);
+ * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]
+ */function uniqWith(array,comparator){comparator=typeof comparator=='function'?comparator:undefined;return array&&array.length?baseUniq(array,undefined,comparator):[];}/**
+ * This method is like `_.zip` except that it accepts an array of grouped
+ * elements and creates an array regrouping the elements to their pre-zip
+ * configuration.
+ *
+ * @static
+ * @memberOf _
+ * @since 1.2.0
+ * @category Array
+ * @param {Array} array The array of grouped elements to process.
+ * @returns {Array} Returns the new array of regrouped elements.
+ * @example
+ *
+ * var zipped = _.zip(['a', 'b'], [1, 2], [true, false]);
+ * // => [['a', 1, true], ['b', 2, false]]
+ *
+ * _.unzip(zipped);
+ * // => [['a', 'b'], [1, 2], [true, false]]
+ */function unzip(array){if(!(array&&array.length)){return[];}var length=0;array=arrayFilter(array,function(group){if(isArrayLikeObject(group)){length=nativeMax(group.length,length);return true;}});return baseTimes(length,function(index){return arrayMap(array,baseProperty(index));});}/**
+ * This method is like `_.unzip` except that it accepts `iteratee` to specify
+ * how regrouped values should be combined. The iteratee is invoked with the
+ * elements of each group: (...group).
+ *
+ * @static
+ * @memberOf _
+ * @since 3.8.0
+ * @category Array
+ * @param {Array} array The array of grouped elements to process.
+ * @param {Function} [iteratee=_.identity] The function to combine
+ * regrouped values.
+ * @returns {Array} Returns the new array of regrouped elements.
+ * @example
+ *
+ * var zipped = _.zip([1, 2], [10, 20], [100, 200]);
+ * // => [[1, 10, 100], [2, 20, 200]]
+ *
+ * _.unzipWith(zipped, _.add);
+ * // => [3, 30, 300]
+ */function unzipWith(array,iteratee){if(!(array&&array.length)){return[];}var result=unzip(array);if(iteratee==null){return result;}return arrayMap(result,function(group){return apply(iteratee,undefined,group);});}/**
+ * Creates an array excluding all given values using
+ * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons.
+ *
+ * **Note:** Unlike `_.pull`, this method returns a new array.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {...*} [values] The values to exclude.
+ * @returns {Array} Returns the new array of filtered values.
+ * @see _.difference, _.xor
+ * @example
+ *
+ * _.without([2, 1, 2, 3], 1, 2);
+ * // => [3]
+ */var without=baseRest(function(array,values){return isArrayLikeObject(array)?baseDifference(array,values):[];});/**
+ * Creates an array of unique values that is the
+ * [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference)
+ * of the given arrays. The order of result values is determined by the order
+ * they occur in the arrays.
+ *
+ * @static
+ * @memberOf _
+ * @since 2.4.0
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @returns {Array} Returns the new array of filtered values.
+ * @see _.difference, _.without
+ * @example
+ *
+ * _.xor([2, 1], [2, 3]);
+ * // => [1, 3]
+ */var xor=baseRest(function(arrays){return baseXor(arrayFilter(arrays,isArrayLikeObject));});/**
+ * This method is like `_.xor` except that it accepts `iteratee` which is
+ * invoked for each element of each `arrays` to generate the criterion by
+ * which by which they're compared. The order of result values is determined
+ * by the order they occur in the arrays. The iteratee is invoked with one
+ * argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {Array} Returns the new array of filtered values.
+ * @example
+ *
+ * _.xorBy([2.1, 1.2], [2.3, 3.4], Math.floor);
+ * // => [1.2, 3.4]
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
+ * // => [{ 'x': 2 }]
+ */var xorBy=baseRest(function(arrays){var iteratee=last(arrays);if(isArrayLikeObject(iteratee)){iteratee=undefined;}return baseXor(arrayFilter(arrays,isArrayLikeObject),getIteratee(iteratee,2));});/**
+ * This method is like `_.xor` except that it accepts `comparator` which is
+ * invoked to compare elements of `arrays`. The order of result values is
+ * determined by the order they occur in the arrays. The comparator is invoked
+ * with two arguments: (arrVal, othVal).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new array of filtered values.
+ * @example
+ *
+ * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
+ * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
+ *
+ * _.xorWith(objects, others, _.isEqual);
+ * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
+ */var xorWith=baseRest(function(arrays){var comparator=last(arrays);comparator=typeof comparator=='function'?comparator:undefined;return baseXor(arrayFilter(arrays,isArrayLikeObject),undefined,comparator);});/**
+ * Creates an array of grouped elements, the first of which contains the
+ * first elements of the given arrays, the second of which contains the
+ * second elements of the given arrays, and so on.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {...Array} [arrays] The arrays to process.
+ * @returns {Array} Returns the new array of grouped elements.
+ * @example
+ *
+ * _.zip(['a', 'b'], [1, 2], [true, false]);
+ * // => [['a', 1, true], ['b', 2, false]]
+ */var zip=baseRest(unzip);/**
+ * This method is like `_.fromPairs` except that it accepts two arrays,
+ * one of property identifiers and one of corresponding values.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.4.0
+ * @category Array
+ * @param {Array} [props=[]] The property identifiers.
+ * @param {Array} [values=[]] The property values.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * _.zipObject(['a', 'b'], [1, 2]);
+ * // => { 'a': 1, 'b': 2 }
+ */function zipObject(props,values){return baseZipObject(props||[],values||[],assignValue);}/**
+ * This method is like `_.zipObject` except that it supports property paths.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.1.0
+ * @category Array
+ * @param {Array} [props=[]] The property identifiers.
+ * @param {Array} [values=[]] The property values.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * _.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]);
+ * // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } }
+ */function zipObjectDeep(props,values){return baseZipObject(props||[],values||[],baseSet);}/**
+ * This method is like `_.zip` except that it accepts `iteratee` to specify
+ * how grouped values should be combined. The iteratee is invoked with the
+ * elements of each group: (...group).
+ *
+ * @static
+ * @memberOf _
+ * @since 3.8.0
+ * @category Array
+ * @param {...Array} [arrays] The arrays to process.
+ * @param {Function} [iteratee=_.identity] The function to combine
+ * grouped values.
+ * @returns {Array} Returns the new array of grouped elements.
+ * @example
+ *
+ * _.zipWith([1, 2], [10, 20], [100, 200], function(a, b, c) {
+ * return a + b + c;
+ * });
+ * // => [111, 222]
+ */var zipWith=baseRest(function(arrays){var length=arrays.length,iteratee=length>1?arrays[length-1]:undefined;iteratee=typeof iteratee=='function'?(arrays.pop(),iteratee):undefined;return unzipWith(arrays,iteratee);});/*------------------------------------------------------------------------*/ /**
+ * Creates a `lodash` wrapper instance that wraps `value` with explicit method
+ * chain sequences enabled. The result of such sequences must be unwrapped
+ * with `_#value`.
+ *
+ * @static
+ * @memberOf _
+ * @since 1.3.0
+ * @category Seq
+ * @param {*} value The value to wrap.
+ * @returns {Object} Returns the new `lodash` wrapper instance.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36 },
+ * { 'user': 'fred', 'age': 40 },
+ * { 'user': 'pebbles', 'age': 1 }
+ * ];
+ *
+ * var youngest = _
+ * .chain(users)
+ * .sortBy('age')
+ * .map(function(o) {
+ * return o.user + ' is ' + o.age;
+ * })
+ * .head()
+ * .value();
+ * // => 'pebbles is 1'
+ */function chain(value){var result=lodash(value);result.__chain__=true;return result;}/**
+ * This method invokes `interceptor` and returns `value`. The interceptor
+ * is invoked with one argument; (value). The purpose of this method is to
+ * "tap into" a method chain sequence in order to modify intermediate results.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Seq
+ * @param {*} value The value to provide to `interceptor`.
+ * @param {Function} interceptor The function to invoke.
+ * @returns {*} Returns `value`.
+ * @example
+ *
+ * _([1, 2, 3])
+ * .tap(function(array) {
+ * // Mutate input array.
+ * array.pop();
+ * })
+ * .reverse()
+ * .value();
+ * // => [2, 1]
+ */function tap(value,interceptor){interceptor(value);return value;}/**
+ * This method is like `_.tap` except that it returns the result of `interceptor`.
+ * The purpose of this method is to "pass thru" values replacing intermediate
+ * results in a method chain sequence.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Seq
+ * @param {*} value The value to provide to `interceptor`.
+ * @param {Function} interceptor The function to invoke.
+ * @returns {*} Returns the result of `interceptor`.
+ * @example
+ *
+ * _(' abc ')
+ * .chain()
+ * .trim()
+ * .thru(function(value) {
+ * return [value];
+ * })
+ * .value();
+ * // => ['abc']
+ */function thru(value,interceptor){return interceptor(value);}/**
+ * This method is the wrapper version of `_.at`.
+ *
+ * @name at
+ * @memberOf _
+ * @since 1.0.0
+ * @category Seq
+ * @param {...(string|string[])} [paths] The property paths to pick.
+ * @returns {Object} Returns the new `lodash` wrapper instance.
+ * @example
+ *
+ * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
+ *
+ * _(object).at(['a[0].b.c', 'a[1]']).value();
+ * // => [3, 4]
+ */var wrapperAt=flatRest(function(paths){var length=paths.length,start=length?paths[0]:0,value=this.__wrapped__,interceptor=function(object){return baseAt(object,paths);};if(length>1||this.__actions__.length||!(value instanceof LazyWrapper)||!isIndex(start)){return this.thru(interceptor);}value=value.slice(start,+start+(length?1:0));value.__actions__.push({'func':thru,'args':[interceptor],'thisArg':undefined});return new LodashWrapper(value,this.__chain__).thru(function(array){if(length&&!array.length){array.push(undefined);}return array;});});/**
+ * Creates a `lodash` wrapper instance with explicit method chain sequences enabled.
+ *
+ * @name chain
+ * @memberOf _
+ * @since 0.1.0
+ * @category Seq
+ * @returns {Object} Returns the new `lodash` wrapper instance.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36 },
+ * { 'user': 'fred', 'age': 40 }
+ * ];
+ *
+ * // A sequence without explicit chaining.
+ * _(users).head();
+ * // => { 'user': 'barney', 'age': 36 }
+ *
+ * // A sequence with explicit chaining.
+ * _(users)
+ * .chain()
+ * .head()
+ * .pick('user')
+ * .value();
+ * // => { 'user': 'barney' }
+ */function wrapperChain(){return chain(this);}/**
+ * Executes the chain sequence and returns the wrapped result.
+ *
+ * @name commit
+ * @memberOf _
+ * @since 3.2.0
+ * @category Seq
+ * @returns {Object} Returns the new `lodash` wrapper instance.
+ * @example
+ *
+ * var array = [1, 2];
+ * var wrapped = _(array).push(3);
+ *
+ * console.log(array);
+ * // => [1, 2]
+ *
+ * wrapped = wrapped.commit();
+ * console.log(array);
+ * // => [1, 2, 3]
+ *
+ * wrapped.last();
+ * // => 3
+ *
+ * console.log(array);
+ * // => [1, 2, 3]
+ */function wrapperCommit(){return new LodashWrapper(this.value(),this.__chain__);}/**
+ * Gets the next value on a wrapped object following the
+ * [iterator protocol](https://mdn.io/iteration_protocols#iterator).
+ *
+ * @name next
+ * @memberOf _
+ * @since 4.0.0
+ * @category Seq
+ * @returns {Object} Returns the next iterator value.
+ * @example
+ *
+ * var wrapped = _([1, 2]);
+ *
+ * wrapped.next();
+ * // => { 'done': false, 'value': 1 }
+ *
+ * wrapped.next();
+ * // => { 'done': false, 'value': 2 }
+ *
+ * wrapped.next();
+ * // => { 'done': true, 'value': undefined }
+ */function wrapperNext(){if(this.__values__===undefined){this.__values__=toArray(this.value());}var done=this.__index__>=this.__values__.length,value=done?undefined:this.__values__[this.__index__++];return{'done':done,'value':value};}/**
+ * Enables the wrapper to be iterable.
+ *
+ * @name Symbol.iterator
+ * @memberOf _
+ * @since 4.0.0
+ * @category Seq
+ * @returns {Object} Returns the wrapper object.
+ * @example
+ *
+ * var wrapped = _([1, 2]);
+ *
+ * wrapped[Symbol.iterator]() === wrapped;
+ * // => true
+ *
+ * Array.from(wrapped);
+ * // => [1, 2]
+ */function wrapperToIterator(){return this;}/**
+ * Creates a clone of the chain sequence planting `value` as the wrapped value.
+ *
+ * @name plant
+ * @memberOf _
+ * @since 3.2.0
+ * @category Seq
+ * @param {*} value The value to plant.
+ * @returns {Object} Returns the new `lodash` wrapper instance.
+ * @example
+ *
+ * function square(n) {
+ * return n * n;
+ * }
+ *
+ * var wrapped = _([1, 2]).map(square);
+ * var other = wrapped.plant([3, 4]);
+ *
+ * other.value();
+ * // => [9, 16]
+ *
+ * wrapped.value();
+ * // => [1, 4]
+ */function wrapperPlant(value){var result,parent=this;while(parent instanceof baseLodash){var clone=wrapperClone(parent);clone.__index__=0;clone.__values__=undefined;if(result){previous.__wrapped__=clone;}else{result=clone;}var previous=clone;parent=parent.__wrapped__;}previous.__wrapped__=value;return result;}/**
+ * This method is the wrapper version of `_.reverse`.
+ *
+ * **Note:** This method mutates the wrapped array.
+ *
+ * @name reverse
+ * @memberOf _
+ * @since 0.1.0
+ * @category Seq
+ * @returns {Object} Returns the new `lodash` wrapper instance.
+ * @example
+ *
+ * var array = [1, 2, 3];
+ *
+ * _(array).reverse().value()
+ * // => [3, 2, 1]
+ *
+ * console.log(array);
+ * // => [3, 2, 1]
+ */function wrapperReverse(){var value=this.__wrapped__;if(value instanceof LazyWrapper){var wrapped=value;if(this.__actions__.length){wrapped=new LazyWrapper(this);}wrapped=wrapped.reverse();wrapped.__actions__.push({'func':thru,'args':[reverse],'thisArg':undefined});return new LodashWrapper(wrapped,this.__chain__);}return this.thru(reverse);}/**
+ * Executes the chain sequence to resolve the unwrapped value.
+ *
+ * @name value
+ * @memberOf _
+ * @since 0.1.0
+ * @alias toJSON, valueOf
+ * @category Seq
+ * @returns {*} Returns the resolved unwrapped value.
+ * @example
+ *
+ * _([1, 2, 3]).value();
+ * // => [1, 2, 3]
+ */function wrapperValue(){return baseWrapperValue(this.__wrapped__,this.__actions__);}/*------------------------------------------------------------------------*/ /**
+ * Creates an object composed of keys generated from the results of running
+ * each element of `collection` thru `iteratee`. The corresponding value of
+ * each key is the number of times the key was returned by `iteratee`. The
+ * iteratee is invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @since 0.5.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The iteratee to transform keys.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * _.countBy([6.1, 4.2, 6.3], Math.floor);
+ * // => { '4': 1, '6': 2 }
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.countBy(['one', 'two', 'three'], 'length');
+ * // => { '3': 2, '5': 1 }
+ */var countBy=createAggregator(function(result,value,key){if(hasOwnProperty.call(result,key)){++result[key];}else{baseAssignValue(result,key,1);}});/**
+ * Checks if `predicate` returns truthy for **all** elements of `collection`.
+ * Iteration is stopped once `predicate` returns falsey. The predicate is
+ * invoked with three arguments: (value, index|key, collection).
+ *
+ * **Note:** This method returns `true` for
+ * [empty collections](https://en.wikipedia.org/wiki/Empty_set) because
+ * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of
+ * elements of empty collections.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {boolean} Returns `true` if all elements pass the predicate check,
+ * else `false`.
+ * @example
+ *
+ * _.every([true, 1, null, 'yes'], Boolean);
+ * // => false
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': false },
+ * { 'user': 'fred', 'age': 40, 'active': false }
+ * ];
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.every(users, { 'user': 'barney', 'active': false });
+ * // => false
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.every(users, ['active', false]);
+ * // => true
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.every(users, 'active');
+ * // => false
+ */function every(collection,predicate,guard){var func=isArray(collection)?arrayEvery:baseEvery;if(guard&&isIterateeCall(collection,predicate,guard)){predicate=undefined;}return func(collection,getIteratee(predicate,3));}/**
+ * Iterates over elements of `collection`, returning an array of all elements
+ * `predicate` returns truthy for. The predicate is invoked with three
+ * arguments: (value, index|key, collection).
+ *
+ * **Note:** Unlike `_.remove`, this method returns a new array.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the new filtered array.
+ * @see _.reject
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': true },
+ * { 'user': 'fred', 'age': 40, 'active': false }
+ * ];
+ *
+ * _.filter(users, function(o) { return !o.active; });
+ * // => objects for ['fred']
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.filter(users, { 'age': 36, 'active': true });
+ * // => objects for ['barney']
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.filter(users, ['active', false]);
+ * // => objects for ['fred']
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.filter(users, 'active');
+ * // => objects for ['barney']
+ *
+ * // Combining several predicates using `_.overEvery` or `_.overSome`.
+ * _.filter(users, _.overSome([{ 'age': 36 }, ['age', 40]]));
+ * // => objects for ['fred', 'barney']
+ */function filter(collection,predicate){var func=isArray(collection)?arrayFilter:baseFilter;return func(collection,getIteratee(predicate,3));}/**
+ * Iterates over elements of `collection`, returning the first element
+ * `predicate` returns truthy for. The predicate is invoked with three
+ * arguments: (value, index|key, collection).
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to inspect.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {*} Returns the matched element, else `undefined`.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': true },
+ * { 'user': 'fred', 'age': 40, 'active': false },
+ * { 'user': 'pebbles', 'age': 1, 'active': true }
+ * ];
+ *
+ * _.find(users, function(o) { return o.age < 40; });
+ * // => object for 'barney'
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.find(users, { 'age': 1, 'active': true });
+ * // => object for 'pebbles'
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.find(users, ['active', false]);
+ * // => object for 'fred'
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.find(users, 'active');
+ * // => object for 'barney'
+ */var find=createFind(findIndex);/**
+ * This method is like `_.find` except that it iterates over elements of
+ * `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @since 2.0.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to inspect.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @param {number} [fromIndex=collection.length-1] The index to search from.
+ * @returns {*} Returns the matched element, else `undefined`.
+ * @example
+ *
+ * _.findLast([1, 2, 3, 4], function(n) {
+ * return n % 2 == 1;
+ * });
+ * // => 3
+ */var findLast=createFind(findLastIndex);/**
+ * Creates a flattened array of values by running each element in `collection`
+ * thru `iteratee` and flattening the mapped results. The iteratee is invoked
+ * with three arguments: (value, index|key, collection).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the new flattened array.
+ * @example
+ *
+ * function duplicate(n) {
+ * return [n, n];
+ * }
+ *
+ * _.flatMap([1, 2], duplicate);
+ * // => [1, 1, 2, 2]
+ */function flatMap(collection,iteratee){return baseFlatten(map(collection,iteratee),1);}/**
+ * This method is like `_.flatMap` except that it recursively flattens the
+ * mapped results.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.7.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the new flattened array.
+ * @example
+ *
+ * function duplicate(n) {
+ * return [[[n, n]]];
+ * }
+ *
+ * _.flatMapDeep([1, 2], duplicate);
+ * // => [1, 1, 2, 2]
+ */function flatMapDeep(collection,iteratee){return baseFlatten(map(collection,iteratee),INFINITY);}/**
+ * This method is like `_.flatMap` except that it recursively flattens the
+ * mapped results up to `depth` times.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.7.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @param {number} [depth=1] The maximum recursion depth.
+ * @returns {Array} Returns the new flattened array.
+ * @example
+ *
+ * function duplicate(n) {
+ * return [[[n, n]]];
+ * }
+ *
+ * _.flatMapDepth([1, 2], duplicate, 2);
+ * // => [[1, 1], [2, 2]]
+ */function flatMapDepth(collection,iteratee,depth){depth=depth===undefined?1:toInteger(depth);return baseFlatten(map(collection,iteratee),depth);}/**
+ * Iterates over elements of `collection` and invokes `iteratee` for each element.
+ * The iteratee is invoked with three arguments: (value, index|key, collection).
+ * Iteratee functions may exit iteration early by explicitly returning `false`.
+ *
+ * **Note:** As with other "Collections" methods, objects with a "length"
+ * property are iterated like arrays. To avoid this behavior use `_.forIn`
+ * or `_.forOwn` for object iteration.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @alias each
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @returns {Array|Object} Returns `collection`.
+ * @see _.forEachRight
+ * @example
+ *
+ * _.forEach([1, 2], function(value) {
+ * console.log(value);
+ * });
+ * // => Logs `1` then `2`.
+ *
+ * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) {
+ * console.log(key);
+ * });
+ * // => Logs 'a' then 'b' (iteration order is not guaranteed).
+ */function forEach(collection,iteratee){var func=isArray(collection)?arrayEach:baseEach;return func(collection,getIteratee(iteratee,3));}/**
+ * This method is like `_.forEach` except that it iterates over elements of
+ * `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @since 2.0.0
+ * @alias eachRight
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @returns {Array|Object} Returns `collection`.
+ * @see _.forEach
+ * @example
+ *
+ * _.forEachRight([1, 2], function(value) {
+ * console.log(value);
+ * });
+ * // => Logs `2` then `1`.
+ */function forEachRight(collection,iteratee){var func=isArray(collection)?arrayEachRight:baseEachRight;return func(collection,getIteratee(iteratee,3));}/**
+ * Creates an object composed of keys generated from the results of running
+ * each element of `collection` thru `iteratee`. The order of grouped values
+ * is determined by the order they occur in `collection`. The corresponding
+ * value of each key is an array of elements responsible for generating the
+ * key. The iteratee is invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The iteratee to transform keys.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * _.groupBy([6.1, 4.2, 6.3], Math.floor);
+ * // => { '4': [4.2], '6': [6.1, 6.3] }
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.groupBy(['one', 'two', 'three'], 'length');
+ * // => { '3': ['one', 'two'], '5': ['three'] }
+ */var groupBy=createAggregator(function(result,value,key){if(hasOwnProperty.call(result,key)){result[key].push(value);}else{baseAssignValue(result,key,[value]);}});/**
+ * Checks if `value` is in `collection`. If `collection` is a string, it's
+ * checked for a substring of `value`, otherwise
+ * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * is used for equality comparisons. If `fromIndex` is negative, it's used as
+ * the offset from the end of `collection`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to inspect.
+ * @param {*} value The value to search for.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.
+ * @returns {boolean} Returns `true` if `value` is found, else `false`.
+ * @example
+ *
+ * _.includes([1, 2, 3], 1);
+ * // => true
+ *
+ * _.includes([1, 2, 3], 1, 2);
+ * // => false
+ *
+ * _.includes({ 'a': 1, 'b': 2 }, 1);
+ * // => true
+ *
+ * _.includes('abcd', 'bc');
+ * // => true
+ */function includes(collection,value,fromIndex,guard){collection=isArrayLike(collection)?collection:values(collection);fromIndex=fromIndex&&!guard?toInteger(fromIndex):0;var length=collection.length;if(fromIndex<0){fromIndex=nativeMax(length+fromIndex,0);}return isString(collection)?fromIndex<=length&&collection.indexOf(value,fromIndex)>-1:!!length&&baseIndexOf(collection,value,fromIndex)>-1;}/**
+ * Invokes the method at `path` of each element in `collection`, returning
+ * an array of the results of each invoked method. Any additional arguments
+ * are provided to each invoked method. If `path` is a function, it's invoked
+ * for, and `this` bound to, each element in `collection`.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Array|Function|string} path The path of the method to invoke or
+ * the function invoked per iteration.
+ * @param {...*} [args] The arguments to invoke each method with.
+ * @returns {Array} Returns the array of results.
+ * @example
+ *
+ * _.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort');
+ * // => [[1, 5, 7], [1, 2, 3]]
+ *
+ * _.invokeMap([123, 456], String.prototype.split, '');
+ * // => [['1', '2', '3'], ['4', '5', '6']]
+ */var invokeMap=baseRest(function(collection,path,args){var index=-1,isFunc=typeof path=='function',result=isArrayLike(collection)?Array(collection.length):[];baseEach(collection,function(value){result[++index]=isFunc?apply(path,value,args):baseInvoke(value,path,args);});return result;});/**
+ * Creates an object composed of keys generated from the results of running
+ * each element of `collection` thru `iteratee`. The corresponding value of
+ * each key is the last element responsible for generating the key. The
+ * iteratee is invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The iteratee to transform keys.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * var array = [
+ * { 'dir': 'left', 'code': 97 },
+ * { 'dir': 'right', 'code': 100 }
+ * ];
+ *
+ * _.keyBy(array, function(o) {
+ * return String.fromCharCode(o.code);
+ * });
+ * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+ *
+ * _.keyBy(array, 'dir');
+ * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
+ */var keyBy=createAggregator(function(result,value,key){baseAssignValue(result,key,value);});/**
+ * Creates an array of values by running each element in `collection` thru
+ * `iteratee`. The iteratee is invoked with three arguments:
+ * (value, index|key, collection).
+ *
+ * Many lodash methods are guarded to work as iteratees for methods like
+ * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
+ *
+ * The guarded methods are:
+ * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`,
+ * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`,
+ * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`,
+ * `template`, `trim`, `trimEnd`, `trimStart`, and `words`
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the new mapped array.
+ * @example
+ *
+ * function square(n) {
+ * return n * n;
+ * }
+ *
+ * _.map([4, 8], square);
+ * // => [16, 64]
+ *
+ * _.map({ 'a': 4, 'b': 8 }, square);
+ * // => [16, 64] (iteration order is not guaranteed)
+ *
+ * var users = [
+ * { 'user': 'barney' },
+ * { 'user': 'fred' }
+ * ];
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.map(users, 'user');
+ * // => ['barney', 'fred']
+ */function map(collection,iteratee){var func=isArray(collection)?arrayMap:baseMap;return func(collection,getIteratee(iteratee,3));}/**
+ * This method is like `_.sortBy` except that it allows specifying the sort
+ * orders of the iteratees to sort by. If `orders` is unspecified, all values
+ * are sorted in ascending order. Otherwise, specify an order of "desc" for
+ * descending or "asc" for ascending sort order of corresponding values.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Array[]|Function[]|Object[]|string[]} [iteratees=[_.identity]]
+ * The iteratees to sort by.
+ * @param {string[]} [orders] The sort orders of `iteratees`.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.
+ * @returns {Array} Returns the new sorted array.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'fred', 'age': 48 },
+ * { 'user': 'barney', 'age': 34 },
+ * { 'user': 'fred', 'age': 40 },
+ * { 'user': 'barney', 'age': 36 }
+ * ];
+ *
+ * // Sort by `user` in ascending order and by `age` in descending order.
+ * _.orderBy(users, ['user', 'age'], ['asc', 'desc']);
+ * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
+ */function orderBy(collection,iteratees,orders,guard){if(collection==null){return[];}if(!isArray(iteratees)){iteratees=iteratees==null?[]:[iteratees];}orders=guard?undefined:orders;if(!isArray(orders)){orders=orders==null?[]:[orders];}return baseOrderBy(collection,iteratees,orders);}/**
+ * Creates an array of elements split into two groups, the first of which
+ * contains elements `predicate` returns truthy for, the second of which
+ * contains elements `predicate` returns falsey for. The predicate is
+ * invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the array of grouped elements.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': false },
+ * { 'user': 'fred', 'age': 40, 'active': true },
+ * { 'user': 'pebbles', 'age': 1, 'active': false }
+ * ];
+ *
+ * _.partition(users, function(o) { return o.active; });
+ * // => objects for [['fred'], ['barney', 'pebbles']]
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.partition(users, { 'age': 1, 'active': false });
+ * // => objects for [['pebbles'], ['barney', 'fred']]
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.partition(users, ['active', false]);
+ * // => objects for [['barney', 'pebbles'], ['fred']]
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.partition(users, 'active');
+ * // => objects for [['fred'], ['barney', 'pebbles']]
+ */var partition=createAggregator(function(result,value,key){result[key?0:1].push(value);},function(){return[[],[]];});/**
+ * Reduces `collection` to a value which is the accumulated result of running
+ * each element in `collection` thru `iteratee`, where each successive
+ * invocation is supplied the return value of the previous. If `accumulator`
+ * is not given, the first element of `collection` is used as the initial
+ * value. The iteratee is invoked with four arguments:
+ * (accumulator, value, index|key, collection).
+ *
+ * Many lodash methods are guarded to work as iteratees for methods like
+ * `_.reduce`, `_.reduceRight`, and `_.transform`.
+ *
+ * The guarded methods are:
+ * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`,
+ * and `sortBy`
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @param {*} [accumulator] The initial value.
+ * @returns {*} Returns the accumulated value.
+ * @see _.reduceRight
+ * @example
+ *
+ * _.reduce([1, 2], function(sum, n) {
+ * return sum + n;
+ * }, 0);
+ * // => 3
+ *
+ * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) {
+ * (result[value] || (result[value] = [])).push(key);
+ * return result;
+ * }, {});
+ * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed)
+ */function reduce(collection,iteratee,accumulator){var func=isArray(collection)?arrayReduce:baseReduce,initAccum=arguments.length<3;return func(collection,getIteratee(iteratee,4),accumulator,initAccum,baseEach);}/**
+ * This method is like `_.reduce` except that it iterates over elements of
+ * `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @param {*} [accumulator] The initial value.
+ * @returns {*} Returns the accumulated value.
+ * @see _.reduce
+ * @example
+ *
+ * var array = [[0, 1], [2, 3], [4, 5]];
+ *
+ * _.reduceRight(array, function(flattened, other) {
+ * return flattened.concat(other);
+ * }, []);
+ * // => [4, 5, 2, 3, 0, 1]
+ */function reduceRight(collection,iteratee,accumulator){var func=isArray(collection)?arrayReduceRight:baseReduce,initAccum=arguments.length<3;return func(collection,getIteratee(iteratee,4),accumulator,initAccum,baseEachRight);}/**
+ * The opposite of `_.filter`; this method returns the elements of `collection`
+ * that `predicate` does **not** return truthy for.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the new filtered array.
+ * @see _.filter
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': false },
+ * { 'user': 'fred', 'age': 40, 'active': true }
+ * ];
+ *
+ * _.reject(users, function(o) { return !o.active; });
+ * // => objects for ['fred']
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.reject(users, { 'age': 40, 'active': true });
+ * // => objects for ['barney']
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.reject(users, ['active', false]);
+ * // => objects for ['fred']
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.reject(users, 'active');
+ * // => objects for ['barney']
+ */function reject(collection,predicate){var func=isArray(collection)?arrayFilter:baseFilter;return func(collection,negate(getIteratee(predicate,3)));}/**
+ * Gets a random element from `collection`.
+ *
+ * @static
+ * @memberOf _
+ * @since 2.0.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to sample.
+ * @returns {*} Returns the random element.
+ * @example
+ *
+ * _.sample([1, 2, 3, 4]);
+ * // => 2
+ */function sample(collection){var func=isArray(collection)?arraySample:baseSample;return func(collection);}/**
+ * Gets `n` random elements at unique keys from `collection` up to the
+ * size of `collection`.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to sample.
+ * @param {number} [n=1] The number of elements to sample.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {Array} Returns the random elements.
+ * @example
+ *
+ * _.sampleSize([1, 2, 3], 2);
+ * // => [3, 1]
+ *
+ * _.sampleSize([1, 2, 3], 4);
+ * // => [2, 3, 1]
+ */function sampleSize(collection,n,guard){if(guard?isIterateeCall(collection,n,guard):n===undefined){n=1;}else{n=toInteger(n);}var func=isArray(collection)?arraySampleSize:baseSampleSize;return func(collection,n);}/**
+ * Creates an array of shuffled values, using a version of the
+ * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle).
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to shuffle.
+ * @returns {Array} Returns the new shuffled array.
+ * @example
+ *
+ * _.shuffle([1, 2, 3, 4]);
+ * // => [4, 1, 3, 2]
+ */function shuffle(collection){var func=isArray(collection)?arrayShuffle:baseShuffle;return func(collection);}/**
+ * Gets the size of `collection` by returning its length for array-like
+ * values or the number of own enumerable string keyed properties for objects.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to inspect.
+ * @returns {number} Returns the collection size.
+ * @example
+ *
+ * _.size([1, 2, 3]);
+ * // => 3
+ *
+ * _.size({ 'a': 1, 'b': 2 });
+ * // => 2
+ *
+ * _.size('pebbles');
+ * // => 7
+ */function size(collection){if(collection==null){return 0;}if(isArrayLike(collection)){return isString(collection)?stringSize(collection):collection.length;}var tag=getTag(collection);if(tag==mapTag||tag==setTag){return collection.size;}return baseKeys(collection).length;}/**
+ * Checks if `predicate` returns truthy for **any** element of `collection`.
+ * Iteration is stopped once `predicate` returns truthy. The predicate is
+ * invoked with three arguments: (value, index|key, collection).
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {boolean} Returns `true` if any element passes the predicate check,
+ * else `false`.
+ * @example
+ *
+ * _.some([null, 0, 'yes', false], Boolean);
+ * // => true
+ *
+ * var users = [
+ * { 'user': 'barney', 'active': true },
+ * { 'user': 'fred', 'active': false }
+ * ];
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.some(users, { 'user': 'barney', 'active': false });
+ * // => false
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.some(users, ['active', false]);
+ * // => true
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.some(users, 'active');
+ * // => true
+ */function some(collection,predicate,guard){var func=isArray(collection)?arraySome:baseSome;if(guard&&isIterateeCall(collection,predicate,guard)){predicate=undefined;}return func(collection,getIteratee(predicate,3));}/**
+ * Creates an array of elements, sorted in ascending order by the results of
+ * running each element in a collection thru each iteratee. This method
+ * performs a stable sort, that is, it preserves the original sort order of
+ * equal elements. The iteratees are invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {...(Function|Function[])} [iteratees=[_.identity]]
+ * The iteratees to sort by.
+ * @returns {Array} Returns the new sorted array.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'fred', 'age': 48 },
+ * { 'user': 'barney', 'age': 36 },
+ * { 'user': 'fred', 'age': 30 },
+ * { 'user': 'barney', 'age': 34 }
+ * ];
+ *
+ * _.sortBy(users, [function(o) { return o.user; }]);
+ * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 30]]
+ *
+ * _.sortBy(users, ['user', 'age']);
+ * // => objects for [['barney', 34], ['barney', 36], ['fred', 30], ['fred', 48]]
+ */var sortBy=baseRest(function(collection,iteratees){if(collection==null){return[];}var length=iteratees.length;if(length>1&&isIterateeCall(collection,iteratees[0],iteratees[1])){iteratees=[];}else if(length>2&&isIterateeCall(iteratees[0],iteratees[1],iteratees[2])){iteratees=[iteratees[0]];}return baseOrderBy(collection,baseFlatten(iteratees,1),[]);});/*------------------------------------------------------------------------*/ /**
+ * Gets the timestamp of the number of milliseconds that have elapsed since
+ * the Unix epoch (1 January 1970 00:00:00 UTC).
+ *
+ * @static
+ * @memberOf _
+ * @since 2.4.0
+ * @category Date
+ * @returns {number} Returns the timestamp.
+ * @example
+ *
+ * _.defer(function(stamp) {
+ * console.log(_.now() - stamp);
+ * }, _.now());
+ * // => Logs the number of milliseconds it took for the deferred invocation.
+ */var now=ctxNow||function(){return root.Date.now();};/*------------------------------------------------------------------------*/ /**
+ * The opposite of `_.before`; this method creates a function that invokes
+ * `func` once it's called `n` or more times.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {number} n The number of calls before `func` is invoked.
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * var saves = ['profile', 'settings'];
+ *
+ * var done = _.after(saves.length, function() {
+ * console.log('done saving!');
+ * });
+ *
+ * _.forEach(saves, function(type) {
+ * asyncSave({ 'type': type, 'complete': done });
+ * });
+ * // => Logs 'done saving!' after the two async saves have completed.
+ */function after(n,func){if(typeof func!='function'){throw new TypeError(FUNC_ERROR_TEXT);}n=toInteger(n);return function(){if(--n<1){return func.apply(this,arguments);}};}/**
+ * Creates a function that invokes `func`, with up to `n` arguments,
+ * ignoring any additional arguments.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Function
+ * @param {Function} func The function to cap arguments for.
+ * @param {number} [n=func.length] The arity cap.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {Function} Returns the new capped function.
+ * @example
+ *
+ * _.map(['6', '8', '10'], _.ary(parseInt, 1));
+ * // => [6, 8, 10]
+ */function ary(func,n,guard){n=guard?undefined:n;n=func&&n==null?func.length:n;return createWrap(func,WRAP_ARY_FLAG,undefined,undefined,undefined,undefined,n);}/**
+ * Creates a function that invokes `func`, with the `this` binding and arguments
+ * of the created function, while it's called less than `n` times. Subsequent
+ * calls to the created function return the result of the last `func` invocation.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Function
+ * @param {number} n The number of calls at which `func` is no longer invoked.
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * jQuery(element).on('click', _.before(5, addContactToList));
+ * // => Allows adding up to 4 contacts to the list.
+ */function before(n,func){var result;if(typeof func!='function'){throw new TypeError(FUNC_ERROR_TEXT);}n=toInteger(n);return function(){if(--n>0){result=func.apply(this,arguments);}if(n<=1){func=undefined;}return result;};}/**
+ * Creates a function that invokes `func` with the `this` binding of `thisArg`
+ * and `partials` prepended to the arguments it receives.
+ *
+ * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,
+ * may be used as a placeholder for partially applied arguments.
+ *
+ * **Note:** Unlike native `Function#bind`, this method doesn't set the "length"
+ * property of bound functions.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to bind.
+ * @param {*} thisArg The `this` binding of `func`.
+ * @param {...*} [partials] The arguments to be partially applied.
+ * @returns {Function} Returns the new bound function.
+ * @example
+ *
+ * function greet(greeting, punctuation) {
+ * return greeting + ' ' + this.user + punctuation;
+ * }
+ *
+ * var object = { 'user': 'fred' };
+ *
+ * var bound = _.bind(greet, object, 'hi');
+ * bound('!');
+ * // => 'hi fred!'
+ *
+ * // Bound with placeholders.
+ * var bound = _.bind(greet, object, _, '!');
+ * bound('hi');
+ * // => 'hi fred!'
+ */var bind=baseRest(function(func,thisArg,partials){var bitmask=WRAP_BIND_FLAG;if(partials.length){var holders=replaceHolders(partials,getHolder(bind));bitmask|=WRAP_PARTIAL_FLAG;}return createWrap(func,bitmask,thisArg,partials,holders);});/**
+ * Creates a function that invokes the method at `object[key]` with `partials`
+ * prepended to the arguments it receives.
+ *
+ * This method differs from `_.bind` by allowing bound functions to reference
+ * methods that may be redefined or don't yet exist. See
+ * [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern)
+ * for more details.
+ *
+ * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic
+ * builds, may be used as a placeholder for partially applied arguments.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.10.0
+ * @category Function
+ * @param {Object} object The object to invoke the method on.
+ * @param {string} key The key of the method.
+ * @param {...*} [partials] The arguments to be partially applied.
+ * @returns {Function} Returns the new bound function.
+ * @example
+ *
+ * var object = {
+ * 'user': 'fred',
+ * 'greet': function(greeting, punctuation) {
+ * return greeting + ' ' + this.user + punctuation;
+ * }
+ * };
+ *
+ * var bound = _.bindKey(object, 'greet', 'hi');
+ * bound('!');
+ * // => 'hi fred!'
+ *
+ * object.greet = function(greeting, punctuation) {
+ * return greeting + 'ya ' + this.user + punctuation;
+ * };
+ *
+ * bound('!');
+ * // => 'hiya fred!'
+ *
+ * // Bound with placeholders.
+ * var bound = _.bindKey(object, 'greet', _, '!');
+ * bound('hi');
+ * // => 'hiya fred!'
+ */var bindKey=baseRest(function(object,key,partials){var bitmask=WRAP_BIND_FLAG|WRAP_BIND_KEY_FLAG;if(partials.length){var holders=replaceHolders(partials,getHolder(bindKey));bitmask|=WRAP_PARTIAL_FLAG;}return createWrap(key,bitmask,object,partials,holders);});/**
+ * Creates a function that accepts arguments of `func` and either invokes
+ * `func` returning its result, if at least `arity` number of arguments have
+ * been provided, or returns a function that accepts the remaining `func`
+ * arguments, and so on. The arity of `func` may be specified if `func.length`
+ * is not sufficient.
+ *
+ * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds,
+ * may be used as a placeholder for provided arguments.
+ *
+ * **Note:** This method doesn't set the "length" property of curried functions.
+ *
+ * @static
+ * @memberOf _
+ * @since 2.0.0
+ * @category Function
+ * @param {Function} func The function to curry.
+ * @param {number} [arity=func.length] The arity of `func`.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {Function} Returns the new curried function.
+ * @example
+ *
+ * var abc = function(a, b, c) {
+ * return [a, b, c];
+ * };
+ *
+ * var curried = _.curry(abc);
+ *
+ * curried(1)(2)(3);
+ * // => [1, 2, 3]
+ *
+ * curried(1, 2)(3);
+ * // => [1, 2, 3]
+ *
+ * curried(1, 2, 3);
+ * // => [1, 2, 3]
+ *
+ * // Curried with placeholders.
+ * curried(1)(_, 3)(2);
+ * // => [1, 2, 3]
+ */function curry(func,arity,guard){arity=guard?undefined:arity;var result=createWrap(func,WRAP_CURRY_FLAG,undefined,undefined,undefined,undefined,undefined,arity);result.placeholder=curry.placeholder;return result;}/**
+ * This method is like `_.curry` except that arguments are applied to `func`
+ * in the manner of `_.partialRight` instead of `_.partial`.
+ *
+ * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic
+ * builds, may be used as a placeholder for provided arguments.
+ *
+ * **Note:** This method doesn't set the "length" property of curried functions.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Function
+ * @param {Function} func The function to curry.
+ * @param {number} [arity=func.length] The arity of `func`.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {Function} Returns the new curried function.
+ * @example
+ *
+ * var abc = function(a, b, c) {
+ * return [a, b, c];
+ * };
+ *
+ * var curried = _.curryRight(abc);
+ *
+ * curried(3)(2)(1);
+ * // => [1, 2, 3]
+ *
+ * curried(2, 3)(1);
+ * // => [1, 2, 3]
+ *
+ * curried(1, 2, 3);
+ * // => [1, 2, 3]
+ *
+ * // Curried with placeholders.
+ * curried(3)(1, _)(2);
+ * // => [1, 2, 3]
+ */function curryRight(func,arity,guard){arity=guard?undefined:arity;var result=createWrap(func,WRAP_CURRY_RIGHT_FLAG,undefined,undefined,undefined,undefined,undefined,arity);result.placeholder=curryRight.placeholder;return result;}/**
+ * Creates a debounced function that delays invoking `func` until after `wait`
+ * milliseconds have elapsed since the last time the debounced function was
+ * invoked. The debounced function comes with a `cancel` method to cancel
+ * delayed `func` invocations and a `flush` method to immediately invoke them.
+ * Provide `options` to indicate whether `func` should be invoked on the
+ * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
+ * with the last arguments provided to the debounced function. Subsequent
+ * calls to the debounced function return the result of the last `func`
+ * invocation.
+ *
+ * **Note:** If `leading` and `trailing` options are `true`, `func` is
+ * invoked on the trailing edge of the timeout only if the debounced function
+ * is invoked more than once during the `wait` timeout.
+ *
+ * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
+ * until to the next tick, similar to `setTimeout` with a timeout of `0`.
+ *
+ * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
+ * for details over the differences between `_.debounce` and `_.throttle`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to debounce.
+ * @param {number} [wait=0] The number of milliseconds to delay.
+ * @param {Object} [options={}] The options object.
+ * @param {boolean} [options.leading=false]
+ * Specify invoking on the leading edge of the timeout.
+ * @param {number} [options.maxWait]
+ * The maximum time `func` is allowed to be delayed before it's invoked.
+ * @param {boolean} [options.trailing=true]
+ * Specify invoking on the trailing edge of the timeout.
+ * @returns {Function} Returns the new debounced function.
+ * @example
+ *
+ * // Avoid costly calculations while the window size is in flux.
+ * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
+ *
+ * // Invoke `sendMail` when clicked, debouncing subsequent calls.
+ * jQuery(element).on('click', _.debounce(sendMail, 300, {
+ * 'leading': true,
+ * 'trailing': false
+ * }));
+ *
+ * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
+ * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
+ * var source = new EventSource('/stream');
+ * jQuery(source).on('message', debounced);
+ *
+ * // Cancel the trailing debounced invocation.
+ * jQuery(window).on('popstate', debounced.cancel);
+ */function debounce(func,wait,options){var lastArgs,lastThis,maxWait,result,timerId,lastCallTime,lastInvokeTime=0,leading=false,maxing=false,trailing=true;if(typeof func!='function'){throw new TypeError(FUNC_ERROR_TEXT);}wait=toNumber(wait)||0;if(isObject(options)){leading=!!options.leading;maxing='maxWait'in options;maxWait=maxing?nativeMax(toNumber(options.maxWait)||0,wait):maxWait;trailing='trailing'in options?!!options.trailing:trailing;}function invokeFunc(time){var args=lastArgs,thisArg=lastThis;lastArgs=lastThis=undefined;lastInvokeTime=time;result=func.apply(thisArg,args);return result;}function leadingEdge(time){// Reset any `maxWait` timer.
+lastInvokeTime=time;// Start the timer for the trailing edge.
+timerId=setTimeout(timerExpired,wait);// Invoke the leading edge.
+return leading?invokeFunc(time):result;}function remainingWait(time){var timeSinceLastCall=time-lastCallTime,timeSinceLastInvoke=time-lastInvokeTime,timeWaiting=wait-timeSinceLastCall;return maxing?nativeMin(timeWaiting,maxWait-timeSinceLastInvoke):timeWaiting;}function shouldInvoke(time){var timeSinceLastCall=time-lastCallTime,timeSinceLastInvoke=time-lastInvokeTime;// Either this is the first call, activity has stopped and we're at the
+// trailing edge, the system time has gone backwards and we're treating
+// it as the trailing edge, or we've hit the `maxWait` limit.
+return lastCallTime===undefined||timeSinceLastCall>=wait||timeSinceLastCall<0||maxing&&timeSinceLastInvoke>=maxWait;}function timerExpired(){var time=now();if(shouldInvoke(time)){return trailingEdge(time);}// Restart the timer.
+timerId=setTimeout(timerExpired,remainingWait(time));}function trailingEdge(time){timerId=undefined;// Only invoke if we have `lastArgs` which means `func` has been
+// debounced at least once.
+if(trailing&&lastArgs){return invokeFunc(time);}lastArgs=lastThis=undefined;return result;}function cancel(){if(timerId!==undefined){clearTimeout(timerId);}lastInvokeTime=0;lastArgs=lastCallTime=lastThis=timerId=undefined;}function flush(){return timerId===undefined?result:trailingEdge(now());}function debounced(){var time=now(),isInvoking=shouldInvoke(time);lastArgs=arguments;lastThis=this;lastCallTime=time;if(isInvoking){if(timerId===undefined){return leadingEdge(lastCallTime);}if(maxing){// Handle invocations in a tight loop.
+clearTimeout(timerId);timerId=setTimeout(timerExpired,wait);return invokeFunc(lastCallTime);}}if(timerId===undefined){timerId=setTimeout(timerExpired,wait);}return result;}debounced.cancel=cancel;debounced.flush=flush;return debounced;}/**
+ * Defers invoking the `func` until the current call stack has cleared. Any
+ * additional arguments are provided to `func` when it's invoked.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to defer.
+ * @param {...*} [args] The arguments to invoke `func` with.
+ * @returns {number} Returns the timer id.
+ * @example
+ *
+ * _.defer(function(text) {
+ * console.log(text);
+ * }, 'deferred');
+ * // => Logs 'deferred' after one millisecond.
+ */var defer=baseRest(function(func,args){return baseDelay(func,1,args);});/**
+ * Invokes `func` after `wait` milliseconds. Any additional arguments are
+ * provided to `func` when it's invoked.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to delay.
+ * @param {number} wait The number of milliseconds to delay invocation.
+ * @param {...*} [args] The arguments to invoke `func` with.
+ * @returns {number} Returns the timer id.
+ * @example
+ *
+ * _.delay(function(text) {
+ * console.log(text);
+ * }, 1000, 'later');
+ * // => Logs 'later' after one second.
+ */var delay=baseRest(function(func,wait,args){return baseDelay(func,toNumber(wait)||0,args);});/**
+ * Creates a function that invokes `func` with arguments reversed.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Function
+ * @param {Function} func The function to flip arguments for.
+ * @returns {Function} Returns the new flipped function.
+ * @example
+ *
+ * var flipped = _.flip(function() {
+ * return _.toArray(arguments);
+ * });
+ *
+ * flipped('a', 'b', 'c', 'd');
+ * // => ['d', 'c', 'b', 'a']
+ */function flip(func){return createWrap(func,WRAP_FLIP_FLAG);}/**
+ * Creates a function that memoizes the result of `func`. If `resolver` is
+ * provided, it determines the cache key for storing the result based on the
+ * arguments provided to the memoized function. By default, the first argument
+ * provided to the memoized function is used as the map cache key. The `func`
+ * is invoked with the `this` binding of the memoized function.
+ *
+ * **Note:** The cache is exposed as the `cache` property on the memoized
+ * function. Its creation may be customized by replacing the `_.memoize.Cache`
+ * constructor with one whose instances implement the
+ * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)
+ * method interface of `clear`, `delete`, `get`, `has`, and `set`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to have its output memoized.
+ * @param {Function} [resolver] The function to resolve the cache key.
+ * @returns {Function} Returns the new memoized function.
+ * @example
+ *
+ * var object = { 'a': 1, 'b': 2 };
+ * var other = { 'c': 3, 'd': 4 };
+ *
+ * var values = _.memoize(_.values);
+ * values(object);
+ * // => [1, 2]
+ *
+ * values(other);
+ * // => [3, 4]
+ *
+ * object.a = 2;
+ * values(object);
+ * // => [1, 2]
+ *
+ * // Modify the result cache.
+ * values.cache.set(object, ['a', 'b']);
+ * values(object);
+ * // => ['a', 'b']
+ *
+ * // Replace `_.memoize.Cache`.
+ * _.memoize.Cache = WeakMap;
+ */function memoize(func,resolver){if(typeof func!='function'||resolver!=null&&typeof resolver!='function'){throw new TypeError(FUNC_ERROR_TEXT);}var memoized=function(){var args=arguments,key=resolver?resolver.apply(this,args):args[0],cache=memoized.cache;if(cache.has(key)){return cache.get(key);}var result=func.apply(this,args);memoized.cache=cache.set(key,result)||cache;return result;};memoized.cache=new(memoize.Cache||MapCache)();return memoized;}// Expose `MapCache`.
+memoize.Cache=MapCache;/**
+ * Creates a function that negates the result of the predicate `func`. The
+ * `func` predicate is invoked with the `this` binding and arguments of the
+ * created function.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Function
+ * @param {Function} predicate The predicate to negate.
+ * @returns {Function} Returns the new negated function.
+ * @example
+ *
+ * function isEven(n) {
+ * return n % 2 == 0;
+ * }
+ *
+ * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven));
+ * // => [1, 3, 5]
+ */function negate(predicate){if(typeof predicate!='function'){throw new TypeError(FUNC_ERROR_TEXT);}return function(){var args=arguments;switch(args.length){case 0:return!predicate.call(this);case 1:return!predicate.call(this,args[0]);case 2:return!predicate.call(this,args[0],args[1]);case 3:return!predicate.call(this,args[0],args[1],args[2]);}return!predicate.apply(this,args);};}/**
+ * Creates a function that is restricted to invoking `func` once. Repeat calls
+ * to the function return the value of the first invocation. The `func` is
+ * invoked with the `this` binding and arguments of the created function.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * var initialize = _.once(createApplication);
+ * initialize();
+ * initialize();
+ * // => `createApplication` is invoked once
+ */function once(func){return before(2,func);}/**
+ * Creates a function that invokes `func` with its arguments transformed.
+ *
+ * @static
+ * @since 4.0.0
+ * @memberOf _
+ * @category Function
+ * @param {Function} func The function to wrap.
+ * @param {...(Function|Function[])} [transforms=[_.identity]]
+ * The argument transforms.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * function doubled(n) {
+ * return n * 2;
+ * }
+ *
+ * function square(n) {
+ * return n * n;
+ * }
+ *
+ * var func = _.overArgs(function(x, y) {
+ * return [x, y];
+ * }, [square, doubled]);
+ *
+ * func(9, 3);
+ * // => [81, 6]
+ *
+ * func(10, 5);
+ * // => [100, 10]
+ */var overArgs=castRest(function(func,transforms){transforms=transforms.length==1&&isArray(transforms[0])?arrayMap(transforms[0],baseUnary(getIteratee())):arrayMap(baseFlatten(transforms,1),baseUnary(getIteratee()));var funcsLength=transforms.length;return baseRest(function(args){var index=-1,length=nativeMin(args.length,funcsLength);while(++index 'hello fred'
+ *
+ * // Partially applied with placeholders.
+ * var greetFred = _.partial(greet, _, 'fred');
+ * greetFred('hi');
+ * // => 'hi fred'
+ */var partial=baseRest(function(func,partials){var holders=replaceHolders(partials,getHolder(partial));return createWrap(func,WRAP_PARTIAL_FLAG,undefined,partials,holders);});/**
+ * This method is like `_.partial` except that partially applied arguments
+ * are appended to the arguments it receives.
+ *
+ * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic
+ * builds, may be used as a placeholder for partially applied arguments.
+ *
+ * **Note:** This method doesn't set the "length" property of partially
+ * applied functions.
+ *
+ * @static
+ * @memberOf _
+ * @since 1.0.0
+ * @category Function
+ * @param {Function} func The function to partially apply arguments to.
+ * @param {...*} [partials] The arguments to be partially applied.
+ * @returns {Function} Returns the new partially applied function.
+ * @example
+ *
+ * function greet(greeting, name) {
+ * return greeting + ' ' + name;
+ * }
+ *
+ * var greetFred = _.partialRight(greet, 'fred');
+ * greetFred('hi');
+ * // => 'hi fred'
+ *
+ * // Partially applied with placeholders.
+ * var sayHelloTo = _.partialRight(greet, 'hello', _);
+ * sayHelloTo('fred');
+ * // => 'hello fred'
+ */var partialRight=baseRest(function(func,partials){var holders=replaceHolders(partials,getHolder(partialRight));return createWrap(func,WRAP_PARTIAL_RIGHT_FLAG,undefined,partials,holders);});/**
+ * Creates a function that invokes `func` with arguments arranged according
+ * to the specified `indexes` where the argument value at the first index is
+ * provided as the first argument, the argument value at the second index is
+ * provided as the second argument, and so on.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Function
+ * @param {Function} func The function to rearrange arguments for.
+ * @param {...(number|number[])} indexes The arranged argument indexes.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var rearged = _.rearg(function(a, b, c) {
+ * return [a, b, c];
+ * }, [2, 0, 1]);
+ *
+ * rearged('b', 'c', 'a')
+ * // => ['a', 'b', 'c']
+ */var rearg=flatRest(function(func,indexes){return createWrap(func,WRAP_REARG_FLAG,undefined,undefined,undefined,indexes);});/**
+ * Creates a function that invokes `func` with the `this` binding of the
+ * created function and arguments from `start` and beyond provided as
+ * an array.
+ *
+ * **Note:** This method is based on the
+ * [rest parameter](https://mdn.io/rest_parameters).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Function
+ * @param {Function} func The function to apply a rest parameter to.
+ * @param {number} [start=func.length-1] The start position of the rest parameter.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var say = _.rest(function(what, names) {
+ * return what + ' ' + _.initial(names).join(', ') +
+ * (_.size(names) > 1 ? ', & ' : '') + _.last(names);
+ * });
+ *
+ * say('hello', 'fred', 'barney', 'pebbles');
+ * // => 'hello fred, barney, & pebbles'
+ */function rest(func,start){if(typeof func!='function'){throw new TypeError(FUNC_ERROR_TEXT);}start=start===undefined?start:toInteger(start);return baseRest(func,start);}/**
+ * Creates a function that invokes `func` with the `this` binding of the
+ * create function and an array of arguments much like
+ * [`Function#apply`](http://www.ecma-international.org/ecma-262/7.0/#sec-function.prototype.apply).
+ *
+ * **Note:** This method is based on the
+ * [spread operator](https://mdn.io/spread_operator).
+ *
+ * @static
+ * @memberOf _
+ * @since 3.2.0
+ * @category Function
+ * @param {Function} func The function to spread arguments over.
+ * @param {number} [start=0] The start position of the spread.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var say = _.spread(function(who, what) {
+ * return who + ' says ' + what;
+ * });
+ *
+ * say(['fred', 'hello']);
+ * // => 'fred says hello'
+ *
+ * var numbers = Promise.all([
+ * Promise.resolve(40),
+ * Promise.resolve(36)
+ * ]);
+ *
+ * numbers.then(_.spread(function(x, y) {
+ * return x + y;
+ * }));
+ * // => a Promise of 76
+ */function spread(func,start){if(typeof func!='function'){throw new TypeError(FUNC_ERROR_TEXT);}start=start==null?0:nativeMax(toInteger(start),0);return baseRest(function(args){var array=args[start],otherArgs=castSlice(args,0,start);if(array){arrayPush(otherArgs,array);}return apply(func,this,otherArgs);});}/**
+ * Creates a throttled function that only invokes `func` at most once per
+ * every `wait` milliseconds. The throttled function comes with a `cancel`
+ * method to cancel delayed `func` invocations and a `flush` method to
+ * immediately invoke them. Provide `options` to indicate whether `func`
+ * should be invoked on the leading and/or trailing edge of the `wait`
+ * timeout. The `func` is invoked with the last arguments provided to the
+ * throttled function. Subsequent calls to the throttled function return the
+ * result of the last `func` invocation.
+ *
+ * **Note:** If `leading` and `trailing` options are `true`, `func` is
+ * invoked on the trailing edge of the timeout only if the throttled function
+ * is invoked more than once during the `wait` timeout.
+ *
+ * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
+ * until to the next tick, similar to `setTimeout` with a timeout of `0`.
+ *
+ * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
+ * for details over the differences between `_.throttle` and `_.debounce`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to throttle.
+ * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
+ * @param {Object} [options={}] The options object.
+ * @param {boolean} [options.leading=true]
+ * Specify invoking on the leading edge of the timeout.
+ * @param {boolean} [options.trailing=true]
+ * Specify invoking on the trailing edge of the timeout.
+ * @returns {Function} Returns the new throttled function.
+ * @example
+ *
+ * // Avoid excessively updating the position while scrolling.
+ * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
+ *
+ * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
+ * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
+ * jQuery(element).on('click', throttled);
+ *
+ * // Cancel the trailing throttled invocation.
+ * jQuery(window).on('popstate', throttled.cancel);
+ */function throttle(func,wait,options){var leading=true,trailing=true;if(typeof func!='function'){throw new TypeError(FUNC_ERROR_TEXT);}if(isObject(options)){leading='leading'in options?!!options.leading:leading;trailing='trailing'in options?!!options.trailing:trailing;}return debounce(func,wait,{'leading':leading,'maxWait':wait,'trailing':trailing});}/**
+ * Creates a function that accepts up to one argument, ignoring any
+ * additional arguments.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Function
+ * @param {Function} func The function to cap arguments for.
+ * @returns {Function} Returns the new capped function.
+ * @example
+ *
+ * _.map(['6', '8', '10'], _.unary(parseInt));
+ * // => [6, 8, 10]
+ */function unary(func){return ary(func,1);}/**
+ * Creates a function that provides `value` to `wrapper` as its first
+ * argument. Any additional arguments provided to the function are appended
+ * to those provided to the `wrapper`. The wrapper is invoked with the `this`
+ * binding of the created function.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {*} value The value to wrap.
+ * @param {Function} [wrapper=identity] The wrapper function.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var p = _.wrap(_.escape, function(func, text) {
+ * return '
+
diff --git a/cartridges/adyen_controllers_changes/app_storefront_core_changes/cartridge/templates/default/checkout/summary/summary.isml b/cartridges/adyen_controllers_changes/app_storefront_core_changes/cartridge/templates/default/checkout/summary/summary.isml
new file mode 100644
index 000000000..2b37ea459
--- /dev/null
+++ b/cartridges/adyen_controllers_changes/app_storefront_core_changes/cartridge/templates/default/checkout/summary/summary.isml
@@ -0,0 +1,245 @@
+
+
+
+
+
+ This template visualizes the last step of the checkout, the order summary
+ page prior to the actual order placing.
+ It displays the complete content of the cart including product line items,
+ bonus products, redeemed coupons and gift certificate line items.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cartridges/adyen_controllers_changes/app_storefront_core_changes/cartridge/templates/default/components/order/orderdetails.isml b/cartridges/adyen_controllers_changes/app_storefront_core_changes/cartridge/templates/default/components/order/orderdetails.isml
new file mode 100644
index 000000000..d44e01c33
--- /dev/null
+++ b/cartridges/adyen_controllers_changes/app_storefront_core_changes/cartridge/templates/default/components/order/orderdetails.isml
@@ -0,0 +1,226 @@
+
+
+
+ Displays order details, such as order number, creation date, payment information,
+ order totals and information for each contained shipment.
+ This template module can be used in order confirmation pages as well as in the
+ order history to render the details of a given order. Depending on the context
+ being used in, one might omit rendering certain information.
+
+ Parameters:
+
+ order : the order whose details to render
+ orderstatus : if set to true, the order status will be rendered
+ if set to false or not existing, the order status will not be rendered
+
+
+
+
+
diff --git a/cartridges/adyen_controllers_changes/app_storefront_core_changes/cartridge/templates/default/components/order/orderdetailsemail.isml b/cartridges/adyen_controllers_changes/app_storefront_core_changes/cartridge/templates/default/components/order/orderdetailsemail.isml
new file mode 100644
index 000000000..facdc2017
--- /dev/null
+++ b/cartridges/adyen_controllers_changes/app_storefront_core_changes/cartridge/templates/default/components/order/orderdetailsemail.isml
@@ -0,0 +1,258 @@
+
+
+ Displays order details, such as order number, creation date, payment information,
+ order totals and information for each contained shipment.
+ This template module can be used in order confirmation pages as well as in the
+ order history to render the details of a given order. Depending on the context
+ being used in, one might omit rendering certain information.
+
+ Parameters:
+
+ order : the order whose details to render
+ orderstatus : if set to true, the order status will be rendered
+ if set to false or not existing, the order status will not be rendered
+
+
+
+
+ Give your shoppers the option to donate to a charity during their payment.
+ Not sure how to register to Adyen Giving? Contact your Account Manager or Support.
+
+
+
+
+
+
+
+ Create a charity account to enable Adyen Giving.
+
+
+ Enter the name of your charity’s merchant account.
+
+
+
+
+
+
+ Enter the suggested amounts shoppers can choose to donate.
+
+
+
+
+
+
+ Create a short description of up to 70 characters to be displayed to your shoppers before they donate.
+
+
+
+
+
+
+ Enter the link to the charity website.
+
+
+
+
+
+
+ Your image needs to be in PNG, JPG, JPEG, or SVG format.
+
+
+
+
Click here to upload.
+
+
+
+
+
+
+
+
+ Your image needs to be in PNG, JPG, JPEG, or SVG format.
+
+
+
+
Click here to upload.
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/cartridges/bm_adyen/cartridge/templates/default/adyenSettings/settingCards/cardSettings.isml b/cartridges/bm_adyen/cartridge/templates/default/adyenSettings/settingCards/cardSettings.isml
new file mode 100644
index 000000000..409b8ff01
--- /dev/null
+++ b/cartridges/bm_adyen/cartridge/templates/default/adyenSettings/settingCards/cardSettings.isml
@@ -0,0 +1,37 @@
+
+
+
+
+
+ Card settings
+
+
+
Choose additional payment features you want to offer to your shoppers.
+
+
+
+
+ This allows you to show the input field for the cardholder’s name.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ If you’re offering an installments option to your shoppers, go to our GitHub to create the required configuration value and paste it here.
+
+
Allow your shoppers to pay instantly with the payment methods they trust across multiple stores.
+
+
+
+
+ Make sure the payment methods are also enabled in the Customer Area.
+
+
+
+
+
+
+
+
+ Customize the order that you would like to place the express buttons in the checkout.
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/cartridges/bm_adyen/cartridge/templates/default/adyenSettings/settingCards/lpmSettings.isml b/cartridges/bm_adyen/cartridge/templates/default/adyenSettings/settingCards/lpmSettings.isml
new file mode 100644
index 000000000..4ac0d32d1
--- /dev/null
+++ b/cartridges/bm_adyen/cartridge/templates/default/adyenSettings/settingCards/lpmSettings.isml
@@ -0,0 +1,70 @@
+
+
+
+
+
+ Local payment method settings
+
+
+
Configure required additional settings for payment methods. Most payment methods work without additional settings, find an overview of all payment methods here.
+
+
+
+
+ If your SFCC is connected to an OMS instance, you need to add the same payment methods you selected in the Customer Area. Make sure that all letters are lowercase, and separate multiple payment methods with commas.
+
+
+
+
+
+
+ This is an open invoice and secured direct debit provider available in Germany, Austria, Switzerland, and the Netherlands. Enter the unique ID provided by your Ratepay integration consultant.
+
+
+
+
+
+
+ Google Pay is enabled by default in the Test environment. If you also want to activate it in the Live environment, enter your Google Merchant ID
+
+
+
+
+
+
+ Apple Pay is enabled by default in the Test environment. If you also want to activate it in the Live environment, enter Apple Pay Domain Association
+
+
+
+
+
+
+ One-click stores payment details of your shoppers securely. It allows you to offer subscriptions, automatic top-ups to shoppers’ accounts, and give them a faster checkout experience.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Klarna inline widget allows you to complete Klarna payments without browser redirect.
+
Improve your business’s transparency and security.
+
+
+
+
+
+ 2/3 Data gives you interchange discounts on US domestic transactions. Your shoppers will also see additional data about their purchases on their credit card statements.
+ Check if your business is eligible for Level 2/3 Data.
+
+
+
+
+ We got you covered with our Revenue protect, risk engine. But if you want to create specific basket fields items in your risk set up, you can.
+ Please set up these custom fields in the Adyen Customer Area and enable this setting.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ To tokenize payment details for all payment methods, make sure you have this feature enabled.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Let us know the name of the company that built your integration with Adyen to get custom support.
+
+
+ SFRA combines best practices in site design and technical architecture so you can customize your store’s website. This setting should only be enabled if you’re using SFRA v6.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Enter the closest regions to your shoppers. This setup influences the speed of the Adyen Checkout Component during checkout.
+ Check the list of regions
+
+
+
+
+
+
+
+
+
+ Please note that you can enter only one merchant account per storefront.
+
+
+
+
Please make sure the combination of merchant account and API key is correct.
+
+
+
+
+ This is the API key you generated in the Customer Area.
+
+
+
+
+
+
+
+
+ Please make sure the combination of merchant account and API key is correct.
+
Custom Error ...: hasn't been defined. This is an error message sample.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
This page allows to view and configure custom values.
+
+ To save your settings, press the 'Apply' button.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cartridges/bm_adyen/cartridge/templates/default/marketing/giftcertificateemail.isml b/cartridges/bm_adyen/cartridge/templates/default/marketing/giftcertificateemail.isml
new file mode 100644
index 000000000..109117276
--- /dev/null
+++ b/cartridges/bm_adyen/cartridge/templates/default/marketing/giftcertificateemail.isml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+A Gift Certificate for You
+
+
+
+
+ Dear Valued Customer,
+
+ Dear ,
+
+
+
+ A Gift Certificate has been issued to you in the amount of .
+
+
+
+ Message:
+
+
+
+
+
+
+ You can redeem your gift certificate at our online store.
+
+
+ Your gift certificate code is .
+
+
+ Sincerely,
+
+
+
+ Customer Support
+
+
+
+
+
+
\ No newline at end of file
diff --git a/cartridges/bm_adyen/cartridge/templates/default/order/PaymentInstrumentInfo_ADYEN_CREDIT.isml b/cartridges/bm_adyen/cartridge/templates/default/order/PaymentInstrumentInfo_ADYEN_CREDIT.isml
new file mode 100644
index 000000000..360e3670f
--- /dev/null
+++ b/cartridges/bm_adyen/cartridge/templates/default/order/PaymentInstrumentInfo_ADYEN_CREDIT.isml
@@ -0,0 +1,54 @@
+
+
+
+
+
+ var order = pdict.Order;
+ var paymentInstruments = order.getPaymentInstruments().iterator();
+ if (paymentInstruments.hasNext()) {
+ var paymentInstrument = paymentInstruments.next();
+ var paymentMethod = dw.order.PaymentMgr.getPaymentMethod(paymentInstrument.paymentMethod);
+ var amount = order.custom.Adyen_value/100;
+ }
+
+
+
\ No newline at end of file
diff --git a/cartridges/bm_adyen/cartridge/templates/default/order/PaymentInstrumentInfo_Adyen.isml b/cartridges/bm_adyen/cartridge/templates/default/order/PaymentInstrumentInfo_Adyen.isml
new file mode 100644
index 000000000..2d12c1758
--- /dev/null
+++ b/cartridges/bm_adyen/cartridge/templates/default/order/PaymentInstrumentInfo_Adyen.isml
@@ -0,0 +1,83 @@
+
+
+
+
+
+ var order = pdict.Order;
+ var paymentInstruments = order.getPaymentInstruments().iterator();
+ if (paymentInstruments.hasNext()) {
+ var paymentInstrument = paymentInstruments.next();
+ var paymentMethod = dw.order.PaymentMgr.getPaymentMethod(paymentInstrument.paymentMethod);
+ var amount = order.custom.Adyen_value/100;
+ var adyenAdditionalPaymentData = JSON.parse(paymentInstrument.custom.adyenAdditionalPaymentData);
+ var adyenAction = JSON.parse(paymentInstrument.custom.adyenAction);
+ }
+
+
+
+
+
\ No newline at end of file
diff --git a/cartridges/bm_adyen/cartridge/templates/default/order/orderdetails.isml b/cartridges/bm_adyen/cartridge/templates/default/order/orderdetails.isml
new file mode 100644
index 000000000..45ff7b7c2
--- /dev/null
+++ b/cartridges/bm_adyen/cartridge/templates/default/order/orderdetails.isml
@@ -0,0 +1,181 @@
+
+
+
+
+
+
+
+
+ var plis : dw.util.Collection = Group.productLineItems;
+ var shipRowSpanCount : Number = plis.size();
+ var shipRowSpanValue : String = "1";
+ var iter : dw.util.Iterator = plis.iterator();
+ while (iter != null && iter.hasNext())
+ {
+ var pli : dw.order.ProductLineItme = iter.next();
+ shipRowSpanCount = shipRowSpanCount + pli.getBundledProductLineItems().size() + pli.getOptionProductLineItems().size();
+ }
+ shipRowSpanValue = shipRowSpanCount.toFixed();
+
+
+
\ No newline at end of file
diff --git a/cartridges/bm_adyen/cartridge/templates/default/order/orderemailcancellation.isml b/cartridges/bm_adyen/cartridge/templates/default/order/orderemailcancellation.isml
new file mode 100644
index 000000000..4f46bfe9c
--- /dev/null
+++ b/cartridges/bm_adyen/cartridge/templates/default/order/orderemailcancellation.isml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+ of your Order #'#'#
+
+
+
+
+
+
+
+
+
+
SiteGenesis
+
Demo SiteGenesis
+ 5 Wall Street
+ Burlington, MA 01803
+ SiteGenesis
+ Phone: +1.888.553.9216
+
+
+
+
+
+
+ Order Cancellation: Order Number: - Your order was cancelled!
+
+
+
This email confirms we cancelled your order at SiteGenesis.
+ You can check your order's status on your account pages at SiteGenesis
+
Thank you for shopping with us!
+
+
Please print and save a copy for reference.
+
PLEASE NOTE:
+ Please don't hesitate to contact us for any further questions via PHONE ONLY, at
+ +1.888.553.9216
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cartridges/bm_adyen/cartridge/templates/default/order/orderemailconfirmation.isml b/cartridges/bm_adyen/cartridge/templates/default/order/orderemailconfirmation.isml
new file mode 100644
index 000000000..5b5cbb014
--- /dev/null
+++ b/cartridges/bm_adyen/cartridge/templates/default/order/orderemailconfirmation.isml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+Confirmation of your Order
+
+
+
+
+
+
+
+
+
+
+
+
+
SiteGenesis
+
Demo SiteGenesis
+ 5 Wall Street
+ Burlington, MA 01803
+ SiteGenesis
+ Phone: +1.888.553.9216
+
+
+
+
+
+
+ Order Confirmation: Order Number: - Thank you for your order!
+
+
+
This email confirms we received your order at SiteGenesis. We will email your shipping
+ confirmation with your tracking number when your items have been shipped.
+ Your order will generally be shipped within 24 hours after we've received it.
+ You can check your order's status on your account pages at SiteGenesis
+
Thank you for shopping with us!
+
+
Please print and save a copy for reference.
+
Please review the order below and verify that everything is correct.
+
PLEASE NOTE:
+ Since we begin processing your order shortly after you submit it on our web site, if any changes are necessary you MUST contact us via PHONE ONLY, at
+ +1.888.553.9216
+