{"version":3,"sources":["webpack://geloso_ordering_portal//src/app/packs/images|sync|/^\\.\\/.*$/","webpack://geloso_ordering_portal/./app/packs/scripts/scrollbar.js","webpack://geloso_ordering_portal/./app/packs/scripts/navbar-search.js","webpack://geloso_ordering_portal/./app/packs/scripts/filters-utility.js","webpack://geloso_ordering_portal/./app/packs/scripts/layouts/brand_assets.js","webpack://geloso_ordering_portal/./app/packs/scripts/components/quantity-selector.js","webpack://geloso_ordering_portal/./app/packs/scripts/components/product-line-items-handler.js","webpack://geloso_ordering_portal/./app/packs/scripts/layouts/cart.js","webpack://geloso_ordering_portal/./app/packs/scripts/components/bootstrap-modal-handler.js","webpack://geloso_ordering_portal/./app/packs/scripts/layouts/product.js","webpack://geloso_ordering_portal/./app/packs/scripts/layouts/set_shipping_addresses.js","webpack://geloso_ordering_portal/./app/packs/scripts/components/shipping-address-search-handler.js","webpack://geloso_ordering_portal/./app/packs/entrypoints/main.js","webpack://geloso_ordering_portal/./app/packs/scripts/layouts/contact_us.js","webpack://geloso_ordering_portal/./app/packs/scripts/layouts/orders.js","webpack://geloso_ordering_portal/./app/packs/scripts/layouts/request_access.js","webpack://geloso_ordering_portal/./app/packs/scripts/layouts/shipping_address_requests.js","webpack://geloso_ordering_portal/./app/packs/scripts/layouts/devise/invitations.js","webpack://geloso_ordering_portal/./app/packs/scripts/layouts/devise/passwords.js","webpack://geloso_ordering_portal/./app/packs/scripts/layouts/devise/registrations.js","webpack://geloso_ordering_portal/./app/packs/scripts/form-validation.js","webpack://geloso_ordering_portal/./app/packs/scripts/password-validation.js"],"names":["map","webpackContext","req","id","webpackContextResolve","__webpack_require__","o","e","Error","code","keys","Object","resolve","module","exports","body","document","querySelector","window","innerWidth","clientWidth","classList","add","setAttribute","remove","searchOpen","searchCancel","searchForm","activeFormClass","openSearchForm","preventDefault","searchInput","focus","addEventListener","isComposing","keyCode","key","clearFilters","clearFilterButtons","formFilterMobile","formFilterDesktop","length","forEach","clearFiltersButton","clearForm","formSelector","doSubmitForm","form","formElements","elements","i","checked","submit","selectSortOrder","selectorClass","orderByInputSelectorString","orderDirInputSelectorString","formSelectorString","sortSelect","selectedOption","getSelectedOption","target","orderByInput","value","getAttribute","orderDirInput","selectField","options","selectedIndex","clearBrandAssetFilters","clearFiltersBtns","querySelectorAll","handleBrandAssetSorting","quantitySelectorHandler","handleQuantityBtns","handleQuantityChanges","handleNoChargeCheckbox","noChargeCheckbox","handleGlCodeToggle","event","targetNoChargeCheckbox","currentTarget","noChargeCheckboxInput","quantitySelectorWrapper","closest","glCodeTextInput","quantityInput","glCodeFormGroupWrapper","disabled","dispatchEvent","Event","decrementBtn","incrementBtn","btn","nextElementSibling","originalStep","Number","isInteger","stepDown","step","Math","floor","min","ceil","previousElementSibling","parentNode","stepUp","quantityInputs","relatedItemId","quantitySelector","palletQtyNode","recalculatePalletCount","weightNode","recalculateWeight","basePriceNode","priceNode","is_no_charge","contains","recalculatePrice","quantity","calculatedTotalPrice","textContent","trim","toFixed","updateHiddenCartItemInput","String","selectorInput","palletCountRow","casesPerPallet","pallets","round","weightDataAttribute","weightRow","weightPerCase","palletCasesDataAttribute","palletWeight","qty","totalWeight","calculatedTotalWeight","extraDataFieldNode","calculatedValue","hiddenInput","productLineItemsHandler","cartItemGroupDataOnLoad","cartItemGroupDataOnChange","glCodeInputValidationHandler","removeCartItemHandler","cartItemGroups","productGroup","cartItemGroupFieldsHandler","calculateDistributorTotal","glCodeTotalsHandler","cartItemGroupQuantityInputs","parentProductGroupElement","cartItemGroup","dataset","groupType","cartItemGroupBeverageFields","cartItemGroupDefaultFields","targetGroupQuantityTotalLabel","setGroupQuantityTotal","setGroupPriceTotal","targetGroupTotalPriceField","groupTotalPriceFields","groupTotalPricePrefix","groupTotalPriceValue","innerText","priceField","isCartItemQueuedToBeDestroyed","currencyPrefixSpan","createSpanWithCssClass","groupTotalPriceSpan","innerHTML","appendChild","setBeverageGroupPalletQuantityTotal","setBeverageGroupWeightTotal","targetGroupTotalQuantityField","groupQuantityValue","parseInt","targetGroupPalletQuantityField","palletQuantityFields","groupPalletQuantityValue","palletQuantityField","parseFloat","targetGroupWeightTotalField","weightTotalFields","groupWeightTotalValue","groupWeightTotalPostfix","weightTotalField","distributorTotalField","groupDataFieldTotals","distributorTotal","currencyPrefix","groupTotal","groupTotalNumber","distributorCurrencyPrefixSpan","distributorTotalSpan","childProductGroupElement","glCodeInputs","glCodeInput","availableGlCodes","getAvailableGlCodeDatalistValues","targetInput","includes","setCustomValidity","glCodeInputElement","glCodeInputWrapperElement","glCodeDatalistElement","availableGlCodeOptions","glCodeOption","push","glCodeTotalWrapperElement","activeGlCodeInputs","showOrHideGlCodeTotalsHandler","glCodeTotalsData","calculateGlCodeTotals","glCodeTotalsRowsMarkup","createGlCodeTotalRowsHTML","removeAllChildElements","glCodeRowElement","hrElement","createElement","glCodeTotalsWrapperElement","glCodeTotals","activeGlCodeInput","toBeDestroyed","glCodePrice","getQuantitySelectorGlCodeTotal","quantitySelectorElement","originalPriceElement","priceElement","targetPriceValueElement","glCodeRowsMarkup","glCodeTotalRowHTML","glCodeHTML","glCodePriceHTML","removeCartItemButtons","removeCartItemButton","currentRemoveCartItemButton","currentHiddenDestroyCheckbox","currentProductWrapper","toggle","parentElement","firstChild","removeChild","text","cssClass","spanElement","cartViewSessionStorage","sessionStorage","unsavedFormUpdatesAlertHandler","storeOriginalCartFormData","serializeCurrentCartFormData","storePreventAlertBool","preventAlertMessageEventListener","presentAlertMessageEventListener","cartSubmissionButtons","submissionButton","_","onbeforeunload","getPreventAlertBool","isSameString","getOriginalCartFormData","clear","returnValue","cartForm","serializeFormData","data","setItem","getItem","preventAlertBool","stringOne","stringTwo","localeCompare","formElement","formDataObject","FormData","JSON","stringify","cartCustomerPOItemCheckboxHandler","useFullOrderCheckbox","cartFullOrderPoCheckboxHandler","fullOrderCheckbox","customerPoInputsByProductType","customerPoInput","fullCustomerPoFormGroup","fullCustomerPoInput","validateQuantityInputsHandler","quantitySelectorInputs","validateNumberInputMin","input","numberInput","min_number","dynamicLinkToConfirmationModalHandler","modalConfirmationLinkTo","linkToBtn","stopPropagation","presentLinkToConfirmationModal","linkToHref","href","siteBodyElement","confirmationModal","buildModalWrapperElement","modalDialog","buildModalDialogElement","modalContent","buildModalContentElement","modalBody","buildModalBodyElement","modalFooter","buildModalFooterElement","Modal","show","modalWrapper","linkToData","buildModalTitleElement","buildModalBodyTextElement","titleText","titleElement","bodyText","bodyTextElement","submitToLink","modalFooterElement","buildModalSubmitButtonElement","buildModalCancelButtonElement","submitButtonText","submitToMethod","submitButtonElement","cancelButtonText","cancelButtonElement","clearProductFilters","handleProductSorting","handleProductSizeSelector","sizeSelector","variantUrl","location","dropdownItemActiveToggleHandler","getElementsByName","shippingAddressOption","activeDropdownItem","validateActiveOptionBeforeSubmitHandler","formSubmitButton","dropdownItemsRadioInputs","radioInput","hasSetValue","searchAddressInputOnKeyUp","searchInputHandler","searchAddressInputOnCloseBtnOrEnterKey","searchInputValue","toLowerCase","shippingDropdownItem","shippingAddressText","search","showElementWithBootstrapClasses","hideElementWithBootstrapClasses","element","require","ready","bodyNode","getElementsByTagName","handleScrollBarStyles","hasAttribute","productSubmenu","getElementById","productSubmenuCollapse","Collapse","hide","navbarSearchToggle","validateForms","some","className","shippingAddressSearchInput","shippingAddressSearchHandler","PasswordValidation","initPasswordValidation","readyState","forms","submitButton","parentFormElement","checkValidity","scrollIntoView","behavior","block","zxcvbn","this","minimumScore","strength","password","passwordFeedback","passwordShowButton","meter","strengthText","passwordButtonTextHide","passwordButtonTextShow","inputValue","result","toggleShowPassword","type","generateCustomValidation","score","updateMeter","resetStrengthText","updateTextIndicator","feedbackWarning","feedback","warning","validatePassword","bind"],"mappings":"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","file":"js/main-92805fd36cba60662fdb.js","sourcesContent":["var map = {\n\t\"./bootlegger-logo.png\": 74,\n\t\"./building-630.jpg\": 472,\n\t\"./buzz-tea-cans.png\": 433,\n\t\"./buzz-tea-logo.jpg\": 719,\n\t\"./cart-item-ratio-135x156.png\": 632,\n\t\"./clubtails-cocktail-party-box.png\": 150,\n\t\"./clubtails-logo.png\": 724,\n\t\"./ct-3-d-fiesta-pack-1.jpg\": 272,\n\t\"./geloso-icon-white.svg\": 277,\n\t\"./home-banner-slide-1.jpg\": 814,\n\t\"./home-banner-slide.jpg\": 446,\n\t\"./icon-arrow-up.svg\": 5,\n\t\"./icon-user-generic.svg\": 731,\n\t\"./jb-ice-lemonade-375-ml-dry.png\": 772,\n\t\"./logo-geloso.svg\": 928,\n\t\"./pepito-logo.png\": 545,\n\t\"./product-image-lg-thumbnail-ratio.png\": 363,\n\t\"./shape-circle.svg\": 637,\n\t\"./slider-image-ratio.png\": 895\n};\n\n\nfunction webpackContext(req) {\n\tvar id = webpackContextResolve(req);\n\treturn __webpack_require__(id);\n}\nfunction webpackContextResolve(req) {\n\tif(!__webpack_require__.o(map, req)) {\n\t\tvar e = new Error(\"Cannot find module '\" + req + \"'\");\n\t\te.code = 'MODULE_NOT_FOUND';\n\t\tthrow e;\n\t}\n\treturn map[req];\n}\nwebpackContext.keys = function webpackContextKeys() {\n\treturn Object.keys(map);\n};\nwebpackContext.resolve = webpackContextResolve;\nmodule.exports = webpackContext;\nwebpackContext.id = 999;","const handleScrollBarStyles = () => {\n const body = document.querySelector('body');\n\n if (window.innerWidth > body.clientWidth + 5) {\n body.classList.add('has-scrollbar');\n body.setAttribute('style', '--main-scroll-bar-width: ' + (window.innerWidth - body.clientWidth) + 'px');\n } else {\n body.classList.remove('has-scrollbar');\n body.setAttribute('style', '--main-scroll-bar-width: 0px');\n }\n}\n\nexport default handleScrollBarStyles;\n","const navbarSearchToggle = () => {\n const searchOpen = document.querySelector('.js-navbar-search-open');\n const searchCancel = document.querySelector('.js-navbar-search-cancel');\n const searchForm = document.querySelector('.js-nav-search-form');\n const activeFormClass = 'navbar__search-form--active';\n\n const openSearchForm = (e) => {\n e.preventDefault();\n searchForm.classList.add(activeFormClass);\n\n const searchInput = searchForm.querySelector('.search-form__input');\n if (searchInput) {\n searchInput.focus();\n }\n };\n\n if(!searchOpen) return;\n\n searchOpen.addEventListener('click', openSearchForm);\n\n searchCancel.addEventListener('click', (e) => {\n e.preventDefault();\n searchForm.classList.remove(activeFormClass);\n });\n\n document.addEventListener('keyup', (e) => {\n if (e.isComposing || e.keyCode === 229) {\n return;\n }\n\n if (e.key === '/') {\n openSearchForm(e);\n }\n });\n}\n\nexport default navbarSearchToggle;\n","/**\n * Clear Filters\n *\n * Clears the index filter form and index view of any active filters IF the mark-up is architected similar to the\n * Product or Brand Assets index views.\n *\n * @param {NodeListOf} clearFilterButtons Array of possible clear filter buttons in the dom. Expect a list to\n * support desktop and mobile filters logic!\n * @param {String} formFilterMobile CSS element target id or class for the mobile form filters present\n * on index view.\n * @param {String} formFilterDesktop CSS element target id or class for the desktop form filters present\n * on index view.\n * @return {void} Adds event listeners to clear filter buttons on click.\n */\nexport const clearFilters = (clearFilterButtons, formFilterMobile, formFilterDesktop) => {\n if (clearFilterButtons.length === 0) return;\n\n clearFilterButtons.forEach((clearFiltersButton) => {\n clearFiltersButton.addEventListener('click', () => {\n clearForm(formFilterMobile);\n clearForm(formFilterDesktop, true);\n });\n });\n};\n\n/**\n * Clear Form\n *\n * Given a form query selector path, this method will clear all filter options and re-submit the filter payload so\n * queried data gets reset. This logic is used via Product and Brand Assets index views \"Clear Filters\" button action!\n *\n * @param formSelector {String} formSelector Target query selector string for form element.\n * @param doSubmitForm {Boolean} doSubmitForm This boolean param will decided whether or not we want to force submit a\n * form cleared of all inputs when this method is called.\n * @return {void} Clear all form inputs and submit empty form payload if asserted.\n */\nconst clearForm = (formSelector, doSubmitForm = false) => {\n const form = document.querySelector(formSelector);\n if (!form) {\n return;\n }\n\n const formElements = form.elements;\n if (!formElements || !formElements.length) {\n return;\n }\n\n for (let i = 0; i < formElements.length; i++) {\n if (formElements[i].checked) {\n formElements[i].checked = false;\n }\n }\n\n if (doSubmitForm) {\n form.submit();\n }\n};\n\n/**\n * Select Sort Order\n *\n * Generalized \"Sort Filter\" logic for the Product and Brand Assets index views. If new views are built similarly to\n * these, this logic should work the same way given matching query selectors!\n *\n * @param {String} selectorClass Javascript query selector for the \"Order\" select input.\n * @param {String} orderByInputSelectorString Javascript query selector for the hidden \"Order By\" input via filters\n * form which will submit the \"Order By\" payload to the controller on form\n * filters submit. By default, this will sort order by :name column!\n * @param {String} orderDirInputSelectorString Javascript query selector for the hidden \"Order Direction\" input via\n * filters form which will the order direction payload to the controller on\n * form submit. Depending on selected option, this will be :asc or :desc.\n * @param {String} formSelectorString Javascript query selector for the filters form that should be present on\n * index view. It is the wrapper for all form filter inputs!\n * @return {void} Update the form filters using new \"Order\" options and submit filters\n * form to update index view with new filters query.\n */\nexport const selectSortOrder = (selectorClass, orderByInputSelectorString, orderDirInputSelectorString, formSelectorString) => {\n const sortSelect = document.querySelector(selectorClass);\n if (!sortSelect) {\n return;\n }\n\n sortSelect.addEventListener('change', (e) => {\n const selectedOption = getSelectedOption(e.target);\n const orderByInput = document.querySelector(orderByInputSelectorString);\n if (!orderByInput) {\n return;\n }\n\n orderByInput.value = selectedOption.getAttribute('data-order-by');\n\n const orderDirInput = document.querySelector(orderDirInputSelectorString);\n if (!orderDirInput) {\n return;\n }\n\n orderDirInput.value = selectedOption.getAttribute('data-order-dir');\n\n const form = document.querySelector(formSelectorString);\n if (!form) {\n return;\n }\n\n form.submit();\n });\n};\n\n/**\n * Get Selected Option\n *\n * Checks the provided select element for the currently selected option.\n *\n * TODO: This method could probably live in a FormElementUtilities.js file to be used in other javascript files.\n *\n * @param {HTMLSelectElement} selectField The input check field to validate selected option for.\n * @returns {HTMLOptionElement|undefined} The option element that is active in the target select list.\n */\nexport const getSelectedOption = (selectField) => {\n if (!selectField || !selectField.options || selectField.options.length === 0) {\n return;\n }\n\n const selectedIndex = selectField.options.selectedIndex;\n return selectField.options[selectedIndex];\n};","import {selectSortOrder, clearFilters} from '../filters-utility';\n\n/**\n * Brand Assets On Ready (Public/Exported)\n *\n * This is the entry point method for the BrandAssetsController based views that gets called in main.js's `ready()`\n * method.\n *\n * @returns {void}\n */\nexport const brandAssetsOnReady = () => {\n clearBrandAssetFilters();\n handleBrandAssetSorting();\n}\n\n/**\n * Clear Brand Asset Filters\n *\n * Clears the product filter form and product view of any active filters using filters-utility.js logic.\n *\n * @returns {void}\n */\nconst clearBrandAssetFilters = () => {\n const clearFiltersBtns = document.querySelectorAll('.js-clear-brand-asset-filters');\n clearFilters(clearFiltersBtns,'#form-brand-asset-filters-collapse', '#form-brand-assets-filters');\n};\n\n/**\n * Handle Brand Asset Sorting\n *\n * Updates the brand asset view with the chosen sort order using filters-utility.js logic.\n *\n * @returns {void}\n */\nconst handleBrandAssetSorting = () => {\n selectSortOrder('.js-brand-asset-sort-select', '#brand-asset-form-order-by-input', '#brand-asset-form-order-dir-input', '#form-brand-assets-filters');\n selectSortOrder('.js-brand-asset-sort-select-collapse', '#brand-asset-form-order-by-input-collapse', '#brand-asset-form-order-dir-input-collapse', '#form-brand-asset-filters-collapse');\n};","/**\n * Quantity Selector Handler\n */\nexport const quantitySelectorHandler = () => {\n handleQuantityBtns();\n handleQuantityChanges();\n handleNoChargeCheckbox();\n}\n\nconst handleNoChargeCheckbox = () => {\n const noChargeCheckboxes = document.querySelectorAll('.js-no-charge-checkbox');\n\n noChargeCheckboxes.forEach((noChargeCheckbox) => {\n // Handle GL Code initialization on load...\n handleGlCodeToggle(noChargeCheckbox);\n\n // Handle GL Code Toggle on \"No Charge\" checkbox on change...\n noChargeCheckbox.addEventListener('change', (event) => {\n const targetNoChargeCheckbox = event.currentTarget;\n handleGlCodeToggle(targetNoChargeCheckbox);\n });\n });\n}\n\n/**\n * HandleGlCodeToggle\n *\n * @param noChargeCheckboxInput\n */\nconst handleGlCodeToggle = (noChargeCheckboxInput) => {\n const quantitySelectorWrapper = noChargeCheckboxInput.closest('.gel-quantity-selector');\n const glCodeTextInput = quantitySelectorWrapper.querySelector('.js-gl-code-input');\n const quantityInput = quantitySelectorWrapper.querySelector('.js-quantity-selector-input');\n const glCodeFormGroupWrapper = glCodeTextInput.closest('.input-group');\n\n if (noChargeCheckboxInput.checked === true) {\n glCodeFormGroupWrapper.classList.remove('d-none');\n glCodeTextInput.disabled = false;\n quantitySelectorWrapper.classList.add('gel-quantity-selector--no-charge');\n } else {\n glCodeFormGroupWrapper.classList.add('d-none');\n glCodeTextInput.disabled = true;\n quantitySelectorWrapper.classList.remove('gel-quantity-selector--no-charge');\n }\n\n // Force recalculation of price by triggering a change event on quantity input...\n quantityInput.dispatchEvent(new Event('change'));\n}\n\n/**\n * Sets triggers on the plus/minus buttons in quantity selectors that increment\n * and decrement the related quantity form input based on it's \"step\" value\n * @returns {void}\n */\nconst handleQuantityBtns = () => {\n const decrementBtn = document.querySelectorAll('.js-quantity-selector-decrement');\n const incrementBtn = document.querySelectorAll('.js-quantity-selector-increment');\n\n if (!decrementBtn || decrementBtn.length === 0) {\n return;\n }\n\n if (!incrementBtn || incrementBtn.length === 0) {\n return;\n }\n\n decrementBtn.forEach((btn) => {\n const quantityInput = btn.nextElementSibling;\n const originalStep = quantityInput.getAttribute('data-original-step');\n\n if (quantityInput.value - originalStep < 1) {\n btn.disabled = true\n } else {\n btn.disabled = false\n }\n\n btn.addEventListener('click', () => {\n /* Check if we are dealing with odd number case quantity */\n if (Number.isInteger(originalStep)) {\n quantityInput.stepDown();\n } else {\n if (Number.isInteger(+quantityInput.value % +originalStep)) {\n /* if current case quantity value is equating to a whole number of pallets - no half pallets */\n quantityInput.step = Math.floor(originalStep);\n quantityInput.min = Math.floor(originalStep);\n } else {\n /* if case quantity is a half-pallet value, and therefore has been rounded up */\n quantityInput.step = Math.ceil(originalStep);\n quantityInput.min = Math.ceil(originalStep);\n }\n\n quantityInput.value = +quantityInput.value - +quantityInput.step;\n\n if (quantityInput.value - originalStep < 1) {\n btn.disabled = true\n }\n }\n\n // the `stepDown()` function doesn't seem to trigger a change event\n // on the input, so we need to do it manually\n quantityInput.dispatchEvent(new Event('change'));\n });\n });\n\n incrementBtn.forEach((btn) => {\n const quantityInput = btn.previousElementSibling;\n const originalStep = quantityInput.getAttribute('data-original-step');\n const decrementBtn = btn.parentNode.querySelector('.js-quantity-selector-decrement')\n\n btn.addEventListener('click', () => {\n decrementBtn.disabled = false\n\n if (Number.isInteger(originalStep)) {\n quantityInput.stepUp();\n } else {\n if (Number.isInteger(+quantityInput.value % +originalStep)) {\n /* if current case quantity value is equating to a whole number of pallets - no half pallets */\n quantityInput.step = Math.ceil(originalStep);\n quantityInput.min = Math.ceil(originalStep);\n } else {\n /* if case quantity is a half-pallet value, and therefore has been rounded up */\n quantityInput.step = Math.floor(originalStep);\n quantityInput.min = Math.floor(originalStep);\n }\n\n quantityInput.value = +quantityInput.value + +quantityInput.step;\n }\n\n // the `stepUp()` function doesn't seem to trigger a change event\n // on the input, so we need to do it manually\n quantityInput.dispatchEvent(new Event('change'));\n });\n });\n};\n\n/**\n * Watches quantity selector input fields for changes and modifies the related\n * price/weight displays based on the new number\n *\n * @returns {void}\n */\nconst handleQuantityChanges = () => {\n const quantityInputs = document.querySelectorAll('.js-quantity-selector-input');\n\n if (!quantityInputs || quantityInputs.length === 0) return;\n\n quantityInputs.forEach((quantityInput) => {\n quantityInput.addEventListener('change', (e) => {\n // TODO: ultimately, it might be nice to send a request to the\n // backend to handle the update calculations, so we don't have to\n // do it in multiple places - rcrichlow - 9/3/2021\n const relatedItemId = e.target.getAttribute('data-related-item');\n if (!relatedItemId) {\n return;\n }\n\n const quantitySelector = document.querySelector(`#quantity-selector-${relatedItemId}`);\n if (!quantitySelector) {\n return;\n }\n\n const palletQtyNode = quantitySelector.querySelector('.js-quantity-selector-pallet-qty');\n if (palletQtyNode) {\n recalculatePalletCount(palletQtyNode, quantitySelector);\n }\n\n const weightNode = quantitySelector.querySelector('.js-quantity-selector-weight');\n if (weightNode) {\n recalculateWeight(weightNode, quantitySelector);\n }\n\n const basePriceNode = quantitySelector.querySelector('.js-quantity-selector-base-price');\n const priceNode = quantitySelector.querySelector('.js-quantity-selector-total-price');\n const is_no_charge = quantitySelector.classList.contains('gel-quantity-selector--no-charge');\n if (basePriceNode && priceNode) {\n recalculatePrice(basePriceNode, priceNode, Number(e.target.value), is_no_charge);\n }\n });\n });\n};\n\n/**\n * Updates the price displayed in a quantity selector.\n *\n * @param {HTMLElement} basePriceNode The HTML element that contains the base price\n * @param {HTMLElement} priceNode The HTML element that contains the total price\n * @param {Number} quantity The number of items currently selected\n * @param {Boolean} is_no_charge Tells us if the current product is set to \"No Charge\" or not! If \"No Charge\" is\n * set to true, then our price will be set to $0.00 dollars!\n * @returns {void}\n */\nconst recalculatePrice = (basePriceNode, priceNode, quantity, is_no_charge = false) => {\n if (!basePriceNode || !priceNode || !quantity) {\n return;\n }\n\n const price = Number(is_no_charge ? 0 : basePriceNode.textContent.trim());\n const newPrice = price * quantity;\n const calculatedTotalPrice = newPrice.toFixed(2)\n priceNode.textContent = calculatedTotalPrice;\n\n // Hidden value input to be sent to the Cart session object\n updateHiddenCartItemInput(priceNode, String(calculatedTotalPrice));\n};\n\n/**\n * Updates the pallet count in a quantity selector\n *\n * @param {Element} palletQtyNode The element tracking the current pallet count\n * @param {Element} quantitySelector The quantity selector element\n * @returns {void}\n */\nconst recalculatePalletCount = (palletQtyNode, quantitySelector) => {\n if (!palletQtyNode || !quantitySelector) {\n return;\n }\n\n const selectorInput = quantitySelector.querySelector('.js-quantity-selector-input');\n if (!selectorInput) {\n return;\n }\n\n const palletCountRow = quantitySelector.querySelector('.gel-quantity-selector__row[data-cases-per-pallet]');\n if (!palletCountRow) {\n return;\n }\n\n const casesPerPallet = palletCountRow.getAttribute('data-cases-per-pallet');\n if (!casesPerPallet) {\n return;\n }\n\n const pallets = Number(selectorInput.value) / Number(casesPerPallet);\n // const calculatedPalletQuantity = Math.ceil(pallets);\n palletQtyNode.textContent = Math.round(pallets * 10) / 10\n\n updateHiddenCartItemInput(palletQtyNode, String(pallets));\n};\n\n/**\n * Updates the weight displayed in a quantity selector\n *\n * @param {Element} weightNode The element tracking the current weight\n * @param {Element} quantitySelector The quantity selector element\n * @returns {void}\n */\nconst recalculateWeight = (weightNode, quantitySelector) => {\n if (!weightNode || !quantitySelector) {\n return;\n }\n\n const selectorInput = quantitySelector.querySelector('.js-quantity-selector-input');\n if (!selectorInput) {\n return;\n }\n\n const weightDataAttribute = 'data-weight-per-case';\n const weightRow = quantitySelector.querySelector(`.gel-quantity-selector__row[${weightDataAttribute}]`);\n if (!weightRow) {\n return;\n }\n\n const weightPerCase = weightRow.getAttribute(weightDataAttribute);\n if (!weightPerCase) {\n return;\n }\n\n const palletCasesDataAttribute = 'data-cases-per-pallet';\n const palletWeightDataAttribute = 'data-base-pallet-weight';\n const palletCountRow = quantitySelector.querySelector(`.gel-quantity-selector__row[${palletCasesDataAttribute}]`);\n if (!palletCountRow) {\n return;\n }\n\n const casesPerPallet = palletCountRow.getAttribute(palletCasesDataAttribute);\n if (!casesPerPallet) {\n return;\n }\n\n const palletWeight = palletCountRow.getAttribute(palletWeightDataAttribute);\n\n const qty = Number(selectorInput.value);\n const pallets = qty / Number(casesPerPallet);\n const caseWeightTotal = qty * Number(weightPerCase);\n const totalWeight = caseWeightTotal + (pallets) * Number(palletWeight);\n const calculatedTotalWeight = Math.ceil(totalWeight);\n weightNode.textContent = calculatedTotalWeight;\n\n updateHiddenCartItemInput(weightNode, String(calculatedTotalWeight));\n};\n\n/**\n * Update Hidden Cart Item Input\n *\n * @param {Element} extraDataFieldNode Related element node we are pulling our updated value from. We want to this hidden\n * input in sync so we can save the final payload to the cart session object on form\n * submission.\n * @param {String} calculatedValue The value of the related extra data field that we are going save in our cart\n * session object on form submit.\n */\nconst updateHiddenCartItemInput = (extraDataFieldNode, calculatedValue) => {\n if(!extraDataFieldNode) return;\n\n const hiddenInput = extraDataFieldNode\n .closest('.gel-quantity-selector__row')\n .querySelector('.gel-quantity-selector__hidden-field-value');\n\n if(!hiddenInput) return;\n\n hiddenInput.value = calculatedValue;\n};\n","/**\n * Product Line Items Handler\n */\nexport const productLineItemsHandler = () => {\n cartItemGroupDataOnLoad();\n cartItemGroupDataOnChange();\n glCodeInputValidationHandler();\n removeCartItemHandler();\n}\n\n/* =================================================================\n Product Group Data Funks\n================================================================= */\n/**\n * Cart Item Group Data Onload (Private)\n *\n * Logic to calculate ALL product group data on initial page load. It will loop through all product type group cards via\n * the Cart index view to kick off the initial calculations for each product group's data fields.\n *\n * @return {void} Calls cartItemGroupFieldHandler method on each present Cart item group in the dom.\n */\nconst cartItemGroupDataOnLoad = () => {\n const cartItemGroups = document.querySelectorAll('.gel-cart-items-group');\n if (cartItemGroups.length === 0) return;\n\n cartItemGroups.forEach((productGroup) => {\n cartItemGroupFieldsHandler(productGroup);\n });\n\n calculateDistributorTotal();\n glCodeTotalsHandler();\n}\n\n/**\n * Cart Item Group Data On Change (Private)\n *\n * This method will attach an \"on change\" event handlers to each product group's quantity input field. If any of the\n * inputs change within a product group, that product group's data will be re-calculated.\n *\n * @return {void} Add on change event listener to each quantity input in order to trigger product group data field\n * updates.\n */\nconst cartItemGroupDataOnChange = () => {\n const cartItemGroupQuantityInputs = document.querySelectorAll('.gel-cart-items-group .js-quantity-selector-input');\n if (cartItemGroupQuantityInputs.length === 0) return;\n\n cartItemGroupQuantityInputs.forEach((quantityInput) => {\n quantityInput.addEventListener('change', (event) => {\n const parentProductGroupElement = event.target.closest('.gel-cart-items-group');\n cartItemGroupFieldsHandler(parentProductGroupElement);\n calculateDistributorTotal();\n glCodeTotalsHandler();\n });\n });\n}\n\n/**\n * Cart Item Group Fields Handler (Private)\n *\n * This method is used to differentiate in between custom group field handler methods or the default field handler.\n * Currently, only the beverage product type has custom fields that don't match the default group fields as of\n * 09/24/2021.\n *\n * @param {Element} cartItemGroup Required cart item group element via Cart index view.\n * @return {void} Calls the proper fields handler method depending on group product type.\n */\nconst cartItemGroupFieldsHandler = (cartItemGroup) => {\n if(!cartItemGroup) return;\n\n if (cartItemGroup.dataset.groupType === 'beverage') cartItemGroupBeverageFields(cartItemGroup);\n\n cartItemGroupDefaultFields(cartItemGroup);\n}\n\n/**\n * Cart Item Group Default Fields (Private)\n *\n * This method handles the default fields you would expect to see via the Cart Index view's product groups. Currently,\n * this would be all product types other than \"beverage\" as of 09/22/2021.\n *\n * @param {Element} cartItemGroup Required cart item group element via Cart index view.\n * @return {void} Calls related group field handlers.\n */\nconst cartItemGroupDefaultFields = (cartItemGroup) => {\n if (!cartItemGroup) return;\n const targetGroupQuantityTotalLabel = cartItemGroup.querySelector('.gel-cart-items-group__group-data-field-total-qty strong');\n\n setGroupQuantityTotal(cartItemGroup, targetGroupQuantityTotalLabel);\n setGroupPriceTotal(cartItemGroup);\n}\n\n/**\n * Set Group Price Total (Private)\n *\n * If Cart Item Group supports a price total, this method will calculate the total and add it to the dom.\n *\n * @param {Element} cartItemGroup Required Cart item group which supports a \"Total Price\" field via Cart index view.\n * @return {void} Updates the cart index view's \"Product Total\" with calculated total price.\n */\nconst setGroupPriceTotal = (cartItemGroup) => {\n if(!cartItemGroup) return;\n const targetGroupTotalPriceField = cartItemGroup.querySelector('.gel-cart-items-group__group-data-field-total-price strong');\n if (!targetGroupTotalPriceField) return;\n\n const groupTotalPriceFields = cartItemGroup.querySelectorAll('.js-quantity-selector-total-price');\n let groupTotalPricePrefix = '';\n let groupTotalPriceValue = 0.0;\n\n if (groupTotalPriceFields.length > 0) {\n // Get Currency prefix based off of first total price field...\n groupTotalPricePrefix = groupTotalPriceFields[0]\n .closest('.gel-quantity-selector__row')\n .querySelector('.gel-quantity-selector__field-value-prefix')\n .innerText;\n\n // Calculate total price of all items in group...\n groupTotalPriceFields.forEach((priceField) => {\n if (isCartItemQueuedToBeDestroyed(priceField)) return;\n\n groupTotalPriceValue += Number(priceField.innerText)\n });\n }\n\n const currencyPrefixSpan = createSpanWithCssClass(String(groupTotalPricePrefix), 'js-price-prefix');\n const groupTotalPriceSpan = createSpanWithCssClass(String(groupTotalPriceValue.toFixed(2)), 'js-price-total');\n\n targetGroupTotalPriceField.innerHTML = '';\n targetGroupTotalPriceField.appendChild(currencyPrefixSpan);\n targetGroupTotalPriceField.appendChild(groupTotalPriceSpan);\n}\n\n/**\n * Cart Item Group Beverage Fields (Private)\n *\n * This method handles the custom beverage fields you would expect to see via the Cart index view's product groups.\n *\n * @param {Element} cartItemGroup Required cart item group element via Cart index view.\n * @return {void} Calls related beverage group field handlers.\n */\nconst cartItemGroupBeverageFields = (cartItemGroup) => {\n if (!cartItemGroup) return;\n const targetGroupQuantityTotalLabel = cartItemGroup.querySelector('.gel-cart-items-group__group-data-field-total-case-qty strong');\n\n setGroupQuantityTotal(cartItemGroup, targetGroupQuantityTotalLabel);\n setBeverageGroupPalletQuantityTotal(cartItemGroup);\n setBeverageGroupWeightTotal(cartItemGroup);\n}\n\n/**\n * Set Group Quantity Total (Private)\n *\n * If Cart Item Group supports a quantity field, this method will calculate the total and add it to the dom.\n *\n * @param {Element} cartItemGroup Required Cart Item Group which supports a js-quantity-selector-input\n * element via Cart index view.\n * @param {Element} targetGroupTotalQuantityField Required target element we want to inject our value into.\n * @return {void} Updates the passed in Cart Item Group's \"Quantity\" field with\n * calculated total via Cart's index view.\n */\nconst setGroupQuantityTotal = (cartItemGroup, targetGroupTotalQuantityField) => {\n if (!cartItemGroup) return;\n if (!targetGroupTotalQuantityField) return;\n\n const quantityInputs = cartItemGroup.querySelectorAll('.js-quantity-selector-input');\n let groupQuantityValue = 0;\n if (quantityInputs.length > 0) {\n quantityInputs.forEach((quantityInput) => {\n if (isCartItemQueuedToBeDestroyed(quantityInput)) return;\n\n groupQuantityValue += parseInt(quantityInput.value);\n });\n }\n targetGroupTotalQuantityField.innerHTML = groupQuantityValue;\n}\n\n/**\n * Set Beverage Group Pallet Quantity Total (Private)\n *\n * Custom method used only for the grouped products of type beverage via Cart index view.\n *\n * @param {Element} cartItemGroup Required cart item group element via Cart index view.\n * @return {void} Updates the passed in Cart Item Group's \"Pallet Quantity\" field with calculated total.\n */\nconst setBeverageGroupPalletQuantityTotal = (cartItemGroup) => {\n if(!cartItemGroup) return;\n const targetGroupPalletQuantityField = cartItemGroup.querySelector('.gel-cart-items-group__group-data-field-pallet-total strong');\n if (!targetGroupPalletQuantityField) return;\n\n const palletQuantityFields = cartItemGroup.querySelectorAll('.js-quantity-selector-pallet-qty');\n let groupPalletQuantityValue = 0;\n if (palletQuantityFields.length > 0) {\n palletQuantityFields.forEach((palletQuantityField) => {\n groupPalletQuantityValue += parseFloat(palletQuantityField.innerText);\n });\n }\n targetGroupPalletQuantityField.innerHTML = groupPalletQuantityValue;\n}\n\n/**\n * Set Beverage Group Weight Total (Private)\n *\n * Custom method used only for the grouped products of type beverage via Cart index view.\n *\n * @param {Element} cartItemGroup Required cart item group element via Cart index view.\n * @return {void} Updates the passed in Cart Item Group's \"Weight Total\" field with calculated total.\n */\nconst setBeverageGroupWeightTotal = (cartItemGroup) => {\n if (!cartItemGroup) return;\n const targetGroupWeightTotalField = cartItemGroup.querySelector('.gel-cart-items-group__group-data-field-weight-total strong');\n if (!targetGroupWeightTotalField) return;\n\n const weightTotalFields = cartItemGroup.querySelectorAll('.js-quantity-selector-weight');\n let groupWeightTotalValue = 0;\n let groupWeightTotalPostfix = '';\n if (weightTotalFields.length > 0) {\n // Get weight's postfix unit based off of first weight field...\n groupWeightTotalPostfix = weightTotalFields[0]\n .closest('.gel-quantity-selector__row')\n .querySelector('.gel-quantity-selector__field-value-postfix')\n .innerText;\n // Calculate total weight of all items in group...\n weightTotalFields.forEach((weightTotalField) => {\n groupWeightTotalValue += parseInt(weightTotalField.innerText);\n });\n }\n targetGroupWeightTotalField.innerHTML = groupWeightTotalValue + ' ' + groupWeightTotalPostfix;\n}\n\n/**\n * Calculate Distributor Total (Private)\n *\n * Will calculate the distributor total displayed on the cart show page's order details section.\n *\n * Note: This has to be called after all of the other group price totals have been calculated!\n *\n * @return {void} Element with class .js-distributor-total via Cart's order detail section will be injected with a\n * calculated distributor total based off of each group data total price labels.\n */\nconst calculateDistributorTotal = () => {\n const distributorTotalField = document.querySelector('.js-distributor-total');\n distributorTotalField.innerHTML = '';\n\n if (!distributorTotalField) return;\n\n const groupDataFieldTotals = document.querySelectorAll('.gel-cart-items-group__group-data-field-total-price strong');\n let distributorTotal = 0.0;\n let currencyPrefix = '$';\n\n if (groupDataFieldTotals.length > 0) {\n currencyPrefix = groupDataFieldTotals[0].querySelector('.js-price-prefix').innerText;\n\n groupDataFieldTotals.forEach((groupTotal) => {\n const groupTotalNumber = Number(groupTotal.querySelector('.js-price-total').innerText);\n distributorTotal += groupTotalNumber;\n });\n }\n\n const distributorCurrencyPrefixSpan = createSpanWithCssClass(String(currencyPrefix), 'js-distributor-total-prefix');\n const distributorTotalSpan = createSpanWithCssClass(String(distributorTotal.toFixed(2)), 'js-distributor-total');\n\n distributorTotalField.appendChild(distributorCurrencyPrefixSpan);\n distributorTotalField.appendChild(distributorTotalSpan);\n}\n\n/**\n * Is Cart Item Queued To Be Destroyed?\n *\n * Given ANY child element of the .gel-cart-items-group__product element... this method will return a true/false value\n * depending on if this product group has been queued up to be \"destroyed\".\n *\n * @param {Element} childProductGroupElement Required. Expecting a child element of the .gel-cart-items-group__product\n * DOM element.\n * @return {boolean} Will return true if product group has been queued up to be destroyed,\n * false if it has not.\n */\nconst isCartItemQueuedToBeDestroyed = (childProductGroupElement) => {\n const parentCartItemGroupWrapper = childProductGroupElement.closest('.gel-cart-items-group__product');\n\n return parentCartItemGroupWrapper.classList.contains('gel-cart-items-group__product--destroy');\n}\n\n/* =================================================================\n GL Code Funks\n================================================================= */\n/**\n * Gl Code Input Validation Handler\n *\n * This method will validate a selected GL Code using the html5 datalist input element. If a user decides to type a\n * random string that is not an available GL Code option, we will set the datalist input element to invalid. Additional\n * logic added to update the input element's value in the dom which the html5 datalist does not do. In addition, we will\n * recalculate all GL Code Totals if any Gl Code input field is changed.\n *\n * @return {void} validate a GL Code input on change and re-calculate all Gl Code totals.\n */\nconst glCodeInputValidationHandler = () => {\n const glCodeInputs = document.querySelectorAll('.js-gl-code-input');\n\n if (glCodeInputs.length === 0) return;\n\n glCodeInputs.forEach((glCodeInput) => {\n const availableGlCodes = getAvailableGlCodeDatalistValues(glCodeInput);\n\n glCodeInput.addEventListener('change', (event) => {\n const targetInput = event.currentTarget;\n\n if (availableGlCodes.includes(targetInput.value)) {\n targetInput.setAttribute('value', targetInput.value);\n targetInput.setCustomValidity('');\n targetInput.classList.remove('is-invalid');\n } else {\n targetInput.setAttribute('value', 'Unknown Gl Code');\n targetInput.setCustomValidity('Unknown Gl Code');\n targetInput.classList.add('is-invalid');\n }\n\n glCodeTotalsHandler();\n });\n });\n}\n\n/**\n * Get Available GL Code Datalist Values\n *\n * Get all of the available GL Code options for the passed in Gl Code input element.\n *\n * @param {HTMLElement} glCodeInputElement Target GL Code input we want to gather available GL Code options for.\n * @return {Array} availableGlCodes Array of all of the available selectable GL Code options.\n */\nconst getAvailableGlCodeDatalistValues = (glCodeInputElement) => {\n let availableGlCodes = []\n if (!glCodeInputElement) return availableGlCodes;\n\n const glCodeInputWrapperElement = glCodeInputElement.closest('.input-group');\n if (!glCodeInputWrapperElement) return availableGlCodes;\n\n const glCodeDatalistElement = glCodeInputWrapperElement.querySelector('#available_gl_codes');\n if (!glCodeDatalistElement) return availableGlCodes;\n\n const availableGlCodeOptions = glCodeDatalistElement.querySelectorAll('option');\n if(availableGlCodeOptions.length === 0) return availableGlCodes;\n\n availableGlCodeOptions.forEach((glCodeOption) => {\n availableGlCodes.push(glCodeOption.value);\n });\n\n return availableGlCodes;\n}\n\n/**\n * GL Code Totals Handler\n *\n * Entry point method that triggers the calculations of all GL Code Totals in the dom.\n *\n * @return {void} This method will inject all Gl Code totals via the Cart's \"Order Details\" section above\n * \"Distributor Total\".\n */\nconst glCodeTotalsHandler = () => {\n const glCodeTotalWrapperElement = document.querySelector('.gel-cart-details__gl-code-totals');\n const activeGlCodeInputs = document.querySelectorAll('.js-gl-code-input:not([disabled])');\n\n showOrHideGlCodeTotalsHandler(activeGlCodeInputs, glCodeTotalWrapperElement);\n\n const glCodeTotalsData = calculateGlCodeTotals(activeGlCodeInputs);\n const glCodeTotalsRowsMarkup = createGlCodeTotalRowsHTML(glCodeTotalsData);\n\n removeAllChildElements(glCodeTotalWrapperElement);\n\n if (glCodeTotalsRowsMarkup.length === 0) return;\n\n glCodeTotalsRowsMarkup.forEach((glCodeRowElement) => {\n glCodeTotalWrapperElement.appendChild(glCodeRowElement);\n });\n\n const hrElement = document.createElement('hr');\n glCodeTotalWrapperElement.appendChild(hrElement);\n}\n\n/**\n * Show or Hide Gl Code Totals Handler\n *\n * This method will show/hide the Gl Code totals displayed via the Cart show pages' \"order details.\" If there are not\n * active Gl Codes in the dom, we will hide the totals. If there are active Gl Codes in the dom, then we will show them\n * in the \"order details\" section of the cart show page.\n *\n * @param {NodeListOf} activeGlCodeInputs An array of active Gl Code inputs.\n * @param {HTMLElement} glCodeTotalsWrapperElement The dom element which contains all of the calculated Gl Code\n * totals.\n * @return {void} Show or hide cart view's Gl Code Totals via order detail\n * section.\n */\nconst showOrHideGlCodeTotalsHandler = (activeGlCodeInputs, glCodeTotalsWrapperElement) => {\n if (activeGlCodeInputs.length === 0) {\n if (!glCodeTotalsWrapperElement.classList.contains('d-none')) glCodeTotalsWrapperElement.classList.add('d-none');\n } else {\n glCodeTotalsWrapperElement.classList.remove('d-none');\n }\n}\n\n/**\n * Calculate Gl Code Totals\n *\n * Calculates the totals for active Gl Code elements in the dom.\n *\n * @param {NodeListOf} activeGlCodeInputs An array of active Gl Code inputs.\n * @return {Object} glCodeTotals An object containing active GL Code keys with product price as its\n * value.\n */\nconst calculateGlCodeTotals = (activeGlCodeInputs) => {\n let glCodeTotals = {}\n if (!activeGlCodeInputs) return glCodeTotals;\n\n activeGlCodeInputs.forEach((activeGlCodeInput) => {\n const quantitySelectorWrapper = activeGlCodeInput.closest('.gel-quantity-selector');\n const toBeDestroyed = isCartItemQueuedToBeDestroyed(activeGlCodeInput);\n const glCodePrice = getQuantitySelectorGlCodeTotal(quantitySelectorWrapper);\n\n if(!toBeDestroyed) {\n if (activeGlCodeInput.value in glCodeTotals) {\n glCodeTotals[activeGlCodeInput.value] += glCodePrice;\n } else {\n glCodeTotals[activeGlCodeInput.value] = glCodePrice;\n }\n }\n });\n\n return glCodeTotals\n}\n\n/**\n * Get Quantity Selector Gl Code Total\n *\n * This method will return the product \"price\" for a GL Code as a Number to be used for calculating GL Code totals. Note\n * that depending on if the product is coop or not, there are different places we need to pull the GL Code \"price\" from.\n *\n * @param {HTMLElement} quantitySelectorElement The parent wrapper of the Quantity Selector widget. If a different type\n * of element is passed, or the mark-up has changed, we won't be able to\n * parse the correct product price.\n * @return {Number} The number value of the product's original price * quantity.\n */\nconst getQuantitySelectorGlCodeTotal = (quantitySelectorElement) => {\n const quantityInput = quantitySelectorElement.querySelector('.js-quantity-selector-input');\n const originalPriceElement = quantitySelectorElement.querySelector('.gel-quantity-selector__row--original-price');\n const priceElement = quantitySelectorElement.querySelector('.gel-quantity-selector__row--price');\n const targetPriceElement = priceElement ? priceElement : originalPriceElement;\n const targetPriceValueElement = targetPriceElement.querySelector('.gel-quantity-selector__field-value');\n\n return Number(targetPriceValueElement.innerText) * Number(quantityInput.value)\n}\n\n/**\n * Create Gl Code Totals HTML\n *\n * This method will create the Gl Code Totals mark-up to display on the cart's \"order details\" section given proper Gl\n * Code totals data via local calculateGlCodeTotals() method.\n *\n * @param {Object} glCodeTotalsData An object containing active GL Code keys with product price as its value.\n * @return {Array} glCodeTotalsMarkup The mark-up containing Gl Code totals to be injected into the dom.\n */\nconst createGlCodeTotalRowsHTML = (glCodeTotalsData) => {\n let glCodeRowsMarkup = []\n if (!glCodeTotalsData) return glCodeRowsMarkup;\n\n Object.keys(glCodeTotalsData).forEach((key) => {\n const glCodeTotalRowHTML = document.createElement('div');\n glCodeTotalRowHTML.setAttribute('class', 'row justify-content-between align-items-center');\n\n const glCodeHTML = document.createElement('div');\n glCodeHTML.setAttribute('class', 'col-6 form-label mb-0');\n glCodeHTML.innerText = key;\n\n const glCodePriceHTML = document.createElement('div');\n glCodePriceHTML.setAttribute('class', 'col-auto h5');\n glCodePriceHTML.appendChild(createSpanWithCssClass('$', 'js-price-prefix'));\n glCodePriceHTML.appendChild(createSpanWithCssClass(String(glCodeTotalsData[key].toFixed(2)), 'js-price-total'))\n\n glCodeTotalRowHTML.appendChild(glCodeHTML);\n glCodeTotalRowHTML.appendChild(glCodePriceHTML);\n\n glCodeRowsMarkup.push(glCodeTotalRowHTML);\n });\n\n return glCodeRowsMarkup;\n}\n/* =================================================================\n Product Line Item Form Input Funks\n================================================================= */\n/**\n * Remove Cart Item Handler\n *\n * This method is specifically used in the Cart show page's form submission. It will remove the selected Cart Item\n * product from the dom. If this is the last product on the page, it will display the empty cart message in the view.\n * This removes the selected Cart Items from the session Cart object on \"Update Cart\" because the \"removed\" Cart Items\n * will not get posted to the update/create controller action. As a result, the \"removed\" Cart Item will not get saved\n * to the new session Cart object instance. Note that, if you refresh the cart show page without updating... these\n * \"removed\" cart items will come back because the update/create controller action logic hasn't been run yet.\n *\n * @return {void} Remove selected product, re-calculate group data fields, and possibly display empty cart message if no\n * more products are present in the view.\n */\nconst removeCartItemHandler = () => {\n const removeCartItemButtons = document.querySelectorAll('.js-remove-cart-item');\n\n if(removeCartItemButtons.length === 0) return;\n\n removeCartItemButtons.forEach((removeCartItemButton) => {\n removeCartItemButton.addEventListener('click', (event) => {\n // Remove single selected product or full product group if selected cart item is the last one in the list\n const currentRemoveCartItemButton = event.currentTarget;\n const currentHiddenDestroyCheckbox = currentRemoveCartItemButton.querySelector('#cart_items_attributes___destroy');\n const currentProductWrapper = currentRemoveCartItemButton.closest('.gel-cart-items-group__product');\n\n currentProductWrapper.classList.toggle('gel-cart-items-group__product--destroy');\n currentHiddenDestroyCheckbox.value = currentProductWrapper.classList.contains('gel-cart-items-group__product--destroy')\n cartItemGroupDataOnLoad();\n\n return false;\n });\n });\n}\n/* =================================================================\n Misc. Helper Funks\n================================================================= */\n/**\n * Remove All Child Elements\n *\n * This is actually a safe and recommended way to remove child elements from a parent node because this method will also\n * remove event listeners from the removed child nodes. As a result, this will prevent memory leaks.\n *\n * TODO: This should be probably be moved to a javascript utility file to be imported where needed so this method can be shared in-between different javascript files.\n *\n * Doc: https://www.javascripttutorial.net/dom/manipulating/remove-all-child-nodes/\n *\n * @param parentElement\n */\nconst removeAllChildElements = (parentElement) => {\n while(parentElement.firstChild) {\n parentElement.removeChild(parentElement.firstChild);\n }\n}\n\n/**\n * Create Span with CSS Class\n *\n * This method will create and return a new javascript span element based off of passed in params of text and cssClass.\n *\n * @param {String} text String to be injected into the created span element.\n * @param {String} cssClass String representation of one css class to be set on the new span element.\n * @return {HTMLElement} spanElement New span element created with text and css class attributes.\n */\nconst createSpanWithCssClass = (text, cssClass) => {\n const spanElement = document.createElement('SPAN');\n spanElement.classList.add(cssClass);\n spanElement.innerText = text;\n return spanElement;\n}\n","import validateForms from '../form-validation';\nimport { quantitySelectorHandler } from '../components/quantity-selector';\nimport { productLineItemsHandler } from \"../components/product-line-items-handler\"\n\n/**\n * Cart Handler (Public/Exported)\n *\n * This is the entry point method for the CartController based views that gets called in main.js's `ready()` method.\n */\nexport const cartOnReady = () => {\n // Final State Method Calls\n validateForms();\n quantitySelectorHandler();\n productLineItemsHandler();\n\n // Methods yet to possibly generalized...\n cartCustomerPOItemCheckboxHandler();\n unsavedFormUpdatesAlertHandler();\n validateQuantityInputsHandler();\n}\n\n/* =====================================================================================================================\n CART'S \"ARE YOU SURE YOU WANT TO LEAVE?\" ALERT MESSAGE METHODS\n===================================================================================================================== */\nconst cartViewSessionStorage = window.sessionStorage;\n\n/**\n * Unsaved Form Updates Alert Handler\n *\n * This method calls all related methods to handle the presentation of a browser alert message if a user tries to\n * navigate away from the Cart show page after updating any form input. In other words, it warns the user that they have\n * unsaved Cart changes.\n *\n * @return {void} Calls related alert handler methods and initializes cartViewSessionStorage data with initial form data\n * on initial page load.\n */\nconst unsavedFormUpdatesAlertHandler = () => {\n // Initialize session storage data fields with current form data...\n storeOriginalCartFormData(serializeCurrentCartFormData());\n storePreventAlertBool(false);\n\n preventAlertMessageEventListener();\n presentAlertMessageEventListener();\n}\n\n/**\n * Prevent Alert Message Event Listener\n *\n * This is an \"on click\" event listener put directly on the \"Update Cart\" and \"Submit Order\" buttons present via the\n * Cart show page. If either of these buttons are clicked on by the user, we add a special boolean flag to the\n * cartViewSessionStorage object to let us know that we dont want to present an Alert message about unsaved data.\n *\n * @return {void} Add true flag to cartViewSessionStorage about skipping Alert message on update/submit button click.\n */\nconst preventAlertMessageEventListener = () => {\n const cartSubmissionButtons = document.querySelectorAll('.js-cart-submission');\n\n if (cartSubmissionButtons.length > 0) {\n cartSubmissionButtons.forEach((submissionButton) => {\n submissionButton.addEventListener('click', (_) => {\n storePreventAlertBool(true);\n });\n });\n }\n}\n\n/**\n * Present Alert Message Event Listener\n *\n * This method uses the browser's \"onbeforeunload\" web hook to trigger a default \"are you sure you want to leave this\n * page?\" message if there is still unsaved data in the Cart's form. We won't present an Alert IF AND ONLY IF our\n * prevent alert session storage boolean was set too true OR the form data hasn't changed.\n *\n * Doc: https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload\n * Example Usage: https://stackoverflow.com/questions/147636/best-way-to-detect-when-a-user-leaves-a-web-page#answer-5893539\n *\n * @return {void} Presents an \"Are you sure\" browser message depending on different conditions.\n */\nconst presentAlertMessageEventListener = () => {\n window.onbeforeunload = (event) => {\n // Don't present alert message if update or submit buttons were clicked...\n if (getPreventAlertBool()) {\n cartViewSessionStorage.clear();\n return\n }\n\n // Don't present alert message id form data hasn't changed...\n if(isSameString(getOriginalCartFormData(), serializeCurrentCartFormData())) {\n cartViewSessionStorage.clear();\n return\n }\n\n // Default browser logic to present \"Are you sure you want to leave\" alert message...\n event.preventDefault();\n event.returnValue = '';\n }\n}\n\n/**\n * Serialize Current Cart Form Data\n *\n * This method will get the current dom instance of the Cart show page's form element and then return all of its data\n * serialized into a string.\n *\n * @return {String} String representation of all of the form data inputs.\n */\nconst serializeCurrentCartFormData = () => {\n const cartForm = document.querySelector('.js-cart-form');\n return serializeFormData(cartForm);\n}\n\n/**\n * Store Original Cart Form Data\n *\n * This session storage key should hold the initial serialized form data on initial page load. This way, we can compare\n * the initial payload against a later one captured when a user tries to navigate away from the Cart page.\n *\n * @param {String} data Serialized form data to be saved to our cart view session storage object.\n * @return {void} Serialized form data is saved to cartViewSessionStorage object via originalCartFormData key.\n */\nconst storeOriginalCartFormData = (data) => {\n cartViewSessionStorage.setItem('originalCartFormData', data);\n}\n\n/**\n * Get Original Cart Form Data\n *\n * This method will pull our the original serialized form data from our cartViewSessionStorage object by the correct\n * parameter key.\n *\n * @return {String} Original serialized form data that should have been captured on initial page load.\n */\nconst getOriginalCartFormData = () => {\n return cartViewSessionStorage.getItem('originalCartFormData');\n}\n\n/**\n * Store Prevent Alert Bool\n *\n * This method will save the passed in boolean value into the cartViewSessionStore object by the correct parameter key.\n * Notice that we have to store the boolean value as a string in our SessionStorage object since it only allows data to\n * be stored as strings.\n *\n * @param {Boolean} data True/False boolean value of whether or not we want to skip \"Are you sure?\" alert message.\n * @return {void} Store passed in boolean value as a string via cartViewSessionStorage object.\n */\nconst storePreventAlertBool = (data) => {\n if(typeof(data) !== 'boolean') return;\n\n cartViewSessionStorage.setItem('preventAlertBool', data === true ? 'true' : 'false');\n}\n\n/**\n * Get Prevent Alert Bool\n *\n * Get prevent alert boolean value out of the cartViewSessionStorage using the correct parameter key. We will also\n * convert the stored string 'true' or 'false' values into true boolean values.\n *\n * @return {Boolean} true or false boolean value of whether or not we want to skip \"Are you sure?\" alert message.\n */\nconst getPreventAlertBool = () => {\n const preventAlertBool = cartViewSessionStorage.getItem('preventAlertBool')\n\n if(!preventAlertBool) return false;\n\n return preventAlertBool === 'true';\n}\n\n/**\n * Is Same String?\n *\n * This method will compare to strings and return a boolean value to tell us if they are the same or not.\n *\n * Doc: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare\n *\n * @param {String} stringOne First string we are comparing.\n * @param {String} stringTwo Second string we are comparing.\n * @return {Boolean} Boolean value of whether or not these two strings are the same or not.\n */\nconst isSameString = (stringOne, stringTwo) => {\n return stringOne.localeCompare(stringTwo) === 0;\n}\n\n/**\n * Serialize Form Data\n *\n * Returns one string payload which represents the current data filled into the passed in form element. Note that we\n * are ignoring the Rails `authenticity_token` parameter because it will always change as the form data changes. This\n * would not allow us to compare two different instances of serialized for data inputs updated by the user.\n *\n * Doc: https://developer.mozilla.org/en-US/docs/Web/API/FormData\n *\n * @param {Element} formElement Instance of the form element we are trying to serialize data from.\n * @return {string} Serialized form data of all form input key/value pairs as one string.\n */\n\nconst serializeFormData = (formElement) => {\n const formData = new FormData(formElement);\n let formDataObject = {};\n for(let [key, value] of formData) {\n if (key !== 'authenticity_token') formDataObject[key] = value;\n }\n return JSON.stringify(formDataObject);\n}\n\n/* =====================================================================================================================\n CART FORM ELEMENT METHODS\n===================================================================================================================== */\n\n/**\n * Cart Customer PO Item Checkbox Handler\n *\n * Entry point for the \"Use this PO for my entire order\" checkbox.\n *\n * @return {void} Will call related methods on load and on change for \"entire order\" checkbox to function.\n */\nconst cartCustomerPOItemCheckboxHandler = () => {\n const useFullOrderCheckbox = document.querySelector('.js-use-full-customer-po-checkbox');\n\n if (!useFullOrderCheckbox) return;\n\n // Run Cart Customer PO input logic on page load.\n cartFullOrderPoCheckboxHandler(useFullOrderCheckbox);\n\n // Run Cart Customer PO input logic when \"entire order\" checkbox is changed.\n useFullOrderCheckbox.addEventListener('change', () => {\n cartFullOrderPoCheckboxHandler(useFullOrderCheckbox);\n });\n}\n\n/**\n * Cart Full Order Po Checkbox Handler\n *\n * This method will toggle the disable/enable logic for Customer PO inputs depending on the \"entire order\" checkbox's\n * state. If it is checked, we will disable all Customer PO inputs under each product type group and enable the\n * \"Customer PO\" input meant for the full order. If it is not checked, we disable the \"Customer PO\" input meant for the\n * full order and enable all of the other ones in each product type group.\n *\n * @param {Element} fullOrderCheckbox Required instance of the \"entire order\" checkbox field.\n * @return {void} Updates the disabled/enabled status of Customer PO inputs based off of the\n * \"entire order\" checkbox.\n */\nconst cartFullOrderPoCheckboxHandler = (fullOrderCheckbox) => {\n // Disable/enable Customer PO inputs for product type groups based off of \"entire order\" checkbox field's status.\n const customerPoInputsByProductType = document.querySelectorAll('.js-customer-po-by-type-input');\n if (customerPoInputsByProductType.length !== 0) {\n customerPoInputsByProductType.forEach((customerPoInput) => {\n customerPoInput.disabled = fullOrderCheckbox.checked;\n });\n }\n\n // Find parent Customer PO form group via \"Order Details\" section of cart show page.\n const fullCustomerPoFormGroup = fullOrderCheckbox.closest('.js-full-customer-po-form-group');\n if (!fullCustomerPoFormGroup) return;\n\n // Find full Customer PO input field based off of parent wrapper.\n const fullCustomerPoInput = fullCustomerPoFormGroup.querySelector('.js-full-customer-po-input');\n if(!fullCustomerPoInput) return;\n\n // Disable/enable full Customer PO input depending on \"entire order\" checkbox field's status. Require full Customer\n // Po input if checkbox is checked!\n fullCustomerPoInput.disabled = !fullOrderCheckbox.checked;\n}\n\n/* =====================================================================================================================\n CART'S CUSTOM FORM VALIDATIONS\n===================================================================================================================== */\n\n/**\n * Validate Quantity Inputs Handler\n *\n * Loops through all Quantity Inputs via Cart show page in order to validate the input value. Quantity input value must\n * be greater than zero!\n *\n * @return {void} Attaches necessary event handlers to validate cart item quantity inputs.\n */\nconst validateQuantityInputsHandler = () => {\n const quantitySelectorInputs = document.querySelectorAll('.js-quantity-selector-input');\n if (quantitySelectorInputs.length === 0) return;\n\n quantitySelectorInputs.forEach((quantityInput) => {\n validateNumberInputMin(quantityInput)\n\n quantityInput.addEventListener('change', (event) => {\n const input = event.currentTarget;\n validateNumberInputMin(input)\n });\n });\n}\n\n/**\n * Validate Number Input Min\n *\n * This method takes in a number input element and a min number to be validated with. If the number input's value is not\n * more than the defined min number, than a custom bootstrap validator will be set.\n *\n * @param {HTMLElement} numberInput Number input field to be validated against.\n * @param {Number} min_number The min number that the numberInput value can be set to.\n * @return {void} Input gets set or unset with custom validity values for bootstrap to catch on the\n * front end.\n */\nconst validateNumberInputMin = (numberInput, min_number = 0) => {\n if (Number(numberInput.value) === Number(min_number) || Number(numberInput.value) < Number(min_number)) {\n numberInput.setCustomValidity('The quantity input can not be zero!');\n } else {\n numberInput.setCustomValidity('');\n }\n}","import Modal from 'bootstrap/js/dist/modal.js';\n\n/**\n * Dynamic Bootstrap Modal Handler (Public/Exported)\n *\n * Entry point for custom/dynamic bootstrap modal logic.\n *\n * @return {void} Calls all related methods to initialize dynamic bootstrap modal logic.\n */\nexport const dynamicBootstrapModalHandlers = () => {\n dynamicLinkToConfirmationModalHandler();\n}\n\n/**\n * Dynamic Confirmation Modal Handler\n *\n * This method will dynamically create a bootstrap modal if an tag element that has properly formatted data\n * attributes. For example:\n *\n * \n *\n * Where the href attribute will be the redirect when the \"submit\" button is clicked! Cancel will just close and remove\n * the modal from the dom!\n */\nconst dynamicLinkToConfirmationModalHandler = () => {\n const modalConfirmationLinkTo = document.querySelectorAll('a[data-modal-confirmation=true]');\n\n if (modalConfirmationLinkTo.length === 0) return;\n\n modalConfirmationLinkTo.forEach((linkToBtn) => {\n linkToBtn.addEventListener('click', (event) => {\n event.preventDefault();\n event.stopPropagation();\n\n presentLinkToConfirmationModal(event.currentTarget);\n });\n });\n}\n\n/**\n * Present Link To Confirmation Modal\n *\n * This logic builds and presents the confirmation modal based off of the set data attributes on the original a tag link\n * clicked. It will also remove the injected bootstrap modal from the dom if closed.\n *\n * @param {HTMLElement} linkToBtn The initial a tag that was configured to present a modal.\n * @return {void} Modal is injected and presented in the dom. If user closes the modal, it will be\n * removed from the dom.\n */\nconst presentLinkToConfirmationModal = (linkToBtn) => {\n // Gather required link to params... to pass into modal submit link button.\n const linkToHref = linkToBtn.href;\n\n // Build modal based off of bootstrap specifications...\n const siteBodyElement = document.querySelector('body');\n const confirmationModal = buildModalWrapperElement();\n const modalDialog = buildModalDialogElement();\n const modalContent = buildModalContentElement();\n const modalBody = buildModalBodyElement(linkToBtn.dataset);\n const modalFooter = buildModalFooterElement(linkToBtn.dataset, linkToHref);\n modalContent.appendChild(modalBody);\n modalContent.appendChild(modalFooter);\n modalDialog.appendChild(modalContent);\n confirmationModal.appendChild(modalDialog);\n siteBodyElement.appendChild(confirmationModal);\n\n // Show modal as soon as it has been built.\n let confirmationModalBootstrapInstance = new Modal(confirmationModal, {});\n confirmationModalBootstrapInstance.show();\n\n // Remove modal from dom once closed...\n confirmationModal.addEventListener('hidden.bs.modal', (event) => {\n event.currentTarget.remove();\n });\n}\n\n/**\n * Build Modal Wrapper Element\n *\n * Create the modal's outer most html element with bootstrap related attributes needed to support bootstrap modal logic.\n *\n * Doc: https://getbootstrap.com/docs/5.0/components/modal/#examples\n *\n * @return {HTMLDivElement} modalWrapper The outer most div element for the bootstrap modal mark-up based off of\n * bootstrap documentation.\n */\nconst buildModalWrapperElement = () => {\n const modalWrapper = document.createElement('div');\n modalWrapper.classList.add('modal', 'fade');\n modalWrapper.setAttribute('id','link-to-confirmation-modal');\n modalWrapper.setAttribute('aria-labelledby', 'linkToConfirmationModal');\n modalWrapper.setAttribute('aria-hidden', 'true');\n modalWrapper.setAttribute('tabindex', '-1');\n return modalWrapper;\n}\n\n/**\n * Build Modal Dialog Element\n *\n * Create the modal's dialog html element with bootstrap related attributes needed to support bootstrap modal logic.\n *\n * Doc: https://getbootstrap.com/docs/5.0/components/modal/#examples\n *\n * @return {HTMLDivElement} modalDialog The dialog div element for the bootstrap modal mark-up based off of bootstrap\n * documentation.\n */\nconst buildModalDialogElement = () => {\n const modalDialog = document.createElement('div');\n modalDialog.classList.add('modal-dialog');\n return modalDialog;\n}\n\n/**\n * Build Modal Content Element\n *\n * Create the modal's content html element with bootstrap related attributes needed to support bootstrap modal logic.\n *\n * Doc: https://getbootstrap.com/docs/5.0/components/modal/#examples\n *\n * @return {HTMLDivElement} modalContent The content div element for the bootstrap modal mark-up based off of bootstrap\n * documentation.\n */\nconst buildModalContentElement = () => {\n const modalContent = document.createElement('div');\n modalContent.classList.add('modal-content');\n return modalContent;\n}\n\n/**\n * Build Modal Body Element\n *\n * Create the modal's body html element with bootstrap related attributes needed to support bootstrap modal logic.\n *\n * Doc: https://getbootstrap.com/docs/5.0/components/modal/#examples\n * @param {DOMStringMap} linkToData The original a tag's data attributes used to parse data from.\n * @return {HTMLDivElement} modalBody The body div element for the bootstrap modal element based off of bootstrap\n * documentation.\n */\nconst buildModalBodyElement = (linkToData) => {\n const modalBody = document.createElement('div');\n modalBody.classList.add('modal-body', 'pt-4', 'px-4');\n\n if (linkToData['modalTitle']) modalBody.appendChild(buildModalTitleElement(linkToData['modalTitle']));\n if (linkToData['modalBody']) modalBody.appendChild(buildModalBodyTextElement(linkToData['modalBody']));\n\n return modalBody;\n}\n\n/**\n * Build Modal Title Element\n *\n * Create the modal's body's title element with default title if none was provided.\n *\n * @param {String} titleText Optional title text input to display as the modal's title.\n * @return {HTMLElement} titleElement The body's title element for the bootstrap modal mark-up.\n */\nconst buildModalTitleElement = (titleText = '') => {\n if (titleText === '') titleText = 'Are you sure you want to proceed? ';\n const titleElement = document.createElement('strong');\n titleElement.classList.add('mb-3', 'd-block');\n titleElement.innerText = titleText;\n return titleElement;\n}\n\n/**\n * Build Modal Body Text Element\n *\n * Create the modal's body's text element with default message if none was provided.\n *\n * @param {String} bodyText Optional body text input to display as the modal's body message.\n * @return {HTMLElement} bodyTextElement The body's text element for the bootstrap modal mark-up.\n */\nconst buildModalBodyTextElement = (bodyText = '') => {\n if (bodyText === '') bodyText = 'Please confirm if you would like to submit or cancel.';\n const bodyTextElement = document.createElement('p');\n bodyTextElement.innerText = bodyText;\n return bodyTextElement;\n}\n\n/**\n * Build Modal Footer Element\n *\n * Create the modal's footer html element with bootstrap related attributes needed to support bootstrap modal logic.\n * This element will contain the modal's buttons required to respond to the confirmation message.\n *\n * Doc: https://getbootstrap.com/docs/5.0/components/modal/#examples\n *\n * @param {DOMStringMap} linkToData The original a tag's data attributes used to parse data from.\n * @param {String} submitToLink The original a tag's href link data to transfer to the modal's submit\n * button.\n * @return {HTMLDivElement} modalFooterElement The footer div element for the bootstrap modal mark-up based off of\n * bootstrap documentation.\n */\nconst buildModalFooterElement = (linkToData, submitToLink) => {\n const modalFooterElement = document.createElement('div');\n modalFooterElement.classList.add('modal-footer', 'border-0', 'px-4', 'pb-4');\n\n if(linkToData['modalSubmitLabel']) modalFooterElement.appendChild(buildModalSubmitButtonElement(linkToData['modalSubmitLabel'], submitToLink, linkToData['method']));\n if(linkToData['modalCancelLabel']) modalFooterElement.appendChild(buildModalCancelButtonElement(linkToData['modalCancelLabel']));\n\n return modalFooterElement;\n}\n\n/**\n * Build Modal Submit Button Element\n *\n * Create the modal's footer's submit button html element with the original a tag's link_to attributes. We want this\n * button to complete the original a tag's href destination that we prevented from executing in\n * dynamicLinkToConfirmationModalHandler method's event.preventDefault() and event.stopPropagation() calls.\n *\n * @param {String} submitButtonText Optional submit button's inner text. We will set default text to\n * \"Submit\" if no value was provided.\n * @param {String} submitToLink The original a tag's href link data to transfer to the modal's submit\n * button. We will set it to \"#\" if nothing was provided.\n * @param {String} submitToMethod Optional data-method parameter which is used in rail's link_to tag helper\n * to post param data to different controller actions.\n * @return {HTMLElement} submitButtonElement The footer's submit button element for the bootstrap modal mark-up based\n * off of the original a tag's data attributes.\n */\nconst buildModalSubmitButtonElement = (submitButtonText = 'Submit', submitToLink = '#', submitToMethod = '') => {\n const submitButtonElement = document.createElement('a');\n submitButtonElement.classList.add('btn', 'btn-primary', 'btn-sm', 'w-auto');\n submitButtonElement.innerText = submitButtonText;\n submitButtonElement.setAttribute('href', submitToLink);\n submitButtonElement.setAttribute('data-disable-with', 'Please wait...');\n if (submitToMethod !== '') submitButtonElement.setAttribute('data-method', submitToMethod);\n\n return submitButtonElement;\n}\n\n/**\n * Build Modal Cancel Button Element\n *\n * Create the modal's footer's cancel button element with bootstrap related attributes needed to support bootstrap modal\n * logic. Attributes include requirements needed to automatically dismiss the modal on cancel.\n *\n * Doc: https://getbootstrap.com/docs/5.0/components/modal/#examples\n *\n * @param {String} cancelButtonText Optional text to use for the Modal's cancel button. We will set the\n * button's text to \"Cancel\" by default.\n * @return {HTMLElement} cancelButtonElement The footer div element's cancel button for the bootstrap modal mark-up\n * based off of bootstrap documentation.\n */\nconst buildModalCancelButtonElement = (cancelButtonText = 'Cancel') => {\n const cancelButtonElement = document.createElement('button');\n\n cancelButtonElement.setAttribute('type', 'button');\n cancelButtonElement.setAttribute('class','btn btn-outline-primary btn-sm w-auto');\n cancelButtonElement.setAttribute('data-bs-dismiss','modal');\n\n cancelButtonElement.innerText = cancelButtonText;\n\n return cancelButtonElement;\n}","import validateForms from \"../form-validation\";\nimport {selectSortOrder, getSelectedOption, clearFilters} from '../filters-utility';\nimport { quantitySelectorHandler } from '../components/quantity-selector';\n\n/**\n * Product On Ready (Public/Exported)\n *\n * This is the entry point method for the ProductController based views that gets called in main.js's `ready()` method.\n *\n * @return {void} Loads product related methods.\n */\nexport const productOnReady = () => {\n validateForms();\n clearProductFilters();\n handleProductSorting();\n handleProductSizeSelector();\n quantitySelectorHandler();\n}\n\n/**\n * Clear Product Filters\n *\n * Clears the product filter form and product view of any active filters using filters-utility.js logic. Note, DOM\n * mark-up should be similar to Product or Brand Asset index views.\n *\n * @returns {void}\n */\nconst clearProductFilters = () => {\n const clearFiltersBtns = document.querySelectorAll('.js-clear-product-filters');\n clearFilters(clearFiltersBtns, '#form-product-filters-collapse', '#form-product-filters');\n};\n\n/**\n * Handle Product Sorting\n *\n * Updates the product view with the chosen sort order using filters-utility.js logic. Note, DOM mark-up should be\n * similar to Product or Brand Asset index views.\n *\n * @returns {void} Sets sort order logic using product index view javascript query selectors.\n */\nconst handleProductSorting = () => {\n selectSortOrder('.js-product-sort-select', '#product-form-order-by-input', '#product-form-order-dir-input', '#form-product-filters');\n selectSortOrder('.js-product-sort-select-collapse', '#product-form-order-by-input-collapse', '#product-form-order-dir-input-collapse', '#form-product-filters-collapse');\n};\n\n/**\n * Handle Product Size Selector\n *\n * Navigates the user to the product reflecting the size variant they select. This is used for a product's show page.\n *\n * @returns {void} Redirect user based off of selected wearable product size.\n */\nconst handleProductSizeSelector = () => {\n const sizeSelector = document.querySelector('.js-size-variant-selector');\n if (!sizeSelector) {\n return;\n }\n\n sizeSelector.addEventListener('change', (e) => {\n const selectedOption = getSelectedOption(e.target);\n if (!selectedOption) {\n return;\n }\n\n const variantUrl = selectedOption.getAttribute('data-variant-url');\n if (!variantUrl) {\n return;\n }\n\n window.location.href = variantUrl;\n });\n};","/**\n * Set Shipping Address On Ready (Public/Exported)\n *\n * This is the entry point method for the SetShippingAddressesController based views that gets called in main.js's `ready()` method.\n */\nexport const setShippingAddressOnReady = () => {\n dropdownItemActiveToggleHandler();\n validateActiveOptionBeforeSubmitHandler();\n}\n\n/**\n * Dropdown Item Active Toggle Handler\n *\n * This method will set and remove an `active` class from the parent `dropdown-item` element depending on if a\n * `shipping_address` radio button was activated or not. This is so we can style the active state of the `dropdown-item`\n * element in scss.\n *\n * @return {void} This method toggles an `active` class on `dropdown-item` elements.\n */\nconst dropdownItemActiveToggleHandler = () => {\n const shippingAddressRadioOptions = document.getElementsByName('shipping_address');\n\n shippingAddressRadioOptions.forEach((shippingAddressOption) => {\n shippingAddressOption.addEventListener('change', (event) => {\n const activeDropdownItem = document.querySelector('.dropdown-item.active');\n if(activeDropdownItem) activeDropdownItem.classList.remove('active');\n\n const selectedDropdownItem = event.currentTarget.closest('.dropdown-item');\n selectedDropdownItem.classList.add('active');\n });\n });\n}\n\n/**\n * Validate Active Option Before Submit Handler\n *\n * This method will enable the \"Select a Shipping Address\" form once a user has selected a shipping address. Page is\n * loaded with the submit disabled so user's are forced to select an address.\n *\n * @return {void} Enables the submit button once a user selects an address.\n */\nconst validateActiveOptionBeforeSubmitHandler = () => {\n const formSubmitButton = document.querySelector('#submit-set-shipping-address');\n\n if(!formSubmitButton) return;\n\n const dropdownItemsRadioInputs = document.getElementsByName('shipping_address');\n\n if(dropdownItemsRadioInputs.length === 0) return;\n\n dropdownItemsRadioInputs.forEach((radioInput) => {\n radioInput.addEventListener('change', (event) => {\n let hasSetValue = false;\n dropdownItemsRadioInputs.forEach((radioInput) => {\n if (radioInput.checked) {\n hasSetValue = true;\n }\n });\n\n formSubmitButton.disabled = !hasSetValue;\n });\n });\n}\n\n","/**\n * Shipping Address Search Handler (Public/Exported)\n *\n * Root method called to initialize the Shipping Address search logic.\n *\n * @return {void} Calls all related methods to initialize the search input logic.\n */\nexport const shippingAddressSearchHandler = (shippingAddressSearchInput) => {\n if(!shippingAddressSearchInput) return;\n\n searchAddressInputOnKeyUp(shippingAddressSearchInput);\n searchAddressInputOnCloseBtnOrEnterKey(shippingAddressSearchInput);\n}\n\n/**\n * Search Address Input On Key Up\n *\n * This method adds an event listener to the passed in \"search input\" which will trigger any time a user types a new\n * character or removes a character from the search input field. Within this event listener, we trigger the search logic\n * which hides irrelevant bootstrap `dropdown-items` within the current `dropdown-menu` list.\n *\n * @param {HTMLElement} searchInput Javascript instance of the search input we are capturing data from.\n * @return {void} Will trigger the true comparison logic which show/hides `dropdown-items` based off\n * of search input.\n */\nconst searchAddressInputOnKeyUp = (searchInput) => {\n searchInput.addEventListener('keyup', (event) => {\n const searchInput = event.currentTarget\n searchInputHandler(searchInput);\n });\n}\n\n/**\n * Search Address Input On Close Button Or Enter Key\n *\n * This adds a browser based event listener to the passed in \"search input\" which triggers whenever a user submits a\n * search by pressing the enter key on their keyboard or by clicking on the browser's clear search (x) button.\n *\n * @param {HTMLElement} searchInput Javascript instance of the search input we are capturing data from.\n * @return {void} Will trigger the true comparison logic which show/hides `dropdown-items` based off\n * of search input.\n */\nconst searchAddressInputOnCloseBtnOrEnterKey = (searchInput) => {\n searchInput.addEventListener('search', (event) => {\n const searchInput = event.currentTarget;\n searchInputHandler(searchInput);\n });\n}\n\n/**\n * Search Input Handler\n *\n * True search logic which will compare the value the user typed into the search input field against all of the text\n * content inside of the `dropdown-item` data. If a match is found in any of the `dropdown-item`s within the\n * `dropdown-menu`'s list; the item will be displayed. Otherwise, the item will be hidden from the view. If a blank\n * search value is submitted, the full list will display all values again (like a search reset).\n *\n * @param {HTMLElement} searchInput Javascript instance of the search input we are capturing data from.\n * @return {void} Show/hide `dropdown-items` based off of search input.\n */\nconst searchInputHandler = (searchInput) => {\n const searchInputValue = searchInput.value.toLowerCase();\n const shippingAddressContainer = searchInput.closest('.js-set-shipping-address-container');\n const shippingAddressOptions = shippingAddressContainer.querySelectorAll('.js-shipping-label-address');\n\n shippingAddressOptions.forEach((shippingAddressOption) => {\n const shippingDropdownItem = shippingAddressOption.closest('.dropdown-item');\n const shippingAddressText = shippingAddressOption.textContent.toLowerCase();\n\n if (searchInputValue === '') {\n showElementWithBootstrapClasses(shippingDropdownItem);\n return;\n }\n\n (shippingAddressText.search(searchInputValue) >= 0) ? showElementWithBootstrapClasses(shippingDropdownItem) : hideElementWithBootstrapClasses(shippingDropdownItem);\n });\n}\n\n/**\n * Show Element With Bootstrap Classes\n *\n * Will show passed in html element using bootstrap css classes.\n *\n * @param {HTMLElement} element Passed in html element we want to show.\n * @return {void} Shows passed in element using bootstrap css classes.\n */\nconst showElementWithBootstrapClasses = (element) => {\n element.classList.remove('d-none');\n element.classList.add('d-block');\n}\n\n/**\n * Hide Element With Bootstrap Classes\n *\n * Will hide passed in html element using bootstrap css classes.\n *\n * @param {HTMLElement} element Passed in html element we want to hide.\n * @return {void} Hides passed in element using bootstrap css classes.\n */\nconst hideElementWithBootstrapClasses = (element) => {\n element.classList.remove('d-block');\n element.classList.add('d-none');\n}","/* eslint no-console:0 */\n// This file is automatically compiled by Webpack, along with any other files\n// present in this directory. You're encouraged to place your actual application logic in\n// a relevant structure within app/packs and only use these pack files to reference\n// that code so it'll be compiled.\n//\n// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate\n// layout file, like app/views/layouts/application.html.erb\n\n// Uncomment to copy all static images under ../images to the output folder and reference\n// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)\n// or the `imagePath` JavaScript helper below.\n//\n\nimport {deviseInvitationsOnReady} from \"../scripts/layouts/devise/invitations\";\n\nconst images = require.context('../images', true)\n// const imagePath = (name) => images(name, true)\n\n// import 'core-js/stable'\n// import 'regenerator-runtime/runtime'\n\n// Even though these look as though they're not used, they actually are required.\nimport Dropdown from 'bootstrap/js/dist/dropdown';\nimport Collapse from 'bootstrap/js/dist/collapse';\nimport Alert from 'bootstrap/js/dist/alert';\n\nimport '../stylesheets/main.scss';\n\nimport handleScrollBarStyles from '../scripts/scrollbar.js';\nimport navbarSearchToggle from '../scripts/navbar-search.js';\n\n// *** IMPORTANT! ***\n// Do not remove this line. For some reason, this is required for Devise's\n// logout functionality. Without this, calling destroy_user_session_path\n// will default to a GET despite telling it to use DESTROY, and will break.\n// TODO: is this still needed? it seems to be throwing errors on several pages. - rcrichlow - 08/05/21\nrequire(\"@rails/ujs\").start()\n\nconst dismissCollapseMenus = () => {\n window.addEventListener('click', (e) => {\n const target = e.target;\n\n if (!target.hasAttribute('aria-expanded') || !target.getAttribute('aria-expanded')) {\n const productSubmenu = document.getElementById('productMenuCollapse');\n const productSubmenuCollapse = Collapse.getInstance(productSubmenu);\n if (productSubmenuCollapse) {\n productSubmenuCollapse.hide();\n }\n }\n });\n}\n\n/**\n * Layout Imports\n *\n * This block imports any custom layout based views.\n */\nimport { brandAssetsOnReady } from \"../scripts/layouts/brand_assets\";\nimport { cartOnReady } from '../scripts/layouts/cart';\nimport { contactUsOnReady} from \"../scripts/layouts/contact_us\";\nimport { ordersOnReady } from '../scripts/layouts/orders';\nimport { devisePasswordsOnReady } from '../scripts/layouts/devise/passwords';\nimport { deviseRegistrationsOnReady } from '../scripts/layouts/devise/registrations';\nimport { dynamicBootstrapModalHandlers } from '../scripts/components/bootstrap-modal-handler';\nimport { productOnReady } from '../scripts/layouts/product';\nimport { requestAccessOnReady } from '../scripts/layouts/request_access';\nimport { setShippingAddressOnReady } from '../scripts/layouts/set_shipping_addresses';\nimport { shippingAddressRequestsOnReady } from '../scripts/layouts/shipping_address_requests';\nimport { shippingAddressSearchHandler } from '../scripts/components/shipping-address-search-handler';\n\nconst ready = () => {\n const bodyNode = document.getElementsByTagName('body')[0];\n\n handleScrollBarStyles();\n dismissCollapseMenus();\n navbarSearchToggle();\n dynamicBootstrapModalHandlers();\n\n if (bodyNode.classList.contains('brand_assets')) brandAssetsOnReady();\n\n if (bodyNode.classList.contains('carts')) cartOnReady();\n\n if (bodyNode.classList.contains('contact')) contactUsOnReady();\n\n if (bodyNode.classList.contains('orders')) ordersOnReady();\n\n const productControllerClasses = ['products', 'product_beverages', 'product_pos', 'product_wearables', 'product_promos']\n if (productControllerClasses.some(className => bodyNode.classList.contains(className))) productOnReady();\n\n if (bodyNode.classList.contains('request_access')) requestAccessOnReady();\n\n if (bodyNode.classList.contains('set_shipping_addresses')) setShippingAddressOnReady();\n\n if (bodyNode.classList.contains('shipping_address_requests')) shippingAddressRequestsOnReady();\n\n const shippingAddressSearchInput = document.querySelector('.js-search-shipping-addresses');\n shippingAddressSearchHandler(shippingAddressSearchInput);\n\n // Devise Views\n if (bodyNode.classList.contains('invitations')) deviseInvitationsOnReady();\n if (bodyNode.classList.contains('passwords')) devisePasswordsOnReady();\n if (bodyNode.classList.contains('registrations')) deviseRegistrationsOnReady();\n}\n\nif (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', ready);\n} else {\n ready();\n}\n","import validateForms from '../form-validation'\n\n/**\n * Contact Us On Ready (Public/Exported)\n *\n * This is the entry point method for the ContactController based views that gets called in main.js's `ready()`\n * method.\n */\nexport const contactUsOnReady = () => {\n validateForms();\n}","import { quantitySelectorHandler } from '../components/quantity-selector';\nimport validateForms from \"../form-validation\";\nimport { productLineItemsHandler } from \"../components/product-line-items-handler\"\n/**\n * Cart Handler (Public/Exported)\n *\n * This is the entry point method for the CartController based views that gets called in main.js's `ready()` method.\n */\nexport const ordersOnReady = () => {\n productLineItemsHandler();\n quantitySelectorHandler();\n validateForms();\n}","import validateForms from '../form-validation';\n\n/**\n * Request Access On Ready (Public/Exported)\n *\n * This is the entry point method for the RequestAccessController based views that gets called in main.js's `ready()`\n * method.\n *\n * @return {void} Loads request access form related methods.\n */\nexport const requestAccessOnReady = () => {\n validateForms();\n}","import validateForms from '../form-validation';\n\n/**\n * Shipping Address Requests On Ready (Public/Exported)\n *\n * This is the entry point method for the ShippingAddressRequestsController based views that gets called in main.js's\n * `ready()`method.\n *\n * @return {void} Loads request access form related methods.\n */\nexport const shippingAddressRequestsOnReady = () => {\n validateForms();\n}","import validateForms from '../../form-validation'\nimport PasswordValidation from '../../password-validation'\n\n/**\n * Devise Invitaions on Ready (Public/Exported)\n *\n * This is the entry point method for the Users::InvitationsController based views that gets called in main.js's `ready()`\n * method.\n */\nexport const deviseInvitationsOnReady = () => {\n validateForms();\n const passwordValidation = new PasswordValidation();\n passwordValidation.initPasswordValidation();\n}\n","import validateForms from '../../form-validation'\nimport PasswordValidation from '../../password-validation'\n\n/**\n * Devise Passwords on Ready (Public/Exported)\n *\n * This is the entry point method for the Users::PasswordsController based views that gets called in main.js's `ready()`\n * method.\n */\nexport const devisePasswordsOnReady = () => {\n validateForms();\n const passwordValidation = new PasswordValidation();\n passwordValidation.initPasswordValidation();\n}","import validateForms from '../../form-validation'\nimport PasswordValidation from '../../password-validation'\n\n/**\n * Registrations on Ready (Public/Exported)\n *\n * This is the entry point method for the Users::RegistrationsController based views that gets called in main.js's\n * `ready()` method.\n */\nexport const deviseRegistrationsOnReady = () => {\n validateForms();\n const passwordValidation = new PasswordValidation();\n passwordValidation.initPasswordValidation();\n}","const validateForms = () => {\n const forms = document.querySelectorAll('.needs-validation');\n\n if (forms) {\n forms.forEach((form) => {\n const formSubmitButtons = form.querySelectorAll('input[type=submit], button[type=submit]');\n formSubmitButtons.forEach((submitButton) => {\n submitButton.addEventListener('click', (event) => {\n const submitButtonElement = event.currentTarget;\n const parentFormElement = submitButtonElement.closest('form');\n if(!parentFormElement.checkValidity()) {\n event.preventDefault();\n event.stopPropagation();\n }\n\n parentFormElement.classList.add('was-validated');\n\n const invalidInputFields = parentFormElement.querySelectorAll('input.form-control:invalid');\n invalidInputFields[0].scrollIntoView({behavior: 'smooth', block: 'center'});\n });\n });\n });\n }\n};\n\nexport default validateForms;\n","const zxcvbn = require('zxcvbn');\n\nclass PasswordValidation {\n constructor() {\n this.minimumScore = 2;\n\n this.strength = {\n 0: \"Not strong enough.\",\n 1: \"Not strong enough.\",\n 2: \"Strong enough, but could be better.\",\n 3: \"Strong.\",\n 4: \"Very strong.\"\n }\n\n this.password = document.getElementById('user_password');\n this.passwordFeedback = document.getElementById('invalid-feedback-password');\n this.passwordShowButton = document.getElementById('show_password');\n this.meter = document.getElementById('strength-meter-fill');\n this.strengthText = document.getElementById('password-strength-text');\n\n this.passwordButtonTextHide = \"Hide\";\n this.passwordButtonTextShow = \"Show\";\n\n this.inputValue = \"\"\n this.result = {}\n }\n\n toggleShowPassword() {\n if (this.password.type === 'password') {\n this.password.type = \"text\";\n this.passwordShowButton.innerText = this.passwordButtonTextHide;\n } else {\n this.password.type = \"password\"\n this.passwordShowButton.innerText = this.passwordButtonTextShow;\n }\n }\n\n generateCustomValidation() {\n // TODO: these messages should live somewhere global.\n // custom password validation\n if (this.inputValue === \"\") {\n this.password.setCustomValidity(\"Missing password\");\n this.passwordFeedback.innerHTML = \"Please enter your password.\";\n } else if (this.result.score < this.minimumScore) {\n this.password.setCustomValidity(\"Password is not strong enough\");\n this.passwordFeedback.innerHTML = \"Please create a stronger password.\";\n } else {\n this.password.setCustomValidity(\"\");\n this.passwordFeedback.innerHTML = \"\";\n }\n }\n\n updateMeter() {\n this.meter.setAttribute('data-strength', this.result.score);\n }\n\n resetStrengthText() {\n this.strengthText.innerHTML = \"Password Strength:\"\n }\n\n // Update the text indicator\n updateTextIndicator() {\n if(this.inputValue !== \"\") {\n const strengthText = this.strength[this.result.score] + '. ';\n const feedbackWarning = this.result.feedback.warning + ' ';\n // const feedbackSuggestions = this.result.feedback.suggestions;\n const openingTag = \"\";\n\n this.strengthText.innerHTML = strengthText + openingTag + feedbackWarning + closingTag;\n } else {\n this.resetStrengthText()\n }\n }\n\n validatePassword(e) {\n this.inputValue = e.target.value;\n this.result = zxcvbn(this.inputValue);\n\n this.generateCustomValidation();\n this.updateMeter();\n this.updateTextIndicator();\n }\n\n initPasswordValidation() {\n if (this.password) {\n this.password.addEventListener('input', this.validatePassword.bind(this))\n\n if (this.passwordShowButton) {\n this.passwordShowButton.addEventListener('click', this.toggleShowPassword.bind(this))\n }\n }\n }\n}\n\nexport default PasswordValidation;\n"],"sourceRoot":""}