{"version":3,"file":"modal-cart-section-xxxxxxxxxxxxxxxxxxxx.min.js","mappings":"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","sources":["webpack://lange/webpack/bootstrap","webpack://lange/webpack/runtime/define property getters","webpack://lange/webpack/runtime/ensure chunk","webpack://lange/webpack/runtime/get javascript chunk filename","webpack://lange/webpack/runtime/get mini-css chunk filename","webpack://lange/webpack/runtime/hasOwnProperty shorthand","webpack://lange/webpack/runtime/load script","webpack://lange/webpack/runtime/publicPath","webpack://lange/webpack/runtime/jsonp chunk loading","webpack://lange/./scripts/sections/modal-cart/alert/index.ts","webpack://lange/./scripts/sections/modal-cart/cart-messages/index.ts","webpack://lange/./scripts/lib/splide-slidebar-extension.ts","webpack://lange/./scripts/lib/webpack-path.ts","webpack://lange/./scripts/sections/modal-cart/upsell-section/index.ts","webpack://lange/../node_modules/@integrabeauty/shopify-ajax-api/src/cart.ts","webpack://lange/./scripts/lib/call-utils.ts","webpack://lange/./scripts/common/liquid-global.ts","webpack://lange/./scripts/sections/modal-cart/update-cart-html.ts","webpack://lange/./scripts/sections/modal-cart/cart-item-remove-button/remove-items.ts","webpack://lange/./scripts/sections/modal-cart/cart-item-remove-button/index.ts","webpack://lange/./scripts/sections/modal-cart/cart-service/update-cart.ts","webpack://lange/./scripts/sections/modal-cart/cart-attribution.ts","webpack://lange/./scripts/sections/modal-cart/cart-page.ts","webpack://lange/./scripts/sections/modal-cart/modal-cart.ts","webpack://lange/./scripts/sections/modal-cart/cart-service/get-cart.ts","webpack://lange/./scripts/sections/modal-cart/cart-quantity-input.ts","webpack://lange/./scripts/sections/modal-cart/delivery-message/index.ts"],"sourcesContent":["// The module cache\nlet __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tlet cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tlet module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n// expose the modules object (__webpack_modules__)\n__webpack_require__.m = __webpack_modules__;\n\n","// define getter/value functions for harmony exports\n__webpack_require__.d = function(exports, definition) {\n\tif(Array.isArray(definition)) {\n\t\tvar i = 0;\n\t\twhile(i < definition.length) {\n\t\t\tvar key = definition[i++];\n\t\t\tvar binding = definition[i++];\n\t\t\tif(!__webpack_require__.o(exports, key)) {\n\t\t\t\tif(binding === 0) {\n\t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, value: definition[i++] });\n\t\t\t\t} else {\n\t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: binding });\n\t\t\t\t}\n\t\t\t} else if(binding === 0) { i++; }\n\t\t}\n\t} else {\n\t\tfor(var key in definition) {\n\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t\t}\n\t\t}\n\t}\n};","__webpack_require__.f = {};\n// This file contains only the entry chunk.\n// The chunk loading function for additional chunks\n__webpack_require__.e = function(chunkId) {\n\treturn Promise.all(Object.keys(__webpack_require__.f).reduce(function(promises, key) {\n\t\t__webpack_require__.f[key](chunkId, promises);\n\t\treturn promises;\n\t}, []));\n};","// This function allow to reference async chunks\n__webpack_require__.u = function(chunkId) {\n\t// return url for filenames based on template\n\treturn \"\" + chunkId + \"-\" + \"2d5466ec02b4471f0a5d\" + \".min.js\";\n};","// This function allow to reference async chunks\n__webpack_require__.miniCssF = function(chunkId) {\n\t// return url for filenames based on template\n\treturn undefined;\n};","__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }","let inProgress = {};\nlet dataWebpackPrefix = \"lange:\";\n// loadScript function to load a script via script tag\n__webpack_require__.l = function(url, done, key, chunkId) {\n\tif(inProgress[url]) { inProgress[url].push(done); return; }\n\tlet script, needAttach;\n\tif(key !== undefined) {\n\t\tlet scripts = document.getElementsByTagName(\"script\");\n\t\tfor(var i = 0; i < scripts.length; i++) {\n\t\t\tlet s = scripts[i];\n\t\t\tif(s.getAttribute(\"src\") == url || s.getAttribute(\"data-webpack\") == dataWebpackPrefix + key) { script = s; break; }\n\t\t}\n\t}\n\tif(!script) {\n\t\tneedAttach = true;\n\t\tscript = document.createElement('script');\n\n\t\tscript.charset = 'utf-8';\n\t\tif (__webpack_require__.nc) {\n\t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n\t\t}\n\t\tscript.setAttribute(\"data-webpack\", dataWebpackPrefix + key);\n\n\t\tscript.src = url;\n\t}\n\tinProgress[url] = [done];\n\tlet onScriptComplete = function(prev, event) {\n\t\t// avoid mem leaks in IE.\n\t\tscript.onerror = script.onload = null;\n\t\tclearTimeout(timeout);\n\t\tlet doneFns = inProgress[url];\n\t\tdelete inProgress[url];\n\t\tscript.parentNode && script.parentNode.removeChild(script);\n\t\tdoneFns && doneFns.forEach(function(fn) { return fn(event); });\n\t\tif(prev) return prev(event);\n\t}\n\tlet timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);\n\tscript.onerror = onScriptComplete.bind(null, script.onerror);\n\tscript.onload = onScriptComplete.bind(null, script.onload);\n\tneedAttach && document.head.appendChild(script);\n};","__webpack_require__.p = \"\";","// no baseURI\n\n// object to store loaded and loading chunks\n// undefined = chunk not loaded, null = chunk preloaded/prefetched\n// [resolve, reject, Promise] = chunk loading, 0 = chunk loaded\nlet installedChunks = {\n\t\"modal-cart-section\": 0\n};\n\n__webpack_require__.f.j = function(chunkId, promises) {\n\t\t// JSONP chunk loading for javascript\n\t\tlet installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;\n\t\tif(installedChunkData !== 0) { // 0 means \"already installed\".\n\n\t\t\t// a Promise means \"currently loading\".\n\t\t\tif(installedChunkData) {\n\t\t\t\tpromises.push(installedChunkData[2]);\n\t\t\t} else {\n\t\t\t\tif(true) { // all chunks have JS\n\t\t\t\t\t// setup Promise in chunk cache\n\t\t\t\t\tlet promise = new Promise(function(resolve, reject) { installedChunkData = installedChunks[chunkId] = [resolve, reject]; });\n\t\t\t\t\tpromises.push(installedChunkData[2] = promise);\n\n\t\t\t\t\t// start chunk loading\n\t\t\t\t\tlet url = __webpack_require__.p + __webpack_require__.u(chunkId);\n\t\t\t\t\t// create error before stack unwound to get useful stacktrace later\n\t\t\t\t\tlet error = new Error();\n\t\t\t\t\tlet loadingEnded = function(event) {\n\t\t\t\t\t\tif(__webpack_require__.o(installedChunks, chunkId)) {\n\t\t\t\t\t\t\tinstalledChunkData = installedChunks[chunkId];\n\t\t\t\t\t\t\tif(installedChunkData !== 0) installedChunks[chunkId] = undefined;\n\t\t\t\t\t\t\tif(installedChunkData) {\n\t\t\t\t\t\t\t\tlet errorType = event && (event.type === 'load' ? 'missing' : event.type);\n\t\t\t\t\t\t\t\tlet realSrc = event && event.target && event.target.src;\n\t\t\t\t\t\t\t\terror.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';\n\t\t\t\t\t\t\t\terror.name = 'ChunkLoadError';\n\t\t\t\t\t\t\t\terror.type = errorType;\n\t\t\t\t\t\t\t\terror.request = realSrc;\n\t\t\t\t\t\t\t\tinstalledChunkData[1](error);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\t__webpack_require__.l(url, loadingEnded, \"chunk-\" + chunkId, chunkId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n};\n\n// no prefetching\n\n// no preloaded\n\n// no HMR\n\n// no HMR manifest\n\n// no on chunks loaded\n\n// install a JSONP callback for chunk loading\nlet webpackJsonpCallback = function(parentChunkLoadingFunction, data) {\n\tlet chunkIds = data[0];\n\tlet moreModules = data[1];\n\tlet runtime = data[2];\n\t// add \"moreModules\" to the modules object,\n\t// then flag all \"chunkIds\" as loaded and fire callback\n\tvar moduleId, chunkId, i = 0;\n\tif(chunkIds.some(function(id) { return installedChunks[id] !== 0; })) {\n\t\tfor(moduleId in moreModules) {\n\t\t\tif(__webpack_require__.o(moreModules, moduleId)) {\n\t\t\t\t__webpack_require__.m[moduleId] = moreModules[moduleId];\n\t\t\t}\n\t\t}\n\t\tif(runtime) var result = runtime(__webpack_require__);\n\t}\n\tif(parentChunkLoadingFunction) parentChunkLoadingFunction(data);\n\tfor(;i < chunkIds.length; i++) {\n\t\tchunkId = chunkIds[i];\n\t\tif(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {\n\t\t\tinstalledChunks[chunkId][0]();\n\t\t}\n\t\tinstalledChunks[chunkId] = 0;\n\t}\n\n}\n\nlet chunkLoadingGlobal = self[\"webpackChunklange\"] = self[\"webpackChunklange\"] || [];\nchunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));\nchunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));","import type { CustomElement } from '@integrabeauty/custom-elements';\nimport html from './index.html';\nimport styles from './index.scss';\n\n/**\n * Renders an customer messaging\n */\nexport class AlertElement extends HTMLElement implements CustomElement {\n  timerId: ReturnType<typeof setTimeout>;\n\n  public static get observedAttributes() {\n    return [\n      'data-type'\n    ];\n  }\n\n  declare readonly dataset: {\n    /**\n     * Boolean represented as string to render the close button.\n     */\n    close: 'false' | 'true';\n\n    /**\n     * Boolean represented as string to hide/show the messaging component.\n     */\n    hidden: 'false' | 'true';\n\n    /**\n     * Position of the messaging component. Makes the component absolute and place the component\n     * according to the attribute value.\n     */\n    position: 'bottom-left' | 'bottom-right' | 'full' | 'top-left' | 'top-right';\n\n    /**\n     * Boolean represented as string to set the component as `position: fixe;`. It might be helpful\n     * in some cases with combination of the data-position attribute.\n     */\n    positionFixed: 'false' | 'true';\n\n    /**\n     * Time in milliseconds after which the component should hide.\n     */\n    timer: string;\n\n    /**\n     * Messaging severity level.\n     */\n    type: 'error' | 'info' | 'success';\n  };\n\n  private onCloseClickedBound = this.onCloseClicked.bind(this);\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n    this.render();\n  }\n\n  public disconnectedCallback() {\n    const closeButton = this.shadowRoot.querySelector('.close-button');\n    closeButton.removeEventListener('click', this.onCloseClickedBound);\n\n    if (this.timerId) {\n      clearTimeout(this.timerId);\n    }\n  }\n\n  private initializeShadowRoot() {\n    if (!this.shadowRoot) {\n      this.attachShadow({ mode: 'open' });\n      this.shadowRoot.innerHTML = `<style>${styles}</style>${html}`;\n    }\n  }\n\n  private render() {\n    const closeButton = this.shadowRoot.querySelector('.close-button');\n    closeButton.addEventListener('click', this.onCloseClickedBound);\n\n    if (this.dataset.timer) {\n      this.timerId = setTimeout(() => {\n        this.remove();\n      }, parseInt(this.dataset.timer, 10));\n    }\n  }\n\n  private onCloseClicked(_event: Event) {\n    this.remove();\n  }\n}\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'alert-element': AlertElement;\n  }\n}\n\nif (!customElements.get('alert-element')) {\n  customElements.define('alert-element', AlertElement);\n}\n","import type { CustomElement } from '@integrabeauty/custom-elements';\n\n/**\n * This component renders the cart messages by building and appending to the DOM an alert-element\n * component.\n *\n * TODO remove event listeners in discounnectedCallback\n */\nexport class CartMessages extends HTMLElement implements CustomElement {\n  connectedCallback() {\n    this.initializeShadowRoot();\n\n    addEventListener('ofir-add-to-cart', <EventListener>this.onAddToCart.bind(this));\n    addEventListener('ofir-update-line-item', <EventListener>this.onUpdateLineItem.bind(this));\n  }\n\n  private initializeShadowRoot() {\n    if (!this.shadowRoot) {\n      this.attachShadow({ mode: 'open' });\n    }\n  }\n\n  onAddToCart(event: OfirCustomEvents.AddToCartEvent) {\n    this.clearMessages();\n\n    if (!event.detail.success) {\n      this.showErrorMessage('There was an issue processing your request, please try again later.');\n      return;\n    }\n\n    const lineItems = event.detail.items;\n    const addedLineItemsString = lineItems.map(item => item.title).join(', ');\n    const messageHtml = `You added ${addedLineItemsString} to your shopping bag!`;\n    this.showSuccessMessage(messageHtml);\n  }\n\n  onUpdateLineItem(event: OfirCustomEvents.UpdateLineItemEvent) {\n    this.clearMessages();\n\n    if (!event.detail.success) {\n      this.showErrorMessage('There was an issue processing your request, please try again later.');\n      return;\n    }\n\n    let messageHtml;\n    if (event.detail.item_deleted) {\n      messageHtml = 'Item was removed successfully from your shopping bag.';\n    } else {\n      const updatedItemId = parseInt(event.detail.item.id, 10);\n      const lineItems = event.detail.cart.items;\n      const updatedLineItem = lineItems.find(\n        (item: ShopifyAjaxApi.LineItem) => item.id === updatedItemId);\n      messageHtml = `${updatedLineItem.title} was updated successfully in your shopping bag!`;\n    }\n\n    this.showSuccessMessage(messageHtml);\n  }\n\n  showSuccessMessage(messageHtml: string) {\n    const message = document.createElement('div');\n    message.setAttribute('slot', 'content');\n    message.innerHTML = messageHtml;\n\n    const messaging = document.createElement('alert-element');\n    messaging.dataset.type = 'success';\n    messaging.dataset.timer = '5000';\n    messaging.append(message);\n\n    this.shadowRoot.append(messaging);\n  }\n\n  showErrorMessage(messageHtml: string) {\n    const message = document.createElement('div');\n    message.setAttribute('slot', 'content');\n    message.innerHTML = messageHtml;\n\n    const messaging = document.createElement('alert-element');\n    messaging.dataset.type = 'error';\n    messaging.dataset.timer = '5000';\n    messaging.append(message);\n\n    this.shadowRoot.append(messaging);\n  }\n\n  clearMessages() {\n    const messages = this.shadowRoot.querySelectorAll('alert-element');\n    for (const message of messages) {\n      message.remove();\n    }\n  }\n}\n\nif (!customElements.get('cart-messages')) {\n  customElements.define('cart-messages', CartMessages);\n}\n","import type { Splide, Components, Options } from '@splidejs/splide';\n\n/**\n * Extension to add a custom slidebar to the Splide slider for visualizing and controlling the slide\n * position.\n *\n * If you want to render a sidebar in a specific place in the HTML, add the sidebar's markup\n * directly to your template. If no markup is provided, the sidebar will be automatically created\n * and inserted right before the closing tag of the Splide root element.\n *\n * This library was copy-paged from Lange.\n *\n * @param splide The main Splide instance\n * @param components Splide components\n * @param options Splide options object, containing customization options\n *\n * @see https://splidejs.com/components/\n * @see https://splidejs.com/guides/options/\n * @see https://splidejs.com/guides/extension/\n */\nexport function splideSlidebarExtension(splide: Splide, components: Components, options: Options) {\n  /**\n   * Root slidebar element\n   */\n  let slidebar: HTMLElement;\n\n  /**\n   * Indicates whether the sidebar markup is already present in the HTML or not.\n   */\n  let hasSlidebarMarkup = false;\n\n  /**\n   * Slide bar handle element\n   */\n  let handle: HTMLElement;\n\n  /**\n   * Slidebar current position\n   */\n  let position = 0;\n\n  /**\n   * Maximum allowable position value for the slidebar handle\n   */\n  let maxPosition: number;\n\n  /**\n   * Maximum allowable position value for the Splide's track. Store the value to not calculate all\n   * the time when the slider is dragged.\n   */\n  let maxTrackPosition: number;\n\n  /**\n   * Previous x-coordinate of the touch, needed to calculate the touch movement distance.\n   */\n  let previousTouchX = 0;\n\n  /**\n   * Mounts the extension, setting up the slidebar and initializing event listeners. Splide call\n   * this method automatically when the slider is ready.\n   *\n   * @see https://splidejs.com/guides/extension/\n   */\n  function mount() {\n    slidebar = components.Elements.root.querySelector('.splide__slider');\n    handle = components.Elements.root.querySelector('.splide__slider-handle');\n\n    if (slidebar && handle) {\n      hasSlidebarMarkup = true;\n    } else {\n      renderSlidebar();\n    }\n\n    slidebar.classList.add('hidden');\n\n    splide.on('ready', onSplideReady);\n    splide.on('updated', onSplideOptionsUpdated);\n    splide.on('overflow', onSplideOverflowed);\n  }\n\n  /**\n   * Enables the slidebar functionality, adding the necessary event listeners.\n   */\n  function enable() {\n    if (!hasSlidebarMarkup) {\n      components.Elements.root.appendChild(slidebar);\n    }\n\n    slidebar.classList.remove('hidden');\n\n    // Without this delay, the first page of the carousel is not clickable. There’s an issue with\n    // calling the components.Controller.go() method immediately when the extension is mounted.\n    setTimeout(onSplideResized);\n\n    if (!splide.is('fade')) {\n      splide.on('dragging.slidebar', onSplideDragging);\n    }\n\n    splide.on('move.slidebar', onSplideMoveStarted);\n    splide.on('resized.slidebar', onSplideResized);\n    handle.addEventListener('mousedown', onMousedown);\n    handle.addEventListener('touchstart', onTouchstart, { passive: true });\n    handle.addEventListener('transitionend', onTransitionend);\n    slidebar.addEventListener('click', onSlidebarClicked);\n  }\n\n  /**\n   * Disables the slidebar functionality, removing the event listeners and slidebar element.\n   */\n  function disable() {\n    slidebar.classList.add('hidden');\n\n    splide.off('move.slidebar');\n    splide.off('resized.slidebar');\n    splide.off('dragging.slidebar');\n    handle.removeEventListener('mousedown', onMousedown);\n    handle.removeEventListener('touchstart', onTouchstart);\n    handle.removeEventListener('transitionend', onTransitionend);\n    slidebar.removeEventListener('click', onSlidebarClicked);\n  }\n\n  /**\n   * Creates elements, calculates, and sets the handle width based on the number of slides, but does\n   * not append the element to the DOM.\n   */\n  function renderSlidebar() {\n    slidebar = document.createElement('div');\n    slidebar.classList.add('splide__slider');\n\n    handle = document.createElement('div');\n    handle.classList.add('splide__slider-handle');\n\n    handle.style.width = calculateHandleWidth();\n\n    slidebar.appendChild(handle);\n  }\n\n  /**\n   * Calculates the width of the handle.\n   *\n   * The function works depending upon the 'mode' the Splide is in. If it's in 'fade' mode, the\n   * width is calculated based on the proportion of 'perPage' option to the total number of slides,\n   * scaled to fit a percentage scale (0-100).\n   *\n   * For other modes, the handle width is determined by the ratio of list size to slider size, also\n   * expressed as a percentage.\n   *\n   * @returns The calculated width of the handle.\n   */\n  function calculateHandleWidth() {\n    let handleWidth;\n    if (splide.is('fade')) {\n      handleWidth = (options.perPage / components.Slides.getLength()) * 100;\n    } else {\n      handleWidth = (components.Layout.listSize() / components.Layout.sliderSize()) * 100;\n    }\n\n    return `${handleWidth}%`;\n  }\n\n  /**\n   * Calculates maximum values for the slidebar and track, ensuring they stay in sync.\n   */\n  function recalculateSlidebarAndTrackPosition() {\n    maxPosition = slidebar.offsetWidth - handle.offsetWidth;\n    maxTrackPosition = components.Layout.sliderSize() - components.Layout.listSize();\n\n    components.Controller.go(splide.index, true);\n  }\n\n  /**\n   * Processes and restricts handle movement within the slidebar bounds.\n   *\n   * @param toPosition Desired new position of the handle.\n   */\n  function processHandleMove(toPosition: number) {\n    const newPosition = getHandlePositionWithinBounds(toPosition);\n\n    // avoid position update if handle position hasn't changed.\n    if (position === newPosition) {\n      return;\n    }\n\n    position = getHandlePositionWithinBounds(toPosition);\n\n    if (!splide.is('fade')) {\n      const newSplidePosition = getCalculatedTrackPosition(position);\n      components.Move.translate(newSplidePosition);\n    }\n\n    // it ensures the DOM updates are synchronized with the browser's refresh rate, avoiding\n    // unnecessary reflows and keeping animations smooth\n    requestAnimationFrame(() => {\n      handle.style.transform = `translateX(${position}px)`;\n    });\n  }\n\n  /**\n   * Adjusts and returns the intended position of the handle within the boundaries of a slider.\n   * Checks whether the new position is within the range [0, maxPosition]. If the intended position\n   * is less than 0, it will be set to 0. If it's larger than maxPosition, it will be set to\n   * maxPosition. If it's the same as the current position, it will return this value without\n   * changes.\n   *\n   * @param toPosition - The intended new position.\n   * @returns newPosition - The adjusted position within the range [0, maxPosition].\n   */\n  function getHandlePositionWithinBounds(toPosition: number) {\n    let newPosition = toPosition;\n    if (newPosition <= 0) {\n      newPosition = 0;\n    } else if (newPosition >= maxPosition) {\n      newPosition = maxPosition;\n    }\n\n    return newPosition;\n  }\n\n  /**\n   * Aligns the track position based on the current position of the handle.\n   */\n  function alignTrackPosition() {\n    let index: number;\n\n    if (splide.is('fade')) {\n      index = Math.round(position / (maxPosition / components.Slides.getLength()));\n    } else {\n      const currentSplidePosition = components.Move.getPosition();\n      index = components.Move.toIndex(currentSplidePosition);\n    }\n\n    components.Controller.go(index, true);\n  }\n\n  function onSplideOptionsUpdated(_options: Options) {\n    toggleSlidebar();\n  }\n\n  function onSplideReady() {\n    toggleSlidebar();\n  }\n\n  function toggleSlidebar() {\n    // We need to both check isEnough() and isOverflow(). Depending on the Splide options or due to\n    // some Splide bugs, solely relying on one of these methods has led to issues.\n    if (options.slidebar && components.Slides.isEnough() && components.Layout.isOverflow()) {\n      enable();\n    } else {\n      disable();\n    }\n  }\n\n  function onMousedown() {\n    addEventListener('mousemove', onMousemove);\n    addEventListener('mouseup', onMouseup);\n  }\n\n  function onMouseup() {\n    // TODO: \"mouseup\" event is fired on touch devices right after \"touchend\". Need to investigate,\n    // if we can replace mouse and touch events with \"pointerdown\" and \"pointerup\" events.\n\n    alignTrackPosition();\n\n    removeEventListener('mousemove', onMousemove);\n    removeEventListener('mouseup', onMouseup);\n  }\n\n  function onMousemove(event: MouseEvent) {\n    const newPosition = position + event.movementX;\n    processHandleMove(newPosition);\n  }\n\n  function onTouchstart(event: TouchEvent) {\n    const touch = event.touches[0];\n    previousTouchX = touch.clientX;\n\n    addEventListener('touchmove', onTouchmove);\n    addEventListener('touchend', onTouchend);\n  }\n\n  function onTouchend(_event: TouchEvent) {\n    previousTouchX = 0;\n\n    alignTrackPosition();\n\n    removeEventListener('touchmove', onTouchmove);\n    removeEventListener('touchend', onTouchend);\n  }\n\n  function onTouchmove(event: TouchEvent) {\n    const touch = event.touches[0];\n    const movementX = touch.clientX - previousTouchX;\n    const newPosition = position + movementX;\n    previousTouchX = touch.clientX;\n\n    processHandleMove(newPosition);\n  }\n\n  /**\n   * Resets handle transition style once transition ends\n   */\n  function onTransitionend() {\n    handle.style.transition = '';\n  }\n\n  function onSlidebarClicked(event: MouseEvent) {\n    if (event.target !== event.currentTarget) {\n      return;\n    }\n\n    const newSlidebarPosition = event.offsetX - handle.offsetWidth / 2;\n    let index: number;\n\n    if (splide.is('fade')) {\n      index = Math.round(newSlidebarPosition / (maxPosition / components.Slides.getLength()));\n    } else {\n      const newTrackPosition = getCalculatedTrackPosition(newSlidebarPosition);\n      index = components.Move.toIndex(newTrackPosition);\n    }\n\n    components.Controller.go(index, true);\n  }\n\n  function onSplideResized() {\n    handle.style.width = calculateHandleWidth();\n\n    recalculateSlidebarAndTrackPosition();\n  }\n\n  function onSplideOverflowed(isOverflow: boolean) {\n    if (isOverflow) {\n      enable();\n    } else {\n      disable();\n    }\n  }\n\n  /**\n   * Sets the handle position based on the active slide's position.\n   *\n   * @param index Active Slide index.\n   */\n  function onSplideMoveStarted(index: number) {\n    let newSlidebarPosition;\n    if (splide.is('fade')) {\n      newSlidebarPosition = (options.perPage / (components.Slides.getLength() - 1)) * maxPosition *\n        index;\n    } else {\n      const newTrackPosition = components.Move.toPosition(index);\n      newSlidebarPosition = getCalculatedSlidebarPosition(newTrackPosition);\n    }\n\n    position = getHandlePositionWithinBounds(newSlidebarPosition);\n    handle.style.transform = `translateX(${position}px)`;\n    handle.style.transition = 'transform 400ms cubic-bezier(0.25, 1, 0.5, 1)';\n  }\n\n  function onSplideDragging() {\n    const newPosition = getCalculatedSlidebarPosition();\n    position = getHandlePositionWithinBounds(newPosition);\n    handle.style.transform = `translateX(${position}px)`;\n  }\n\n  /**\n   * Calculates the handle's position based on the current track position.\n   *\n   * @param trackPosition Optional track position for calculation. If not provided the value from\n   *   the Move component is taken. We don't need to pass a new track position if this function used\n   *   to get the slidebar position when the slider is dragged, but it's necessary if used when the\n   *   slider is moved to a certain slide because the position at the end of moving should be taken\n   *   instead of the current Splide's track position.\n   */\n  function getCalculatedSlidebarPosition(trackPosition?: number) {\n    // Use the max track position for the last reachable slide index rather than computing from\n    // layout dimensions. The arithmetic below doesn't work if the last slide is reached. It's good\n    // enough for the slide position in the middle of the slider but causes the slidebar handle to\n    // never fully reach the end.\n    const endIndex = components.Controller.getEnd();\n    const currentIndex = components.Controller.getIndex();\n    if (endIndex === currentIndex) {\n      return maxPosition;\n    }\n\n    const currentTrackPosition = -1 * (trackPosition ?? components.Move.getPosition());\n    const ratio = currentTrackPosition / maxTrackPosition;\n    const newSlideBarPosition = ratio * maxPosition;\n    return newSlideBarPosition;\n  }\n\n  /**\n   * Calculates the track's position based on the handle's position.\n   *\n   * @param slidebarPosition Position of the slidebar handle.\n   */\n  function getCalculatedTrackPosition(slidebarPosition: number) {\n    const ratio = slidebarPosition / maxPosition;\n    const newTrackPosition = -1 * ratio * maxTrackPosition;\n    return newTrackPosition;\n  }\n\n  /**\n   * Cleans up the slidebar when Splide is destroyed. Splide calls this automatically when a\n   * breakpoint sets `destroy: true` (e.g. when all slides fit on screen). Without this handler the\n   * slidebar would remain visible after Splide tears down.\n   */\n  function destroy() {\n    disable();\n  }\n\n  return {\n    mount,\n    destroy\n  };\n}\n","export {};\n\n// See webpack.md for help. This MUST be included in any module that performs dynamic imports.\n\ndeclare global {\n  /**\n   * This is a global that is defined in a liquid snippet to point to the Shopify asset path.\n   * Webpack compilation is transpiled to point the variable to the global. Because Webpack\n   * compilation involves raw string replacement, this variable has to actually exist. Because we do\n   * not know the public path at compile time this is basically a hack that allows to define the\n   * public path at runtime. This public path is needed for webpack to perform its dynamic module\n   * loading, e.g. to support dynamic imports.\n   */\n  // eslint-disable-next-line @typescript-eslint/naming-convention\n  var __webpack_public_path__: string;\n}\n\n__webpack_public_path__ = window.__webpack_public_path__;\n","import '../../../lib/webpack-path.js';\nimport { CustomElement } from '@integrabeauty/custom-elements';\nimport type { Splide } from '@splidejs/splide';\nimport { splideSlidebarExtension } from '../../../lib/splide-slidebar-extension.js';\nimport html from './index.html';\nimport styles from './index.scss';\n\n/**\n * A custom element for the upsell section in the modal cart.\n */\nclass UpsellSection extends HTMLElement implements CustomElement {\n  public splide: Splide;\n\n  private onCarouselSlotChangeBound = this.onCarouselSlotChange.bind(this);\n  private onCartModalOpenedBound = this.onCartModalOpened.bind(this);\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n    this.render();\n  }\n\n  public disconnectedCallback() {\n    const carouselSlot = this.shadowRoot.querySelector('slot[name=\"carousel\"]');\n    carouselSlot.removeEventListener('slotchange', this.onCarouselSlotChangeBound);\n\n    removeEventListener('cart-modal-opened', this.onCartModalOpenedBound);\n\n    if (this.splide) {\n      this.splide.destroy();\n    }\n  }\n\n  private initializeShadowRoot() {\n    if (!this.shadowRoot) {\n      this.attachShadow({ mode: 'open' });\n      this.shadowRoot.innerHTML = `<style>${styles}</style>${html}`;\n    }\n  }\n\n  private render() {\n    const carouselSlot = this.shadowRoot.querySelector('slot[name=\"carousel\"]');\n    carouselSlot.addEventListener('slotchange', this.onCarouselSlotChangeBound);\n\n    addEventListener('cart-modal-opened', this.onCartModalOpenedBound);\n  }\n\n  /**\n   * Handles the `slotchange` event. Listening to DOMContentLoaded is not reliable. Splide doesn't\n   * work properly.\n   */\n  private onCarouselSlotChange(event: Event) {\n    const slot = <HTMLSlotElement>event.target;\n    const assignedElements = slot.assignedElements();\n    if (assignedElements.length > 0) {\n      this.initCarousel().catch(console.warn);\n    }\n  }\n\n  /**\n   * Handles the `cart-modal-opened` event. We need to refresh the carousel when the modal is\n   * opened, because the carousel is initialized while the modal is hidden (display: none), and\n   * Splide cannot calculate and display slidebar correctly in this case.\n   */\n  private onCartModalOpened(_event: Event) {\n    if (this.splide) {\n      this.splide.refresh();\n    }\n  }\n\n  private async initCarousel() {\n    const { Splide } = await import('@splidejs/splide');\n    const element = this.querySelector<HTMLElement>('.upsell__carousel.splide');\n    if (!element) {\n      return;\n    }\n\n    const splide = new Splide(element, {\n      type: 'slide',\n      fixedWidth: 170,\n      perMove: 1,\n      rewind: false,\n      arrows: false,\n      pagination: false,\n      gap: 12,\n      keyboard: true,\n      slidebar: true,\n      omitEnd: true\n    });\n\n    splide.mount({\n      splideSlidebarExtension\n    });\n\n    this.splide = splide;\n  }\n}\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'upsell-section': UpsellSection;\n  }\n}\n\nif (!customElements.get('upsell-section')) {\n  customElements.define('upsell-section', UpsellSection);\n}\n","import * as _ShopifyBrowserTypes from '@integrabeauty/shopify-browser-types';\n\n/**\n * Represents the value of a cart attribute. Ordinarily values are strings. However, Shopify also\n * supports storing nested objects as values.\n */\nexport type AttributeValue = Record<string, string> | string;\n\n/**\n * The type of the value of `Cart['attributes']`\n */\nexport type Attributes = Record<string, AttributeValue>;\n\n/**\n * @todo selling plan properties\n * @todo endpoint request body response body types\n *\n * @see https://shopify.dev/docs/api/ajax/reference/cart\n */\nexport interface Cart {\n  /**\n   * An object where the key is the attribute name and the value is the attribute value.\n   */\n  attributes: Attributes;\n\n  /**\n   * A discount application represents a type of discount.\n   *\n   * When a discount is applied to one or more line items, it is said to be _allocated_ to the line\n   * items. That is why line level discount allocation objects have a nested application object that\n   * will correlate with one of the applications in this array.\n   *\n   * When a discount is applied to the whole cart, it will only be present in this array because it\n   * is not allocated to particular lines.\n   */\n  cart_level_discount_applications: DiscountApplication[];\n\n  /**\n   * The ISO currency code for the cart.\n   *\n   * @example \"USD\"\n   */\n  currency: string;\n\n  /**\n   * Discount codes that have been applied to the cart.\n   *\n   * Not all codes are applicable. It is possible for a code to be applied but inapplicable.\n   *\n   * This does not provide information about the amount of each discount. Refer to the cart level\n   * discount applications or line level discount allocations for that information.\n   *\n   * This array will include shipping discounts. A shipping discount will be present in the array if\n   * applied but in the inapplicable state while in the cart. The discount will appear as applicable\n   * at checkout automatically.\n   */\n  discount_codes: DiscountCode[];\n\n  /**\n   * Total quantity of items in a cart\n   */\n  item_count: number;\n\n  /**\n   * The cart's line items.\n   *\n   * A cart will always have a defined items array, even if the cart is empty.\n   */\n  items: Item[];\n\n  items_subtotal_price: number;\n\n  /**\n   * An optional textual note that may be applied to the cart. If a cart becomes a checkout, and\n   * then the checkout becomes an order, the cart note will carry over to the order note.\n   *\n   * An order's note, and an order's note attributes, are two completely separate concepts.\n   */\n  note: string;\n\n  original_total_price: number;\n\n  /**\n   * This is true when at least one item in the cart requires shipping.\n   *\n   * This is false when no items in the cart require shipping.\n   *\n   * Shopify supports digital-only carts. These are carts that when purchased do not require the\n   * creation of a fulfillment. For example, a cart that consists only of a digital gift card.\n   */\n  requires_shipping: boolean;\n\n  /**\n   * This property is defined when there are cart mutations that involve the use of liquid-rendered\n   * sections.\n   *\n   * @see https://shopify.dev/docs/api/ajax/reference/cart#request-sections\n   */\n  sections: Record<string, string>;\n\n  /**\n   * This is the publicly visible cart id.\n   *\n   * If a checkout is created from this cart, the checkout's cart token will be equal to this value.\n   *\n   * Never write logic that parses this value. The format can change at any time, and has changed\n   * several times in the past.\n   *\n   * The value may contain url-like values, e.g. \"abc?key=123\"\n   */\n  token: string;\n\n  /**\n   * The total reduction in price in currency subunits (e.g. in cents for USD).\n   *\n   * @example 5010\n   */\n  total_discount: number;\n\n  /**\n   * Total price in currency subunits. For example, if the currency is USD, this is in cents.\n   *\n   * @example 1050\n   */\n  total_price: number;\n\n  /**\n   * The total weight of the cart (in grams?). This is a simple sum of the weight of the items of\n   * the cart (unit weight * quantity).\n   *\n   * @example 123.456\n   */\n  total_weight: number;\n}\n\n/**\n * Represents a discount that has been applied to an item.\n */\nexport interface Discount {\n  amount: number;\n  title: string;\n}\n\n/**\n * Represents a discount application that has been allocated to a line item.\n */\nexport interface DiscountAllocation {\n  /**\n   * The amount of the application that was allocated to a particular line item.\n   *\n   * When a discount application is allocated to several line items, this is a partial value of the\n   * full discount application amount that is allocated to this particular line. When the discount\n   * application is only allocated to a single line, this value will be equal to the total value of\n   * the discount application.\n   *\n   * For example, let's say we have cart with 2 line items and 10 dollar off discount application.\n   * The total value of the discount application is 10 dollars. Let's say the discount is allocated\n   * to both lines. The value of each allocation is 5 dollars. For each line this amount will\n   * contain the value 500. Within the nested application object, the application's total amount\n   * will be 1000. This is because each nested application object is a copy of the one application\n   * that happens to have multiple allocations (the discount is applied to multiple lines).\n   *\n   * The amount is in currency subunits. For example, for USD, this is the value of cents. For\n   * example, a discount of $2.50 is 250.\n   *\n   * @example 1050\n   */\n  amount: number;\n\n  /**\n   * The corresponding discount application that was allocated to a particular line item.\n   */\n  discount_application: DiscountApplication;\n}\n\n/**\n * A discount application represents a discount of some kind. There are automatically applied\n * discount applications, discount code applications, and script applied discount applications.\n */\nexport interface DiscountApplication {\n  allocation_method: string;\n  created_at: string;\n  description: string;\n  key: string;\n  target_selection: string;\n  target_type: string;\n  title: string;\n  total_allocated_amount: number;\n  type: string;\n  value: string;\n  value_type: string;\n}\n\n/**\n * The details of an image resource, such as the hero image for a product.\n */\nexport interface Image {\n  alt: string;\n  aspect_ratio: number;\n  height: number;\n  url: string;\n  width: number;\n}\n\n/**\n * Represents a line item in a cart.\n */\nexport interface Item {\n  /**\n   * The price of the line item after discounts\n   */\n  discounted_price: number;\n\n  /**\n   * Discount codes applied to the line item.\n   *\n   * @deprecated\n   */\n  discounts: Discount[];\n\n  /**\n   * The main image for the product\n   */\n  featured_image: Image;\n\n  /**\n   * The combined price, in the currency's subunit, of all of the items in the line item. This\n   * includes any line-level discounts.\n   *\n   * The value is equal to line_item.final_price multiplied by line_item.quantity. It's output in\n   * the customer's local (presentment) currency.\n   *\n   * For currencies without subunits, such as JPY and KRW, tenths and hundredths of a unit are\n   * appended. For example, 1000 Japanese yen is output as 100000.\n   */\n  final_line_price: number;\n\n  /**\n   * The price of the line item in the currency's subunit. This includes any line-level discounts.\n   *\n   * The value is output in the customer's local (presentment) currency.\n   *\n   * For currencies without subunits, such as JPY and KRW, tenths and hundredths of a unit are\n   * appended. For example, 1000 Japanese yen is output as 100000.\n   */\n  final_price: number;\n\n  /**\n   * Returns true if the product associated with the line item is a gift card. Returns false if not.\n   */\n  gift_card: boolean;\n\n  /**\n   * The weight of the line item in the store's default weight unit.\n   */\n  grams: number;\n\n  handle: string;\n\n  has_components: boolean;\n\n  /**\n   * The ID of the line item. The ID differs depending on the context. The following table outlines\n   * the possible contexts and their associated values:\n   *\n   * Context Value\n   *\n   * * cart.items The ID of the line item's variant. This ID isn't unique, and can be shared by\n   *   multiple items with the same variant.\n   * * checkout.line_items A temporary unique hash generated for the checkout.\n   * * order.line_items A unique integer ID.\n   */\n  id: number;\n\n  image: string;\n\n  /**\n   * Added in conjunction with parent_relationship to support determining whether a line item can be\n   * removed or have its quantity updated when the line item is a child of another line item, e.g.\n   * part of a bundle or value set or promotional gift.\n   */\n  instructions: {\n    can_remove: boolean;\n    can_update_quantity: boolean;\n  };\n\n  key: string;\n\n  /**\n   * The discount allocations that apply to the line item.\n   */\n  line_level_discount_allocations: DiscountAllocation[];\n\n  /**\n   * The total amount of any discounts applied to the line item in the currency's subunit.\n   *\n   * The value is output in the customer's local (presentment) currency.\n   *\n   * For currencies without subunits, such as JPY and KRW, tenths and hundredths of a unit are\n   * appended. For example, 1000 Japanese yen is output as 100000.\n   */\n  line_level_total_discount: number;\n\n  /**\n   * @deprecated https://shopify.dev/docs/api/liquid/objects/line_item\n   */\n  line_price: number;\n\n  /**\n   * The name and value pairs for each option of the variant associated with the line item.\n   *\n   * The array is never empty because variants with no options still have a default option. Because\n   * of this, you should use line_item.product.has_only_default_variant to check whether there's any\n   * information to output.\n   */\n  options_with_values: Option[];\n\n  /**\n   * The combined price of all of the items in a line item in the currency's subunit, before any\n   * discounts have been applied.\n   *\n   * The value is equal to line_item.original_price multiplied by line_item.quantity. It's output in\n   * the customer's local (presentment) currency.\n   *\n   * For currencies without subunits, such as JPY and KRW, tenths and hundredths of a unit are\n   * appended. For example, 1000 Japanese yen is output as 100000.\n   */\n  original_line_price: number;\n\n  /**\n   * The price of the line item in the currency's subunit, before discounts have been applied.\n   *\n   * The value is output in the customer's local (presentment) currency.\n   *\n   * For currencies without subunits, such as JPY and KRW, tenths and hundredths of a unit are\n   * appended. For example, 1000 Japanese yen is output as 100000.\n   */\n  original_price: number;\n\n  /**\n   * The parent relationship of the line item. This is only present if the line item is a child of\n   * another line item. For example, when the line item is part of a bundle or value set or\n   * promotional gift.\n   *\n   * The parent relationship must be established by adding the \"parent_id\" property to the add to\n   * bag request.\n   *\n   * @see https://shopify.dev/docs/apps/build/product-merchandising/nested-cart-lines/create-nested-cart-lines#cart-ajax-api\n   */\n  parent_relationship: {\n    parent_key: string;\n  };\n\n  presentment_price: number;\n\n  /**\n   * @deprecated https://shopify.dev/docs/api/liquid/objects/line_item\n   */\n  price: number;\n\n  product_description: string;\n\n  product_has_only_default_variant: boolean;\n\n  /**\n   * The ID of the line item's product.\n   */\n  product_id: number;\n\n  product_title: string;\n\n  product_type: string;\n\n  properties: Record<string, string>;\n  quantity: number;\n\n  quantity_rule: QuantityRule;\n\n  requires_shipping: boolean;\n\n  /**\n   * The selling plan allocation of the line item.\n   */\n  selling_plan_allocation: SellingPlanAllocation;\n\n  /**\n   * The SKU of the variant in the line item.\n   *\n   * Stock Keeping Unit.\n   */\n  sku: string;\n\n  successfully_fulfilled_quantity: number;\n\n  taxable: boolean;\n\n  /**\n   * The title of the line item. The title is a combination of line_item.product.title and\n   * line_item.variant.title, separated by a hyphen.\n   *\n   * In most contexts, the line item title appears in the customer's preferred language. However, in\n   * the context of an order, the line item title appears in the language that the customer checked\n   * out in. The title can receive an override value from the Cart Transform API. Overrides take\n   * precedence over translations.\n   *\n   * #### Line item title history\n   *\n   * When referencing line item, product, and variant titles in the context of an order, the\n   * following changes might result in a different output than you expect:\n   * * A product or variant being deleted\n   * * A product or variant title being edited\n   *\n   * When line_item.title is referenced for an order, the line item title at the time of the order\n   * is returned. However, when line_item.product.title and line_item.variant.title are referenced,\n   * the current value for each title is returned.\n   */\n  title: string;\n\n  /**\n   * @deprecated https://shopify.dev/docs/api/liquid/objects/line_item\n   */\n  total_discount: number;\n\n  unit_price_measurement: {\n    measured_type: string;\n    quantity_unit: string;\n    quantity_value: string;\n    reference_unit: string;\n    reference_value: number;\n  };\n\n  untranslated_produce_title: string;\n\n  untranslated_variant_title: string;\n\n  /**\n   * The relative URL of the variant associated with the line item.\n   */\n  url: string;\n\n  /**\n   * The ID of the line item's variant.\n   */\n  variant_id: number;\n\n  variant_options: string[];\n\n  variant_title: string;\n\n  /**\n   * The vendor of the variant associated with the line item.\n   */\n  vendor: string;\n}\n\nexport interface SellingPlanAllocation {\n  compare_at_price: number;\n\n  per_delivery_price: number;\n\n  /**\n   * @example 900\n   */\n  price: number;\n\n  price_adjustments: SellingPlanAllocationPriceAdjustment[];\n  selling_plan: SellingPlan;\n}\n\nexport interface SellingPlanAllocationPriceAdjustment {\n  /**\n   * @example 1\n   */\n  position: number;\n\n  /**\n   * @example 900\n   */\n  price: number;\n}\n\nexport interface SellingPlan {\n  description: unknown;\n\n  fixed_selling_plan: boolean;\n\n  id: number;\n  name: string;\n\n  options: SellingPlanOption[];\n\n  price_adjustments: SellingPlanPriceAdjustment[];\n\n  recurring_deliveries: boolean;\n}\n\nexport interface SellingPlanOption {\n  name: string;\n  position: number;\n  value: string;\n}\n\nexport interface SellingPlanPriceAdjustment {\n  order_count: unknown;\n  position: number;\n  value: number;\n  value_type: string;\n}\n\nexport interface Option {\n  name: string;\n  value: string;\n}\n\nexport interface QuantityRule {\n  increment: number;\n  max: number;\n  min: number;\n}\n\nexport interface DiscountCode {\n  /**\n   * Whether the discount is currently applicable. It is possible to apply a discount code to a cart\n   * but have it not yet be applicable. When not applicable, there will not be any associated\n   * discount applications or allocations. The moment someone performs an action that makes the\n   * discount code applicable, Shopify will update the property to true.\n   */\n  applicable: boolean;\n\n  /**\n   * The discount code string.\n   *\n   * This is NOT guaranteed to be uppercase. Discount codes entered at checkout are captured in\n   * whatever case was used during input.\n   *\n   * @example \"MONEY1234\"\n   * @example \"Abc123def\"\n   */\n  code: string;\n}\n\n/**\n * Returns the request id header from the ajax cart api response. Capturing this value is important\n * when helping Shopify support investigate issues.\n *\n * This does not error. If there is a problem, this returns null or undefined.\n */\nfunction getRequestId(response: Response) {\n  try {\n    return response.headers.get('X-Request-Id');\n  } catch (error) {\n    // ignore\n  }\n}\n\n/**\n * Returns whether the given value is shaped like a fetch Response.\n *\n * The return type of fetch is a contract, not a verified fact. A third party that overrides the\n * global fetch can resolve it to null, undefined, or some unrelated value, in which case the very\n * next property access throws an opaque error with no indication that a bad override was the cause.\n * This checks structurally for the members this module reads rather than with an instanceof, so a\n * conforming fetch polyfill that returns its own Response class still passes while a missing or\n * malformed value is trapped at the point it is received.\n *\n * This type check does not technically prove that the value is a genuine Response object, only that\n * it has the expected shape. We are intentionally lying with types here, so that we can leverage\n * the type inference to support the subset of use cases this module actually relies on. More over,\n * this lying is ok, as the call to `response = await fetch` is also lying because it overly trusts\n * that fetch returns value of type Response. We are going to keep trying to \"make fetch happen\"\n * regardless.\n *\n *\n * ## War Story\n *\n * As much as it seems like this is an insane and irrationally paranoid check, you would be\n * surprised. For example, in the wild, we observed a third party vendor that hooked global fetch,\n * had an error in their hook, and simultaneously, another vendor's third party polyfill that only\n * defined fetch without also defining Response, leading to even more obscure cases when older\n * browsers were involved. Then, to make matters worse, a fix was implemented leveraging the clean\n * global fetch using an iframe content window hack to work around the inability to cleanly capture\n * the native fetch before it was erroneously hooked, and this broke certain expressions that use\n * instanceof because instanceof has unintuitive behavior when checking instanceof of a value from a\n * different global context, e.g. from an iframe. A web of lies!\n */\nfunction isResponse(value: any): value is Response {\n  if (value === null || typeof value !== 'object') {\n    return false;\n  }\n\n  if (typeof value.ok !== 'boolean') {\n    return false;\n  }\n\n  if (typeof value.status !== 'number') {\n    return false;\n  }\n\n  if (typeof value.json !== 'function') {\n    return false;\n  }\n\n  return true;\n}\n\n/**\n * Attaches a single piece of context onto an error before it is thrown.\n *\n * The target may be a value caught upstream rather than one constructed here, and such a value can\n * carry a non-writable property, such as a DOMException whose code is a read-only accessor, or be\n * frozen outright, in which case assigning to it throws. Decorating an error must never itself\n * fail, so a rejected assignment is swallowed. This context is best effort metadata, so the\n * property is skipped when it cannot be set, leaving the error otherwise intact to be thrown and\n * handled by the caller.\n */\nfunction decorateError(error: any, key: string, value: unknown) {\n  try {\n    error[key] = value;\n  } catch (error) {\n    // The target rejects this property. Skip it rather than letting decoration fail.\n  }\n}\n\n/**\n * An item to add to the cart\n */\nexport interface AddItem {\n  /**\n   * Variant id\n   *\n   * Shopify says that it supports variant ids as numbers, but we have witnessed strange errors when\n   * using numbers so we impose an artificial limitation on the type here and require strings.\n   */\n  id: string;\n\n  /**\n   * The id of the line item to add the item as a child of. This is used for adding items as\n   * children of value sets and bundles. If not specified, the item is added as a top level line\n   * item.\n   *\n   * @see https://shopify.dev/docs/apps/build/product-merchandising/nested-cart-lines/create-nested-cart-lines#cart-ajax-api\n   */\n  parent_id?: number | string;\n\n  /**\n   * Line item properties to include.\n   *\n   * If the variant does not already exist in the cart, then the logic is straightforward. This will\n   * result in a new line item with these properties.\n   *\n   * If the variant exists in the cart, then the logic is convoluted. If the specified properties\n   * are identical to the existing properties, then the quantity of the existing line item\n   * increments. If the specified properties are different, then a new line item is created with\n   * these properties, resulting in the same variant being present in multiple line items.\n   */\n  properties?: Attributes;\n\n  /**\n   * The amount of the variant to add.\n   *\n   * @see https://shopify.dev/docs/api/ajax/reference/cart#post-locale-cart-add-js\n   */\n  quantity: number;\n\n  selling_plan?: number | string;\n}\n\n/**\n * Adds items to the cart. At least one item should be specified.\n *\n * This does not fetch the response body. The raw response body does not contain the full cart\n * state. The raw response body may not be the latest state of cart items. The raw response body may\n * contain zero-quantity items still present after prior removal.\n *\n * This validates the response status. If the status is not ok this throws an error.\n *\n * This specifies fetch mode no-store so that the call is not unintentionally cached.\n *\n * Adding an item the cart does not necessarily mean a new line item is created. For each item in\n * the input, Shopify makes a server side determination of whether to update the quantity of an\n * existing line item or add a new line item. The factors in that decision are related to how a line\n * item key is determined, which is poorly documented. Basically, if an item has some different\n * properties, e.g. line item properties, or pricing, then the item will always be added as a new\n * line item, even though the variant id is the same as an existing line item.\n *\n * The line item scripts run on every mutation. The scripts will remove any orphaned value set line\n * items implicitly. Keep this in mind when debugging.\n *\n * The content type header must be specified in the request or Shopify interprets the request as a\n * form submission, which we are not doing here, which leads to an error.\n *\n * Shopify's injected front end code (e.g. Trekkie, Monorail) triggers a console warning when it\n * detects that the number of items requested to be added is less than the number that was added.\n * This is not necessarily an error. It can indicate that the line item scripts removed one or more\n * line items during the add mutation.\n *\n * @see https://shopify.dev/docs/api/ajax/reference/cart#post-locale-cart-add-js\n * @see https://help.shopify.com/en/manual/checkout-settings/script-editor/shopify-scripts\n */\nexport async function cartAdd(items: AddItem[]) {\n  const response = await fetch(Shopify.routes.root + 'cart/add.js', {\n    body: JSON.stringify({ items }),\n    headers: {\n      'Content-Type': 'application/json'\n    },\n    cache: 'no-store',\n    method: 'POST'\n  });\n\n  if (!isResponse(response)) {\n    const error: any = new Error('fetch did not return a response adding to cart');\n    decorateError(error, 'items', items);\n    decorateError(error, 'response', response);\n    throw error;\n  }\n\n  if (!response.ok) {\n    const error: any = new Error(`Received status ${response.status} adding to cart`);\n    decorateError(error, 'items', items);\n    decorateError(error, 'request_id', getRequestId(response));\n\n    decorateError(error, 'response_status', response.status);\n\n    try {\n      const responseBody = await response.json();\n      decorateError(error, 'response_body', responseBody);\n    } catch (error) {\n      // ignore\n    }\n\n    // For certain error responses, Shopify provides a clearer error message in the response body.\n    // On some 422 responses a more specific description is present alongside the generic message,\n    // so prefer the description, fall back to the generic message, and otherwise leave the default\n    // received-status message in place. For example, adding a bundle whose component is unavailable\n    // yields a body like the following, where description is meaningful and message is generic:\n    //\n    // { \"description\": \"The bundle product 'Product Title' cannot be added to the cart.\",\n    // \"message\": \"Cart Error\", \"status\": 422 }\n    //\n    // The status property of the response body mirrors the HTTP status code (empirical).\n\n    let message;\n    if (response.status === 422 && typeof error.response_body?.description === 'string') {\n      message = error.response_body.description;\n    } else if (typeof error.response_body?.message === 'string') {\n      message = error.response_body.message;\n    }\n\n    if (message) {\n      decorateError(error, 'message', message);\n    }\n\n    throw error;\n  }\n\n  return response;\n}\n\n/**\n * Either id or line must be specified.\n */\nexport interface CartChangeInput {\n  /**\n   * Can be either the variant id or the line item key.\n   */\n  id?: number | string;\n\n  /**\n   * A cart can have multiple line items that share the same variant_id. For example, when variants\n   * have different line item properties, or automatic discounts create variants at different\n   * prices. To account for this, you can use the line property when updating the cart.\n   *\n   * The value of line is the 1-based index position of the item in the cart.\n   */\n  line?: number;\n\n  /**\n   * The properties property sets the line item properties. Its value must be an object of key-value\n   * pairs.\n   *\n   * Whenever a POST request includes properties, it overwrites the entire properties object. Any\n   * key-value pairs that were already in the properties object are erased.\n   *\n   * It's not possible to set a line item's properties property to an empty object once a value is\n   * set. If you need to remove all line item properties, then you need to remove the entire line\n   * item.\n   *\n   * You can visually hide line item properties at checkout by creating private properties. This\n   * technique might be an alternative to removing a line item when managing properties.\n   */\n  properties?: Record<string, any>;\n\n  /**\n   * The value of the quantity property represents the new quantity you want for the line item. It\n   * must be an integer.\n   *\n   * You can remove a line item by setting the quantity to 0.\n   *\n   * It is not clear from the documentation, but based on the description of the selling plan\n   * property, it looks like quantity is actually optional and defaults to 1 when not specified.\n   */\n  quantity?: number;\n\n  /**\n   * The selling_plan property sets the line item selling plan. Its value must be one of the\n   * following:\n   *\n   * * The selling plan ID: To set a specific selling plan for the line item.\n   * * null or an empty string: To remove any selling plan from the line item.\n   *\n   * When specifying the selling_plan property, consider the following:\n   * * You can use only the line property for identifying the line item.\n   * * You should always specify the quantity property. If the quantity isn't specified, then it's\n   *   assumed to be 1.\n   */\n  selling_plan?: number;\n}\n\n/**\n * Use the /{locale}/cart/change.js endpoint to change the quantity, properties, and selling_plan\n * properties of a cart line item. Only items already in your cart can be changed, and you can\n * change only one line item at a time.\n *\n * The POST data requires either an id or line property to identify the line item to be changed.\n *\n * The value of the id property can be one of the following:\n *\n * * The line item's variant_id\n * * The line item's key\n *\n * A cart can have multiple line items that share the same variant_id. For example, when variants\n * have different line item properties, or automatic discounts create variants at different prices.\n * Because of this, it's recommended to use the line item key to ensure that you're only changing\n * the intended line item.\n *\n * If the item that you're attempting to change isn't already in the cart, then\n * /{locale}/cart/change.js doesn't add it. Instead, it returns a 404 error.\n *\n * The documentation states that the response body is the JSON of the cart. This exposes the\n * response object but does not fetch the response body.\n *\n * ### Removing out of stock items\n * It is unclear but based on a conversation with Shopify support, prefer to use cart/change instead\n * of cart/update to avoid issues with removing an out of stock item from the cart. Using update\n * results in a 422 error but supposedly cart/change does not.\n */\nexport async function cartChange(input: CartChangeInput) {\n  const response = await fetch(Shopify.routes.root + 'cart/change.js', {\n    body: JSON.stringify(input),\n    cache: 'no-store',\n    headers: {\n      'Content-Type': 'application/json'\n    },\n    method: 'POST'\n  });\n\n  if (!isResponse(response)) {\n    const error: any = new Error('fetch did not return a response changing cart');\n    decorateError(error, 'request_body', input);\n    decorateError(error, 'response', response);\n    throw error;\n  }\n\n  if (!response.ok) {\n    const error: any = new Error(`Received status ${response.status} changing cart`);\n    decorateError(error, 'request_body', input);\n    decorateError(error, 'request_id', getRequestId(response));\n\n    try {\n      decorateError(error, 'response_body', await response.json());\n    } catch (error) {\n      // ignore\n    }\n\n    decorateError(error, 'response_status', response.status);\n    decorateError(error, 'response_url', response.url);\n\n    // For certain error responses, Shopify provides a clearer error message in the response body.\n    // Extract the message from the response and use that as the thrown error message.\n\n    if (typeof error.response_body?.message === 'string') {\n      decorateError(error, 'message', error.response_body.message);\n    }\n\n    throw error;\n  }\n\n  return response;\n}\n\n/**\n * Use the GET /{locale}/cart.js endpoint to get the cart as JSON.\n *\n * All monetary properties are returned in the customer's presentment currency. To check the\n * customer's presentment currency, you can use the currency field in the response.\n *\n * @see https://shopify.dev/docs/api/ajax/reference/cart#get-locale-cart-js\n */\nexport async function cartFetch() {\n  const url = Shopify.routes.root + 'cart.js';\n\n  let response;\n  try {\n    response = await fetch(url, { cache: 'no-store' });\n  } catch (error) {\n    decorateError(error, 'url', url);\n    throw error;\n  }\n\n  if (!isResponse(response)) {\n    const error: any = new Error('fetch did not return a response fetching cart');\n    decorateError(error, 'response', response);\n    decorateError(error, 'url', url);\n    throw error;\n  }\n\n  if (!response.ok) {\n    const error: any = new Error(`Received status ${response.status} fetching cart`);\n    decorateError(error, 'request_id', getRequestId(response));\n\n    try {\n      decorateError(error, 'response_body', await response.json());\n    } catch (error) {\n      // ignore\n    }\n\n    decorateError(error, 'status', response.status);\n    decorateError(error, 'url', response.url);\n    throw error;\n  }\n\n  let body;\n  try {\n    body = await response.json();\n  } catch (error) {\n    decorateError(error, 'status', response.status);\n    decorateError(error, 'url', response.url);\n    throw error;\n  }\n\n  if (!isCart(body)) {\n    const error: any = new Error('Unexpected get cart response body');\n    decorateError(error, 'response_body', body);\n    decorateError(error, 'status', response.status);\n    decorateError(error, 'url', response.url);\n    throw error;\n  }\n\n  return body;\n}\n\nexport interface CartUpdateInput {\n  /**\n   * New cart attributes. Existing cart attributes are overwritten.\n   */\n  attributes?: Attributes;\n\n  /**\n   * Beta shopify feature. Essentially works like Storefront discount apply mutation.\n   *\n   * Pass an empty string to remove discount(s).\n   */\n  discount?: string;\n\n  /**\n   * Cart note.\n   */\n  note?: string;\n\n  /**\n   * Even though Shopify supports specifying variant ids as numbers, we impose the restriction that\n   * the property names of the updates object must be strings due to some strange looking errors\n   * when specifying numbers.\n   */\n  updates?: Record<string, any>;\n}\n\n/**\n * Performs a cart update.\n *\n * This does not return the cart updated state. You will need to do a followup call to fetch the\n * updated state of the cart. One reason this does also return the updated state is that we must\n * perform a second call to fetch the updated state but not all callers are interested in doing so.\n *\n * This does not return the response body of the call. There is no need to fetch the body because\n * the body does not contain useful data. The response body can contain 0 quantity items and other\n * kinds of strange things.\n *\n * This must specify the content type header. Shopify supports both a JSON request body and a form\n * data request body. Shopify defaults to interpreting requests as form submissions. This uses the\n * JSON format so the header is required.\n *\n * Backend scripts may affect the output.\n *\n * @see https://shopify.dev/docs/api/ajax/reference/cart#post-locale-cart-update-js\n * @see https://shopify.dev/docs/api/ajax/reference/cart#update-line-item-quantities\n * @see https://shopify.dev/docs/api/ajax/reference/cart#private-cart-attributes\n */\nexport async function cartUpdate(input: CartUpdateInput) {\n  const response = await fetch(Shopify.routes.root + 'cart/update.js', {\n    body: JSON.stringify(input),\n    cache: 'no-store',\n    headers: {\n      'Content-Type': 'application/json'\n    },\n    method: 'POST'\n  });\n\n  if (!isResponse(response)) {\n    const error: any = new Error('fetch did not return a response updating cart');\n    decorateError(error, 'request_body', input);\n    decorateError(error, 'response', response);\n    throw error;\n  }\n\n  if (!response.ok) {\n    const error: any = new Error(`Received status ${response.status} updating cart`);\n    decorateError(error, 'request_body', input);\n    decorateError(error, 'request_id', getRequestId(response));\n\n    try {\n      decorateError(error, 'response_body', await response.json());\n    } catch (error) {\n      // ignore\n    }\n\n    decorateError(error, 'response_status', response.status);\n\n    // For certain error responses, Shopify provides a clearer error message in the response body.\n    // Extract the message from the response and use that as the thrown error message.\n\n    if (typeof error.response_body?.message === 'string') {\n      decorateError(error, 'message', error.response_body.message);\n    }\n\n    throw error;\n  }\n\n  return response;\n}\n\n/**\n * Returns whether the error indicates the add failed because one of the products is out of stock\n * (not available for sale). In this case Shopify provides an error response body that looks like\n * this:\n *\n * ```json\n * {\n *   \"description\": \"All 0 <product title> are in your cart\",\n *   \"message\": \"Cart Error\",\n *   \"status\": 422\n * }\n * ```\n *\n * ```json\n * {\n *   \"description\": \"The product '<title>' is already sold out.\",\n *   \"message\": \"Cart Error\",\n *   \"status\": 422\n * }\n * ```\n *\n * This error can happen both when adding an item or updating the quantity of an item.\n *\n * This assumes error messages are in English.\n */\nexport function isUnavailableError(error: any) {\n  if (error instanceof Error && 'response_body' in error && isObject(error.response_body) &&\n    error.response_body.status === 422) {\n    const description = error.response_body.description;\n    if (typeof description === 'string') {\n      // this one is only present in the add error response\n      if (description.startsWith('All 0') && description.includes('in your cart')) {\n        return true;\n      }\n\n      // this string is sometimes present in the update error response, not just the add response\n      if (description.includes('already sold out')) {\n        return true;\n      }\n    }\n  }\n\n  return false;\n}\n\n/**\n * Returns whether the error indicates the add failed because the selling plan sent with the item is\n * not one Shopify will accept for that variant. This happens when a rendered page carries a selling\n * plan ID (for example a pre-order deferred-payment plan) that has since become stale: the variant\n * came back in stock, or the plan was edited or removed, after the page was rendered but before the\n * shopper clicked add. Shopify responds with a body that looks like this:\n *\n * ```json\n * {\n *   \"description\": \"Cannot apply selling plan to variant\",\n *   \"message\": \"Cart Error\",\n *   \"status\": 422\n * }\n * ```\n *\n * This assumes error messages are in English.\n */\nexport function isInvalidSellingPlanError(error: any) {\n  if (error instanceof Error && 'response_body' in error && isObject(error.response_body) &&\n    error.response_body.status === 422) {\n    const description = error.response_body.description;\n    if (typeof description === 'string') {\n      if (description.includes('Cannot apply selling plan to variant')) {\n        return true;\n      }\n    }\n  }\n\n  return false;\n}\n\n/**\n * Returns whether the given value looks like a cart object.\n *\n * This is not a complete validation. This is a shallow check that examines whether the value is an\n * object containing an items property that is an array. Any value that meets that criteria\n * satisfies the meaning of a cart.\n */\nexport function isCart(value: any): value is Cart {\n  return isObject(value) && Array.isArray(value.items);\n}\n\nfunction isObject(value: any): value is Record<string, any> {\n  return typeof value === 'object' && value !== null;\n}\n","// Keeps track of the timeout timer between the start and end of all calls\nlet timerId: ReturnType<typeof setTimeout>;\n\n/**\n * Called at the beginning of most service operations. This signals to the view that an operation\n * has started.\n *\n * @see http://jdebp.info/FGA/function-perilogues.html\n */\nexport function startCall(...args: any[]) {\n  // This should never happen because we always clear the timeout and set the variable value back to\n  // undefined in the epilogue. This might help debug some screwy things.\n\n  if (typeof timerId !== 'undefined') {\n    window.console.error('concurrent call detected!');\n    window.console.error('[call-utils.ts] args: ', args);\n\n    window.Sentry?.captureException(new Error('Concurrent checkout service call detected'), {\n      extra: {\n        args\n      },\n      level: 'warning'\n    });\n  }\n\n  // Guarantee that the end event will fire by starting a timer.\n  timerId = setTimeout(() => {\n    endCall(false, true);\n  }, 30 * 1000, true);\n\n  const event = new Event('ofir-cart-call-started', { cancelable: false });\n  dispatchEvent(event);\n}\n\n/**\n * Called at the end of most service operations. This signals to the view that an operation has\n * completed (successfully or not).\n *\n * This is called either manually whenever the operation completed, or automatically, if the\n * operation errored or has not completed after a certain amount of time. We do this to prevent the\n * situation of never firing an end event in case of extremely messed up code. Sometimes that kind\n * of thing happens and we never want to fully lock someone out of cart interactions.\n *\n * @see http://jdebp.info/FGA/function-perilogues.html\n */\nexport function endCall(notifyView = true, didTimeOut = false) {\n  // Unconditionally clear the timeout timer, regardless of whether the timer id is set.\n  clearTimeout(timerId);\n\n  // Reset the timer id. Not really needed but it makes it crystal clear that there is no timer id\n  // active.\n  timerId = undefined;\n\n  // If we got here from a timeout, just make sure something is logged. In theory, nothing ever\n  // times out.\n  if (didTimeOut) {\n    console.warn('epilogue timed out');\n  }\n\n  const event = new CustomEvent('ofir-cart-call-ended', {\n    cancelable: false,\n    detail: {\n      is_timeout: didTimeOut,\n      notify_view: notifyView\n    }\n  });\n\n  dispatchEvent(event);\n}\n","/* eslint-disable @typescript-eslint/naming-convention */\n/**\n * Do not use this export. This is a hacky fix to a strange inlining optimization performed by\n * webpack where it removes this module and points identifiers directly to the global values.\n * Without this export, webpack fails to understand that `window.__ofir` must also be inlined.\n *\n * @type {unknown}\n */\nexport const __ofir: unknown = window.__ofir;\n\n/**\n * Cart should never be undefined\n */\nexport const Cart: OfirLiquidGlobals.Cart = window.__ofir?.Cart;\n\n/**\n * Checkout is undefined if user is not loading a checkout page.\n *\n * @type {OfirLiquidGlobals.Checkout}\n */\nexport const Checkout: OfirLiquidGlobals.Checkout = window.__ofir?.Checkout;\n\n/**\n * Collection is undefined if user is not loading a product or collection page.\n *\n * @type {OfirLiquidGlobals.Collection}\n */\nexport const Collection: OfirLiquidGlobals.Collection = window.__ofir?.Collection;\n\n/**\n * Customer is undefined if user is not logged in.\n *\n * @type {OfirLiquidGlobals.Customer}\n */\nexport const Customer: OfirLiquidGlobals.Customer = window.__ofir?.Customer;\n\n/**\n * GiftCard is undefined if user is not on gift card page.\n *\n * @type {OfirLiquidGlobals.GiftCard}\n */\nexport const GiftCard: OfirLiquidGlobals.GiftCard = window.__ofir?.GiftCard;\n\n/**\n * Order is undefined unless user is on thank_you page, order status page or account/order detail\n * page.\n *\n * @type {OfirLiquidGlobals.Order}\n */\nexport const Order: OfirLiquidGlobals.Order = window.__ofir?.Order;\n\n/** @type {OfirLiquidGlobals.Page} */\nexport const Page: OfirLiquidGlobals.Page = window.__ofir?.Page;\n\n/** @type {OfirLiquidGlobals.Pagination} */\nexport const Pagination: OfirLiquidGlobals.Pagination = window.__ofir?.Pagination;\n\n/**\n * Product is undefined if not accessing a product page template.\n *\n * @type {OfirLiquidGlobals.Product}\n */\nexport const Product: OfirLiquidGlobals.Product = window.__ofir?.Product;\n\n/** @type {OfirLiquidGlobals.Request} */\nexport const Request: OfirLiquidGlobals.Request = window.__ofir?.Request;\n\n/** @type {OfirLiquidGlobals.Scripts} */\nexport const Scripts: OfirLiquidGlobals.Scripts = window.__ofir?.Scripts;\n\n/** @type {OfirLiquidGlobals.Search} */\nexport const Search: OfirLiquidGlobals.Search = window.__ofir?.Search;\n\n/** @type {OfirLiquidGlobals.SearchEngineListing} */\nexport const SearchEngineListing: OfirLiquidGlobals.SearchEngineListing =\n  window.__ofir?.SearchEngineListing;\n\n/** @type {OfirLiquidGlobals.SectionSettings} */\nexport const SectionSettings: OfirLiquidGlobals.SectionSettings = window.__ofir?.SectionSettings;\n\n/** @type {OfirLiquidGlobals.Shop} */\nexport const Shop: OfirLiquidGlobals.Shop = window.__ofir?.Shop;\n\n/** @type {OfirLiquidGlobals.Template} */\nexport const Template: OfirLiquidGlobals.Template = window.__ofir?.Template;\n\n/** @type {OfirLiquidGlobals.Theme} */\nexport const Theme: OfirLiquidGlobals.Theme = window.__ofir?.Theme;\n\n/** @type {OfirLiquidGlobals.ThemeSettings} */\nexport const ThemeSettings: OfirLiquidGlobals.ThemeSettings = window.__ofir?.ThemeSettings;\n","import * as LiquidGlobals from '../../common/liquid-global.js';\n\nexport default function updateCartHtml(html: string) {\n  const element = document.createElement('div');\n  element.innerHTML = html;\n\n  const cartContentElement = element.querySelector('.modal-content');\n  if (!cartContentElement) {\n    return;\n  }\n\n  if (LiquidGlobals.Template.name !== 'cart') {\n    const cartModalElement = document.getElementById('cart-modal');\n    const modalContentElement = cartModalElement?.querySelector('.modal-content');\n    if (!modalContentElement) {\n      return;\n    }\n\n    const modalContentCartHeaderElement = modalContentElement.querySelector('.cart__header');\n    modalContentCartHeaderElement.innerHTML =\n      cartContentElement.querySelector('.cart__header').innerHTML;\n\n    const modalContentCartContentElement = modalContentElement.querySelector('.cart__main');\n    modalContentCartContentElement.innerHTML =\n      cartContentElement.querySelector('.cart__main').innerHTML;\n\n    const cartCount =\n      cartContentElement.querySelector('.cart__header > span').textContent.replace(/\\D+/, '');\n    const cartCounterElement = document.querySelector('#cart-counter');\n    cartCounterElement.textContent = cartCount;\n\n    if (parseInt(cartCount, 10) === 0) {\n      cartCounterElement.classList.add('hidden');\n    } else {\n      cartCounterElement.classList.remove('hidden');\n    }\n  } else {\n    // Update cart content if user landed on cart page\n    const cartPageElement = document.getElementById('cart-page');\n    if (cartPageElement.classList.contains('cart--empty')) {\n      return;\n    }\n\n    const cartPageCartHeaderElement = cartPageElement.querySelector('.cart__header');\n    cartPageCartHeaderElement.innerHTML =\n      cartContentElement.querySelector('.cart__header').innerHTML;\n\n    const cartPageCartContentElement = cartPageElement.querySelector('.cart__main');\n    cartPageCartContentElement.innerHTML =\n      cartContentElement.querySelector('.cart__main').innerHTML;\n  }\n\n  const event = new Event('cart-html-updated');\n  dispatchEvent(event);\n}\n\ndeclare global {\n  interface WindowEventMap {\n    'cart-html-updated': Event;\n  }\n}\n","import * as ajax from '@integrabeauty/shopify-ajax-api';\nimport * as CallUtils from '../../../lib/call-utils.js';\nimport updateCartHtml from '../update-cart-html.js';\n\n/**\n * Utility method to remove Shopify cart line item in the shopping cart using the provided Ajax API\n * endpoint.\n *\n * CAUTION: This endpoint does not validate inventory levels for variants already in the cart,\n * meaning it is possible to add more inventory than is available in this particular use case.\n *\n * The imported CallUtils methods emit events to notify any view layer logic listening so it can\n * implement any required changes to update the user of the application state. You only need to use\n * these methods if you want the UX/UI to represent and/or provide messaging for the asynchronous\n * request.\n *\n * @param lineItemKeys line_item keys\n * @param notifyView passed on to ofir-cart-call-ended event to for view layer listeners\n *\n * @returns cart data\n *\n * @see https://shopify.dev/api/ajax/reference/cart#post-locale-cart-update-js\n */\n\nexport async function removeItems(lineItemKeys: string[], notifyView = true) {\n  CallUtils.startCall('updateCart');\n\n  const updateStartedEvent = new Event('cart-update-started');\n  dispatchEvent(updateStartedEvent);\n\n  let response;\n  let cart: ajax.Cart;\n\n  const updates: Record<string, 0> = {};\n  for (const lineItemKey of lineItemKeys) {\n    updates[lineItemKey] = 0;\n  }\n\n  const payload = {\n    updates,\n    sections: ['modal-cart']\n  };\n\n  try {\n    const previousCart = await ajax.cartFetch();\n    response = await ajax.cartUpdate(payload);\n\n    if (!response.ok) {\n      throw new Error(`CartService.updateCart Error: ${response.status} ${response.statusText} ` +\n        response.url);\n    }\n\n    cart = await response.json();\n\n    if (cart.sections?.['modal-cart']) {\n      updateCartHtml(cart.sections['modal-cart']);\n    }\n\n    type CartUpdatedEventDetail = WindowEventMap['cart-updated']['detail'];\n    const cartUpdatedEvent = new CustomEvent<CartUpdatedEventDetail>('cart-updated', {\n      detail: {\n        cart,\n        payload,\n        success: true\n      }\n    });\n    dispatchEvent(cartUpdatedEvent);\n\n    for (const lineItemKey of lineItemKeys) {\n      const lineItem = previousCart.items.find(item => item.key === lineItemKey);\n\n      if (!lineItem) {\n        throw Error(`Line item not found in cart. Key: ${lineItemKey}`);\n      }\n\n      type LineItemRemovedEventDetail = WindowEventMap['line-item-removed']['detail'];\n      const removedEvent = new CustomEvent<LineItemRemovedEventDetail>('line-item-removed', {\n        detail: {\n          item: lineItem\n        }\n      });\n      dispatchEvent(removedEvent);\n    }\n  } catch (error) {\n    const detail = {\n      error_code: response?.status,\n      error_message: response?.statusText,\n      updates,\n      success: false,\n      payload\n    };\n\n    window.Sentry?.captureException(error, {\n      extra: {\n        ...detail,\n        source: 'cart-service-update-cart'\n      }\n    });\n\n    type Detail = WindowEventMap['cart-updated']['detail'];\n    const event = new CustomEvent<Detail>('cart-updated', {\n      detail\n    });\n    dispatchEvent(event);\n  } finally {\n    CallUtils.endCall(notifyView, false);\n  }\n\n  return cart;\n}\n\ntype LineItemRemovedEvent = CustomEvent<{\n  /**\n   * Item object from the cart before the update. It's important to have the original item because\n   * the Google Analytics event needs data of the item that was removed (as well as other scripts\n   * may need it), and the updated cart doesn't contain it.\n   */\n  item: ajax.Item;\n}>;\n\ndeclare global {\n  interface WindowEventMap {\n    'line-item-removed': LineItemRemovedEvent;\n  }\n}\n","import type { CustomElement } from '@integrabeauty/custom-elements';\nimport html from './index.html';\nimport styles from './index.scss';\nimport { removeItems } from './remove-items.js';\n\n/**\n * A custom web component representing a button for removing items from the cart.\n */\nclass CartItemRemoveButton extends HTMLElement implements CustomElement {\n  declare readonly dataset: {\n    /**\n     * Comma-separated list of the product variant key, e.g.\n     * \"41770561273907:da3939d2d5d7db8847bca5d8880c2ad1,41770561273907:da3939d2d5d7db8847bca5d8880c2\n     * ad1\"\n     *\n     * @see https://shopify.dev/docs/api/liquid/objects/line_item#line_item-key\n     */\n    lineItemKeys: string;\n  };\n\n  private onClickedBound = this.onClicked.bind(this);\n  private onCartUpdateStartedBound = this.onCartUpdateStarted.bind(this);\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n    this.render();\n  }\n\n  public disconnectedCallback() {\n    const button = this.shadowRoot.querySelector('button');\n    button.removeEventListener('click', this.onClickedBound);\n    removeEventListener('cart-update-started', this.onCartUpdateStartedBound);\n  }\n\n  private initializeShadowRoot() {\n    if (!this.shadowRoot) {\n      this.attachShadow({ mode: 'open' });\n      this.shadowRoot.innerHTML = `<style>${styles}</style>${html}`;\n    }\n  }\n\n  private render() {\n    const button = this.shadowRoot.querySelector('button');\n    button.addEventListener('click', this.onClickedBound);\n\n    addEventListener('cart-update-started', this.onCartUpdateStartedBound);\n  }\n\n  private onClicked(event: Event) {\n    const button = <HTMLButtonElement>event.currentTarget;\n\n    // We don't need to enable this button when cart update ended because the whole cart component\n    // rerenders when cart update.\n    button.disabled = true;\n    const lineItemKeys = this.dataset.lineItemKeys.split(',');\n    removeItems(lineItemKeys).catch(() => {\n      // ignore catch as it never happens.\n    });\n  }\n\n  private onCartUpdateStarted(_event: Event) {\n    // Prevent button click during the cart update\n    const button = this.shadowRoot.querySelector('button');\n\n    // We don't need to enable this button when cart update ended because the whole cart component\n    // rerenders when cart update.\n    button.disabled = true;\n  }\n}\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'cart-item-remove-button': CartItemRemoveButton;\n  }\n}\n\nif (!customElements.get('cart-item-remove-button')) {\n  customElements.define('cart-item-remove-button', CartItemRemoveButton);\n}\n","import type * as ajax from '@integrabeauty/shopify-ajax-api';\nimport * as CallUtils from '../../../lib/call-utils.js';\nimport updateCartHtml from '../update-cart-html.js';\n\n/**\n * Utility method to update a users Shopify cart note or attributes and multiple line item\n * quantities in the shopping cart using the provided Ajax API endpoint.\n *\n * CAUTION: This endpoint does not validate inventory levels for variants already in the cart,\n * meaning it is possible to add more inventory than is available in this particular use case.\n *\n * The imported CallUtils methods emit events to notify any view layer logic listening so it can\n * implement any required changes to update the user of the application state. You only need to use\n * these methods if you want the UX/UI to represent and/or provide messaging for the asynchronous\n * request.\n *\n * @param payload update cart request payload\n * @param notifyView passed on to ofir-cart-call-ended event to for view layer listeners\n *\n * @returns cart data\n *\n * @see https://shopify.dev/api/ajax/reference/cart#post-locale-cart-update-js\n */\n\nexport async function updateCart(payload: ajax.CartUpdateInput, notifyView = true) {\n  CallUtils.startCall('updateCart');\n\n  const updateStartedEvent = new Event('cart-update-started', {\n    cancelable: false\n  });\n  dispatchEvent(updateStartedEvent);\n\n  let response;\n  let cart;\n\n  try {\n    const body = JSON.stringify({ ...payload, sections: 'modal-cart' });\n    response = await fetch('/cart/update.js', {\n      method: 'POST',\n      body,\n      headers: {\n        'Content-Type': 'application/json'\n      }\n    });\n\n    if (!response.ok) {\n      throw new Error(`CartService.updateCart Error: ${response.status} ${response.statusText} ` +\n        response.url);\n    }\n\n    cart = <ajax.Cart> await response.json();\n\n    if (cart.sections?.['modal-cart']) {\n      updateCartHtml(cart.sections['modal-cart']);\n    }\n\n    const customEvent = new CustomEvent('cart-updated', {\n      cancelable: false,\n      detail: {\n        cart,\n        payload,\n        success: true\n      }\n    });\n    dispatchEvent(customEvent);\n  } catch (error) {\n    const detail = {\n      error_code: response?.status,\n      error_message: response?.statusText,\n      payload,\n      success: false\n    };\n\n    window.Sentry?.captureException(error, {\n      extra: {\n        ...detail,\n        source: 'cart-service-update-cart'\n      }\n    });\n\n    const customEvent = new CustomEvent('cart-updated', {\n      cancelable: false,\n      detail\n    });\n    dispatchEvent(customEvent);\n  } finally {\n    CallUtils.endCall(notifyView, false);\n  }\n\n  return cart;\n}\n\ntype CartUpdateStartedEvent = Event;\n\ntype CartUpdatedEvent = CustomEvent<{\n  /**\n   * In case of an error while updating the cart the event will be sent without the `cart` property.\n   */\n  cart?: ajax.Cart;\n  error_code?: number;\n  error_message?: string;\n\n  /**\n   * The payload that was sent to the server for the cart update. This is optional because we\n   * dispatch the 'cart-updated' event also when adding a product to the cart, and in that case\n   * there is no payload for cart update. This is neccessary to support the upsell modal.\n   *\n   * @todo the whole cart flow should be refactored in a L'ange way\n   */\n  payload?: ajax.CartUpdateInput;\n  success: boolean;\n}>;\n\ndeclare global {\n  interface WindowEventMap {\n    'cart-update-started': CartUpdateStartedEvent;\n    'cart-updated': CartUpdatedEvent;\n  }\n}\n","import * as CartService from './cart-service/index.js';\n\n/**\n * Build an array of attributes based on page state.\n *\n * We collect some attributes from the currently loaded page and stash them in the checkout,\n * treating it like a server-side session store. These are important attributes to retain across\n * multiple browser sessions, such as when a visitor resumes an abandoned cart in a new browser\n * session. We could probably come up with other ways to stitch session data together but a lot of\n * backend functionality is currently written that assumes these properties exist.\n *\n * An ideal system would be something like Segment. We send values we want to retain at session\n * scope in misc. track or identify or page events to Segment, and then we aggregate by session\n * within Segment across multiple events to get a whole picture. However, we did not originally use\n * Segment when the site was first designed, and for now are continuing the legacy practice.\n *\n * The facebook attribution service uses some of these properties to link LittleData order completed\n * events to ad click events captured in Segment.\n */\nfunction gatherAttributes() {\n  const url = new URL(location.href);\n  const attributes = [];\n\n  attributes.push({\n    key: 'checkout_started_ua',\n    value: navigator.userAgent\n  });\n\n  // google related url param\n  const gclid = url.searchParams.get('gclid')?.trim();\n  if (gclid) {\n    attributes.push({\n      key: 'gclid',\n      value: gclid\n    });\n  }\n\n  // google related url param, related to impersonal aggregated user identification\n  const gbraid = url.searchParams.get('gbraid')?.trim();\n  if (gbraid) {\n    attributes.push({\n      key: 'gbraid',\n      value: gbraid\n    });\n  }\n\n  // google related url param, related to impersonal aggregated user identification\n  const wbraid = url.searchParams.get('wbraid')?.trim();\n  if (wbraid) {\n    attributes.push({\n      key: 'wbraid',\n      value: wbraid\n    });\n  }\n\n  const transactionId = url.searchParams.get('transaction_id')?.trim();\n  if (transactionId) {\n    attributes.push({\n      key: 'hasoffers',\n      value: location.href\n    });\n    attributes.push({\n      key: 'hasoffers_updated_at',\n      value: Date.now().toString()\n    });\n  }\n\n  const adId = url.searchParams.get('ad.id')?.trim();\n  if (adId && adId !== '{ad.id}' && adId !== '{{ad.id}}') {\n    attributes.push({\n      key: 'facebook_ad_id',\n      value: adId\n    });\n  }\n\n  const fbclid = url.searchParams.get('fbclid')?.trim();\n  if (fbclid && fbclid !== '{fbclid}' && fbclid !== '{{fbclid}}') {\n    attributes.push({\n      key: 'fbclid',\n      value: fbclid\n    });\n  }\n\n  const utmCampaign = url.searchParams.get('utm_campaign')?.trim();\n  if (utmCampaign) {\n    attributes.push({\n      key: 'utm_campaign',\n      value: utmCampaign\n    });\n  }\n\n  const utmSource = url.searchParams.get('utm_source')?.trim();\n  if (utmSource) {\n    attributes.push({\n      key: 'utm_source',\n      value: utmSource\n    });\n  }\n\n  const utmMedium = url.searchParams.get('utm_medium')?.trim();\n  if (utmMedium) {\n    attributes.push({\n      key: 'utm_medium',\n      value: utmMedium\n    });\n  }\n\n  if (utmSource?.toLowerCase() === 'attentive' && utmMedium?.toLowerCase() === 'sms') {\n    attributes.push({\n      key: 'attentive_last_click_epoch',\n      value: Date.now().toString()\n    });\n  }\n\n  const affiliate = url.searchParams.get('aff');\n  if (affiliate) {\n    attributes.push({\n      key: 'affiliate',\n      value: affiliate\n    });\n  }\n\n  const klaviyoId = getKlaviyoId();\n  if (klaviyoId) {\n    attributes.push({\n      key: 'klaviyo_id',\n      value: klaviyoId\n    });\n  }\n\n  const clickId = getKlaviyoClickId();\n  if (clickId) {\n    attributes.push({\n      key: 'klaviyo_click_id',\n      value: clickId\n    }, {\n      key: 'klaviyo_last_click_epoch',\n      value: Date.now().toString()\n    });\n  }\n\n  const fbp = getCookie('fbp');\n  if (fbp) {\n    attributes.push({\n      key: 'fbp',\n      value: fbp\n    });\n  }\n\n  const fbc = getCookie('fbc');\n  if (fbc) {\n    attributes.push({\n      key: 'fbc',\n      value: fbc\n    });\n  }\n\n  try {\n    // For some reason the value is sometimes wrapped in quotes\n    const tiktokSessionId = sessionStorage?.getItem('tt_sessionId')?.trim()?.replace(/\"/g, '');\n    if (tiktokSessionId) {\n      attributes.push({\n        key: 'tiktok_session_id',\n        value: tiktokSessionId\n      });\n    }\n  } catch (error) {\n    // ignore\n  }\n\n  return attributes;\n}\n\n/**\n * @param cookieName key value of cookie being retrieved\n */\nfunction getCookie(cookieName: string) {\n  const name = cookieName + '=';\n  const cookies = document.cookie.split(';');\n  for (let cookie of cookies) {\n    while (cookie.charAt(0) === ' ') {\n      cookie = cookie.substring(1);\n    }\n\n    if (cookie.indexOf(name) === 0) {\n      return cookie.substring(name.length, cookie.length);\n    }\n  }\n\n  return undefined;\n}\n\nfunction getKlaviyoId() {\n  const encoded = getCookie('__kla_id');\n  if (encoded) {\n    const decoded = atob(encoded);\n    const parsed: { cid: string; } = JSON.parse(decoded);\n    if (parsed.cid) {\n      return atob(parsed.cid);\n    }\n  }\n}\n\n/**\n * @see https://help.klaviyo.com/hc/en-us/articles/115005076767#h_01HADAYAACVVC4BXQ0ES5Y50TC\n */\nfunction getKlaviyoClickId() {\n  const url = new URL(location.href);\n  const kx = url.searchParams.get('_kx');\n  if (kx) {\n    return kx;\n  }\n}\n\nfunction onDOMContentLoaded() {\n  const attributeKeyValues = gatherAttributes();\n  const attributes: Record<string, string> = {};\n\n  for (const attribute of attributeKeyValues) {\n    attributes[attribute.key] = attribute.value;\n  }\n\n  CartService.updateCart({ attributes }, false)\n    .catch(error => {\n      window.Sentry?.captureException(error, {\n        extra: {\n          ...attributes,\n          source: 'cart-attribution-update-cart'\n        }\n      });\n    });\n}\n\naddEventListener('DOMContentLoaded', onDOMContentLoaded);\n","import * as LiquidGlobals from '../../common/liquid-global.js';\n\nfunction loadCheckoutHandler() {\n  const cartPage = document.getElementById('cart-page');\n  if (!cartPage) {\n    return;\n  }\n\n  const checkoutCta = cartPage.querySelector('.cart__checkout');\n  if (checkoutCta) {\n    checkoutCta.addEventListener('click', onCheckoutClicked);\n  }\n}\n\n/**\n * Handles the click event on the checkout CTA on the cart page. We want to display the upsell modal\n * when the checkout button is clicked, but only if it wasn't displayed before. If the upsell modal\n * was already displayed, we want to proceed to checkout as normal.\n */\nfunction onCheckoutClicked(event: Event) {\n  const upsellModal = document.querySelector('#modal-upsell');\n  if (!upsellModal) {\n    console.warn('#modal-upsell element not found');\n    return;\n  }\n\n  if (cart_drawer_upsell_modal_displayed) {\n    return;\n  }\n\n  event.preventDefault();\n\n  const modalShowEvent = new CustomEvent('upsell-modal-show-requested');\n  dispatchEvent(modalShowEvent);\n\n  cart_drawer_upsell_modal_displayed = true;\n}\n\nif (LiquidGlobals.Template.name === 'cart') {\n  addEventListener('DOMContentLoaded', loadCheckoutHandler);\n  addEventListener('cart-html-updated', loadCheckoutHandler);\n}\n","import type * as ajax from '@integrabeauty/shopify-ajax-api';\nimport * as LiquidGlobal from '../../common/liquid-global.js';\nimport * as CartService from './cart-service/index.js';\n\nexport function loadCartModal() {\n  const cartModalElement = document.getElementById('cart-modal');\n  if (!cartModalElement) {\n    return;\n  }\n\n  const modalContentElement = cartModalElement.querySelector('.modal-content');\n  if (!modalContentElement) {\n    return;\n  }\n\n  const closeModalElement = cartModalElement.querySelector('.cart__close');\n  if (!closeModalElement) {\n    return;\n  }\n\n  closeModalElement.addEventListener('click', onCloseClicked);\n\n  modalContentElement.addEventListener('click', function (event: Event) {\n    // TODO why is this needed? Review, add comments or remove\n    if (!closeModalElement.contains(<HTMLElement>event.target)) {\n      event.stopPropagation();\n    }\n  });\n\n  const checkoutCta = cartModalElement.querySelector('.cart__checkout');\n  if (checkoutCta) {\n    checkoutCta.addEventListener('click', onCheckoutClicked);\n  }\n}\n\nfunction onCloseClicked(event: Event) {\n  event.preventDefault();\n  closeModal();\n}\n\n/**\n * Handles the click event on the checkout CTA in the cart modal. We want to display the upsell\n * modal when the checkout button is clicked, but only if it wasn't displayed before. If the upsell\n * modal was already displayed, we want to proceed to checkout as normal.\n */\nfunction onCheckoutClicked(event: Event) {\n  // check if the upsell modal exists\n  const upsellModal = document.querySelector('#modal-upsell');\n  if (!upsellModal) {\n    console.warn('#modal-upsell element not found');\n    return;\n  }\n\n  // the upsell modal should be displayed only once, if it was already displayed, proceed to\n  // checkout\n  if (cart_drawer_upsell_modal_displayed) {\n    return;\n  }\n\n  // block going to checkout and open the upsell modal instead\n\n  event.preventDefault();\n\n  closeModal();\n\n  const modalShowEvent = new Event('upsell-modal-show-requested');\n  dispatchEvent(modalShowEvent);\n\n  // set a global variable to indicate that the upsell modal was displayed\n  cart_drawer_upsell_modal_displayed = true;\n}\n\nfunction onShroudClicked(event: WindowEventMap['shroud-clicked']) {\n  const modal = document.querySelector('#cart-modal');\n  if (modal === event.detail.element) {\n    closeModal();\n  }\n}\n\nfunction closeModal() {\n  const cartModalElement = document.getElementById('cart-modal');\n  if (!cartModalElement) {\n    console.warn('#cart-modal element not found');\n    return;\n  }\n\n  cartModalElement.classList.add('hidden');\n\n  // send event to modal-manager so it can show the modal shroud and handle accessibility\n  type Detail = WindowEventMap['modal-close-requested']['detail'];\n  const modalCloseEvent = new CustomEvent<Detail>('modal-close-requested', {\n    detail: { element: cartModalElement }\n  });\n  dispatchEvent(modalCloseEvent);\n}\n\nfunction openModal() {\n  const modal = document.getElementById('cart-modal');\n\n  // check if the modal exists, expected to be always in the DOM but just in case, if it doesn't\n  // exist, log a warning and return\n  if (!modal) {\n    console.warn('#modal-upsell element not found');\n    return;\n  }\n\n  // cart is already open, do nothing\n  if (!modal.classList.contains('hidden')) {\n    return;\n  }\n\n  modal.classList.remove('hidden');\n\n  CartService.getCart(false).then((cart: ajax.Cart) => {\n    const viewCartEvent = new CustomEvent<OfirCustomEvents.ViewCartEventDetails>('ofir-view-cart', {\n      cancelable: false,\n      detail: {\n        items: cart.items,\n        source: 'cart-header-icon'\n      }\n    });\n\n    dispatchEvent(viewCartEvent);\n  }).catch(console.error);\n\n  // send event to modal-manager so it can show the modal shroud and handle accessibility\n  type Detail = WindowEventMap['modal-show-requested']['detail'];\n  const modalShowEvent = new CustomEvent<Detail>('modal-show-requested', {\n    detail: { element: modal }\n  });\n  dispatchEvent(modalShowEvent);\n\n  // notify other components that the cart modal was opened, so they can react accordingly\n  const cartModalOpenedEvent = new Event('cart-modal-opened');\n  dispatchEvent(cartModalOpenedEvent);\n}\n\nfunction updateListeners(event: OfirCustomEvents.CartCallEndedEvent) {\n  if (event.detail.notify_view) {\n    loadCartModal();\n    openModal();\n  }\n}\n\nif (LiquidGlobal.Template.name !== 'cart') {\n  addEventListener('ofir-cart-call-ended', <EventListener>updateListeners);\n  addEventListener('DOMContentLoaded', loadCartModal);\n  addEventListener('cart-html-updated', loadCartModal);\n  addEventListener('cart-modal-show-requested', openModal);\n  addEventListener('cart-modal-close-requested', closeModal);\n  addEventListener('shroud-clicked', onShroudClicked);\n}\n\ndeclare global {\n  interface WindowEventMap {\n    'cart-modal-close-requested': Event;\n    'cart-modal-opened': Event;\n    'cart-modal-show-requested': Event;\n  }\n}\n","import type * as ajax from '@integrabeauty/shopify-ajax-api';\nimport * as CallUtils from '../../../lib/call-utils.js';\n\n/**\n * Utility method to retrieve a users current cart using the provided Shopify Ajax API endpoint.\n *\n * The imported CallUtils methods emit events to notify any view layer logic listening so it can\n * implement any required changes to update the user of the application state. You only need to use\n * these methods if you want the UX/UI to represent and/or provide messaging for the asynchronous\n * request.\n *\n * Since all Shopify cart interaction are done client side asynchronously, there are numorous use\n * cases involving the need for the most up-to-date shopping cart. Examples being after a user adds\n * an item to their cart or changes a quantity. The LiquiGlobal.Cart values would not be accurate,\n * and if required; users should utilize this method with emitStartStop value of false to not\n * interfere with UX/UI operations.\n *\n * @param notifyView passed on to ofir-cart-call-ended event to for view layer listeners (false)\n * @param emitStartStop passed as control to emit start and stop call events (false)\n *\n * @returns cart data\n *\n * @see https://shopify.dev/api/ajax/reference/cart#get-locale-cart-js\n */\nexport async function getCart(notifyView = true, emitStartStop = false) {\n  if (emitStartStop) {\n    CallUtils.startCall('getCart');\n  }\n\n  let response;\n  let cart;\n\n  try {\n    response = await fetch('/cart.js', {\n      method: 'GET',\n      headers: {\n        'Content-Type': 'application/json'\n      }\n    });\n\n    if (!response.ok) {\n      throw new Error(`CartService.getCart Error: ${response.status} ${response.statusText} ` +\n        response.url);\n    }\n\n    cart = <ajax.Cart> await response.json();\n  } catch (error) {\n    window.Sentry?.captureException(error, {\n      extra: {\n        source: 'cart-service-get-cart'\n      }\n    });\n  } finally {\n    if (emitStartStop) {\n      CallUtils.endCall(notifyView);\n    }\n  }\n\n  return cart;\n}\n","import type * as ajax from '@integrabeauty/shopify-ajax-api';\nimport * as CartService from './cart-service/index.js';\n\nasync function updateCartVariantQuantity(variantKey: string, quantity: number) {\n  const payload = <ajax.CartUpdateInput>{ updates: {} };\n  payload.updates[variantKey] = quantity;\n\n  await CartService.updateCart(payload);\n}\n\nfunction onQuantityChange(event: WindowEventMap['quantity-input-changed']) {\n  if (!event.detail?.variant_keys) {\n    return;\n  }\n\n  if (event.detail.source === 'cart-variant') {\n    // In the case of regular product variant `variant_keys` contains only one line item key\n    updateCartVariantQuantity(event.detail.variant_keys, event.detail.quantity)\n      .catch(error => {\n        throw new Error(<string>error);\n      });\n  }\n}\n\naddEventListener('quantity-input-changed', onQuantityChange);\n","import type { CustomElement } from '@integrabeauty/custom-elements';\nimport html from './index.html';\nimport styles from './index.scss';\n\n/**\n * Custom element that displays the free shipping progress message in the cart.\n *\n * Shows how much more the customer needs to spend to get free shipping, or confirms they qualify.\n * Updates automatically when the cart is updated via the `cart-updated` event.\n *\n * @example\n * <delivery-message\n *   data-shipping-threshold=\"75\"\n *   data-upsell=\"true\"\n * </delivery-message>\n */\nclass DeliveryMessage extends HTMLElement implements CustomElement {\n  declare readonly dataset: {\n    /**\n     * The price threshold (in dollars) for free shipping. Used to calculate how much more the\n     * customer needs to spend.\n     */\n    shippingThreshold: string;\n\n    /**\n     * Whether the message is rendered in the upsell modal or not. Boolean represented as string.\n     *\n     * There are special visibility rules for the upsell modal:\n     *   - if the user qualify for free shipping before the upsell opens, the message will be hidden\n     *   - if the user doesn't qualify for free shipping before the upsell opens, the message\n     *     will be shown, and it will be updated on each product add to show the remaining amount to\n     *     get free shipping\n     *   - if the upsell modal is open and the user becomes qualified for free shipping after adding\n     *     a product to the cart, the message remains shown\n     */\n    upsell: 'false' | 'true';\n  };\n\n  /**\n   * Whether the user currently qualifies for free shipping. We need to keep track of this to know\n   * whether to hide the message when the upsell modal is about to be shown.\n   */\n  private hasFreeShipping = false;\n\n  private onCartUpdatedBound = this.onCartUpdated.bind(this);\n  private onUpsellModalShowRequestedBound = this.onUpsellModalShowRequested.bind(this);\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n    addEventListener('cart-updated', this.onCartUpdatedBound);\n\n    if (this.dataset.upsell === 'true') {\n      addEventListener('upsell-modal-show-requested', this.onUpsellModalShowRequestedBound);\n    }\n  }\n\n  public disconnectedCallback() {\n    removeEventListener('cart-updated', this.onCartUpdatedBound);\n    removeEventListener('upsell-modal-show-requested', this.onUpsellModalShowRequestedBound);\n  }\n\n  private initializeShadowRoot() {\n    if (!this.shadowRoot) {\n      this.attachShadow({ mode: 'open' });\n      this.shadowRoot.innerHTML = `<style>${styles}</style>${html}`;\n    }\n  }\n\n  /**\n   * Handles the `upsell-modal-show-requested` event. This event is emitted when the upsell modal is\n   * about to be shown.\n   */\n  private onUpsellModalShowRequested(_event: Event) {\n    // if the user qualifies for free shipping when the upsell modal is about to be shown, we hide\n    // the delivery message in the modal since it's not relevant anymore\n    if (this.hasFreeShipping) {\n      this.hidden = true;\n    }\n  }\n\n  /**\n   * Handles the `cart-updated` event. On each cart update, we check if the user qualifies for free\n   * shipping and update the message accordingly.\n   */\n  private onCartUpdated(event: WindowEventMap['cart-updated']) {\n    // it's possible for this event to fire before the element is connected, do nothing in that case\n    if (!this.isConnected) {\n      return;\n    }\n\n    const cart = event.detail?.cart;\n    if (!cart) {\n      return;\n    }\n\n    const hasSubscription = cart.items.some(item => item.selling_plan_allocation);\n\n    // convert from cents to dollars\n    const totalPrice = cart.total_price / 100;\n    const shippingThreshold = parseInt(this.dataset.shippingThreshold, 10);\n    this.hasFreeShipping = shippingThreshold - totalPrice <= 0 || hasSubscription;\n    const noFreeShipping = this.shadowRoot.querySelector('.no-free-shipping');\n    const freeShipping = this.shadowRoot.querySelector('.free-shipping');\n\n    if (this.hasFreeShipping) {\n      noFreeShipping.classList.add('hidden');\n      freeShipping.classList.remove('hidden');\n    } else {\n      // set remaining amount until free shipping\n      const tillFreeShipping = (shippingThreshold - totalPrice).toFixed(2).replace(/\\.00$/, '');\n      const remaining = this.shadowRoot.querySelector('.remaining');\n      remaining.textContent = `$${tillFreeShipping}`;\n\n      // set progress bar\n      const percent = Math.round((totalPrice / shippingThreshold) * 100);\n      const progress = this.shadowRoot.querySelector<HTMLElement>('.progress');\n      progress.style.width = `${percent}%`;\n\n      noFreeShipping.classList.remove('hidden');\n      freeShipping.classList.add('hidden');\n    }\n  }\n}\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'delivery-message': DeliveryMessage;\n  }\n}\n\nif (!customElements.get('delivery-message')) {\n  customElements.define('delivery-message', DeliveryMessage);\n}\n"],"names":["__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","exports","module","__webpack_modules__","m","d","definition","Array","isArray","i","length","key","binding","o","Object","defineProperty","enumerable","value","get","f","e","chunkId","Promise","all","keys","reduce","promises","u","miniCssF","obj","prop","prototype","hasOwnProperty","call","inProgress","dataWebpackPrefix","l","url","done","push","script","needAttach","scripts","document","getElementsByTagName","s","getAttribute","createElement","charset","nc","setAttribute","src","onScriptComplete","prev","event","onerror","onload","clearTimeout","timeout","doneFns","parentNode","removeChild","forEach","fn","setTimeout","bind","type","target","head","appendChild","p","installedChunks","j","installedChunkData","promise","resolve","reject","error","Error","loadingEnded","errorType","realSrc","message","name","request","webpackJsonpCallback","parentChunkLoadingFunction","data","chunkIds","moreModules","runtime","some","id","chunkLoadingGlobal","self","AlertElement","HTMLElement","timerId","observedAttributes","onCloseClickedBound","this","onCloseClicked","connectedCallback","initializeShadowRoot","render","disconnectedCallback","shadowRoot","querySelector","removeEventListener","attachShadow","mode","innerHTML","addEventListener","dataset","timer","remove","parseInt","_event","customElements","define","CartMessages","onAddToCart","onUpdateLineItem","clearMessages","detail","success","showErrorMessage","messageHtml","items","map","item","title","join","showSuccessMessage","item_deleted","updatedItemId","cart","find","messaging","append","messages","querySelectorAll","splideSlidebarExtension","splide","components","options","slidebar","handle","maxPosition","maxTrackPosition","hasSlidebarMarkup","position","previousTouchX","enable","Elements","root","classList","onSplideResized","is","on","onSplideDragging","onSplideMoveStarted","onMousedown","onTouchstart","passive","onTransitionend","onSlidebarClicked","disable","add","off","calculateHandleWidth","handleWidth","perPage","Slides","getLength","Layout","listSize","sliderSize","processHandleMove","toPosition","newPosition","getHandlePositionWithinBounds","newSplidePosition","getCalculatedTrackPosition","Move","translate","requestAnimationFrame","style","transform","alignTrackPosition","index","Math","round","currentSplidePosition","getPosition","toIndex","Controller","go","onSplideOptionsUpdated","_options","toggleSlidebar","onSplideReady","isEnough","isOverflow","onMousemove","onMouseup","movementX","touch","touches","clientX","onTouchmove","onTouchend","transition","currentTarget","newSlidebarPosition","offsetX","offsetWidth","newTrackPosition","width","onSplideOverflowed","getCalculatedSlidebarPosition","trackPosition","getEnd","getIndex","slidebarPosition","mount","destroy","window","__webpack_public_path__","UpsellSection","onCarouselSlotChangeBound","onCarouselSlotChange","onCartModalOpenedBound","onCartModalOpened","assignedElements","initCarousel","catch","console","warn","refresh","Splide","then","element","fixedWidth","perMove","rewind","arrows","pagination","gap","keyboard","omitEnd","getRequestId","response","headers","isResponse","ok","status","json","decorateError","async","cartFetch","Shopify","routes","body","fetch","cache","isObject","startCall","_window$Sentry","_len","arguments","args","_key","Sentry","captureException","extra","level","endCall","Event","cancelable","dispatchEvent","notifyView","didTimeOut","CustomEvent","is_timeout","notify_view","__ofir","_window$__ofir","Cart","_window$__ofir2","Checkout","_window$__ofir3","Collection","_window$__ofir4","Customer","_window$__ofir5","GiftCard","_window$__ofir6","Order","_window$__ofir7","Page","_window$__ofir8","Pagination","_window$__ofir9","Product","_window$__ofir0","Request","_window$__ofir1","Scripts","_window$__ofir10","Search","_window$__ofir11","SearchEngineListing","_window$__ofir12","SectionSettings","_window$__ofir13","Shop","Template","_window$__ofir14","_window$__ofir15","Theme","_window$__ofir16","ThemeSettings","updateCartHtml","html","cartContentElement","cartModalElement","getElementById","modalContentElement","cartCount","textContent","replace","cartCounterElement","cartPageElement","contains","removeItems","lineItemKeys","updateStartedEvent","updates","lineItemKey","payload","sections","_cart$sections","previousCart","input","JSON","stringify","method","_error$response_body4","response_body","cartUpdate","statusText","cartUpdatedEvent","lineItem","removedEvent","_response","_response2","error_code","error_message","_extends","source","CartItemRemoveButton","onClickedBound","onClicked","onCartUpdateStartedBound","onCartUpdateStarted","disabled","split","updateCart","update_cart_extends","customEvent","gatherAttributes","_url$searchParams$get","_url$searchParams$get2","_url$searchParams$get3","_url$searchParams$get4","_url$searchParams$get5","_url$searchParams$get6","_url$searchParams$get7","_url$searchParams$get8","_url$searchParams$get9","URL","location","href","attributes","navigator","userAgent","gclid","searchParams","trim","gbraid","wbraid","Date","now","toString","adId","fbclid","utmCampaign","utmSource","utmMedium","toLowerCase","affiliate","klaviyoId","encoded","getCookie","decoded","atob","parsed","parse","cid","getKlaviyoId","clickId","kx","getKlaviyoClickId","fbp","fbc","_sessionStorage","tiktokSessionId","sessionStorage","getItem","cookieName","cookies","cookie","charAt","substring","indexOf","loadCheckoutHandler","cartPage","checkoutCta","onCheckoutClicked","cart_drawer_upsell_modal_displayed","preventDefault","modalShowEvent","loadCartModal","closeModalElement","stopPropagation","modal_cart_onCheckoutClicked","closeModal","modalCloseEvent","openModal","modal","emitStartStop","getCart","viewCartEvent","cartModalOpenedEvent","_event$detail","variant_keys","variantKey","quantity","updateCartVariantQuantity","attributeKeyValues","attribute","cart_attribution_extends","DeliveryMessage","hasFreeShipping","onCartUpdatedBound","onCartUpdated","onUpsellModalShowRequestedBound","onUpsellModalShowRequested","upsell","hidden","isConnected","hasSubscription","selling_plan_allocation","totalPrice","total_price","shippingThreshold","noFreeShipping","freeShipping","tillFreeShipping","toFixed","percent"],"sourceRoot":""}