{
  "version": 3,
  "sources": ["/cdn/shop/t/42/assets/free-shipping-goal.js?x=1"],
  "sourcesContent": ["import { Component } from \"@theme/component\";\nimport { ThemeEvents } from \"@theme/events\";\nimport { formatCurrency } from \"@theme/utilities\";\n\n/**\n * Progress Bar Component\n *\n * Reusable progress bar component that can be used anywhere\n * Later can be moved to separate file\n */\nclass ProgressBar extends Component {\n  static get observedAttributes() {\n    return [\"percent\", \"label\"];\n  }\n\n  #isReady = false;\n\n  connectedCallback() {\n    super.connectedCallback();\n    this.#updateDisplay();\n    this.#isReady = true;\n\n    // Dispatch ready event\n    this.dispatchEvent(\n      new CustomEvent(\"progress-bar-ready\", {\n        bubbles: true,\n        detail: { element: this },\n      })\n    );\n  }\n\n  attributeChangedCallback() {\n    if (this.#isReady) {\n      this.#updateDisplay();\n    }\n  }\n\n  /**\n   * Update progress bar display\n   */\n  #updateDisplay() {\n    const percent = this.getAttribute(\"percent\") || \"0\";\n    const label = this.getAttribute(\"label\") || \"\";\n\n    // Update progress fill\n    const fill = this.querySelector(\".progress-bar__fill\");\n    if (fill) {\n      const percentValue = Math.min(Math.max(parseFloat(percent), 0), 100);\n      fill.style.setProperty(\"--percent\", `${percentValue}%`);\n    }\n\n    // Update label if present\n    const labelElement = this.querySelector(\".progress-bar__label\");\n    if (labelElement && label) {\n      labelElement.textContent = label;\n    }\n  }\n\n  /**\n   * Update progress programmatically\n   * @param {number|string} percent - Progress percentage\n   * @param {string} label - Optional label\n   */\n  update(percent, label = \"\") {\n    this.setAttribute(\"percent\", percent.toString());\n    if (label) {\n      this.setAttribute(\"label\", label);\n    }\n  }\n}\n\n/**\n * Free Shipping Goal Component\n *\n * Displays free shipping progress and updates in real-time when cart changes.\n * Supports both single currency (with conversion) and multiple currency formats.\n *\n * @extends {Component}\n */\nexport class FreeShippingGoal extends Component {\n  #progressBarElement = null;\n\n  connectedCallback() {\n    super.connectedCallback();\n\n    // Listen to cart targets\n    document.addEventListener(ThemeEvents.cartUpdate, this.#handleCartUpdate);\n    document.addEventListener(\"progress-bar-ready\", this.#handleProgressBarReady);\n\n    // Initialize on load\n    this.#initialize();\n  }\n\n  disconnectedCallback() {\n    super.disconnectedCallback();\n    document.removeEventListener(ThemeEvents.cartUpdate, this.#handleCartUpdate);\n    document.removeEventListener(\"progress-bar-ready\", this.#handleProgressBarReady);\n  }\n\n  /**\n   * Handle progress bar ready event\n   * @param {CustomEvent} event - Progress bar ready event\n   */\n  #handleProgressBarReady = (event) => {\n    // Check if this progress bar belongs to us\n    if (event.detail.element.closest(\"free-shipping-goal\") === this) {\n      this.#progressBarElement = event.detail.element;\n    }\n  };\n\n  /**\n   * Handle cart update events\n   * @param {CustomEvent} event - Cart update event\n   */\n  #handleCartUpdate = (event) => {\n    const { data } = event.detail;\n\n    // Update component data from cart response\n    if (data.sections) {\n      this.#updateCartTotalFromEvent(data);\n\n      // Re-setup currency conversion in case currency changed\n      this.#setupCurrencyConversion();\n\n      // Update display immediately after data extraction\n      this.#updateDisplay();\n    }\n  };\n\n  /**\n   * Initialize the component\n   */\n  #initialize() {\n    this.#setupCurrencyConversion();\n\n    // Try to get progress bar element immediately\n    this.#progressBarElement = this.querySelector(\"progress-bar\");\n\n    requestAnimationFrame(() => {\n      this.#updateDisplay();\n    });\n  }\n\n  /**\n   * Update cart data from cart update event\n   * @param {Object} cartData - Cart update event data\n   */\n  #updateCartTotalFromEvent(cartData) {\n    if (!cartData.sections) return;\n\n    const sectionId = this.closest(\"[data-section-id]\")?.dataset.sectionId;\n    if (!sectionId || !cartData.sections[sectionId]) return;\n\n    const newSectionHTML = new DOMParser().parseFromString(\n      cartData.sections[sectionId],\n      \"text/html\"\n    );\n    const newFreeShippingGoal = newSectionHTML.querySelector(\"free-shipping-goal\");\n\n    if (!newFreeShippingGoal) return;\n\n    // Update all relevant data attributes\n    const newData = {\n      cartTotal: newFreeShippingGoal.dataset.cartTotal,\n      currentCurrency: newFreeShippingGoal.dataset.currentCurrency,\n      minimumAmount: newFreeShippingGoal.dataset.minimumAmount,\n      isSingleCurrency: newFreeShippingGoal.dataset.isSingleCurrency,\n      shopCurrency: newFreeShippingGoal.dataset.shopCurrency,\n    };\n\n    // Only update if data actually changed to avoid unnecessary re-renders\n    let hasChanges = false;\n    for (const [key, value] of Object.entries(newData)) {\n      if (this.dataset[key] !== value) {\n        this.dataset[key] = value;\n        hasChanges = true;\n      }\n    }\n\n    // Data updated successfully\n  }\n\n  /**\n   * Setup currency conversion logic\n   */\n  #setupCurrencyConversion() {\n    const minimumAmount = parseFloat(this.dataset.minimumAmount);\n    const isSingleCurrency = this.dataset.isSingleCurrency === \"true\";\n    const shopCurrency = this.dataset.shopCurrency;\n    const currentCurrency = this.dataset.currentCurrency;\n\n    // Validate minimum amount\n    if (isNaN(minimumAmount) || minimumAmount <= 0) {\n      console.warn(\"FreeShippingGoal: Invalid minimum amount\", this.dataset.minimumAmount);\n      this.convertedAmountInCents = 0;\n      return;\n    }\n\n    // Cache key for conversion to avoid recalculation\n    const cacheKey = `${minimumAmount}-${currentCurrency}-${window.Shopify?.currency?.rate || 1}`;\n\n    if (this.conversionCache?.key === cacheKey) {\n      return; // Use cached value\n    }\n\n    // If single currency and current currency differs from shop currency\n    if (isSingleCurrency && currentCurrency !== shopCurrency) {\n      // Use Shopify currency rate for conversion\n      if (window.Shopify?.currency?.rate) {\n        const rate = parseFloat(window.Shopify.currency.rate);\n        if (!isNaN(rate) && rate > 0) {\n          this.convertedAmount = minimumAmount * rate;\n          this.convertedAmountInCents = Math.round(this.convertedAmount * 100);\n        } else {\n          console.warn(\"FreeShippingGoal: Invalid currency rate\", window.Shopify.currency.rate);\n          this.convertedAmountInCents = Math.round(minimumAmount * 100);\n        }\n      } else {\n        // Fallback if no rate available\n        this.convertedAmountInCents = Math.round(minimumAmount * 100);\n      }\n    } else {\n      // No conversion needed\n      this.convertedAmountInCents = Math.round(minimumAmount * 100);\n    }\n\n    // Cache the conversion result\n    this.conversionCache = {\n      key: cacheKey,\n      convertedAmountInCents: this.convertedAmountInCents,\n      minimumAmount,\n      isSingleCurrency,\n      shopCurrency,\n      currentCurrency,\n      rate: window.Shopify?.currency?.rate,\n    };\n  }\n\n  /**\n   * Update the display based on current cart state\n   */\n  #updateDisplay() {\n    const cartTotal = parseInt(this.dataset.cartTotal);\n\n    // Validate cart total\n    if (isNaN(cartTotal) || cartTotal < 0) {\n      console.warn(\"FreeShippingGoal: Invalid cart total\", this.dataset.cartTotal);\n      return;\n    }\n\n    // Don't show if minimum amount is invalid\n    if (this.convertedAmountInCents <= 0) {\n      return;\n    }\n\n    const isQualified = cartTotal >= this.convertedAmountInCents;\n    this.#toggleStates(isQualified, cartTotal);\n  }\n\n  /**\n   * Get debug information for troubleshooting\n   * @returns {Object} Debug information\n   */\n  getDebugInfo() {\n    return {\n      cartTotal: parseInt(this.dataset.cartTotal),\n      convertedAmountInCents: this.convertedAmountInCents,\n      isQualified: parseInt(this.dataset.cartTotal) >= this.convertedAmountInCents,\n      remainingAmount: this.convertedAmountInCents - parseInt(this.dataset.cartTotal),\n      moneyFormat: window.FoxTheme?.moneyFormat || window.Shopify?.currency?.money_format,\n      conversionInfo: this.conversionInfo,\n    };\n  }\n\n  /**\n   * Update progress bar with current percentage\n   * @param {number} cartTotal - Current cart total in cents\n   */\n  #updateProgressBar(cartTotal) {\n    // Use cached element or find new one\n    const progressBarElement = this.#progressBarElement || this.querySelector(\"progress-bar\");\n    if (!progressBarElement) return;\n\n    const percent = Math.min((cartTotal / this.convertedAmountInCents) * 100, 100);\n\n    // Always use attribute update - safer and more reliable\n    progressBarElement.setAttribute(\"percent\", percent.toString());\n  }\n\n  /**\n   * Toggle between qualified and remaining states\n   * @param {boolean} isQualified - Whether customer qualifies for free shipping\n   * @param {number} cartTotal - Current cart total in cents\n   */\n  #toggleStates(isQualified, cartTotal) {\n    const congratulations = this.querySelector(\".free-shipping-goal__congratulations\");\n    const remaining = this.querySelector(\".free-shipping-goal__remaining\");\n\n    // Always update progress bar\n    this.#updateProgressBar(cartTotal);\n\n    if (isQualified) {\n      congratulations?.classList.remove(\"hidden\");\n      remaining?.classList.add(\"hidden\");\n      // Keep progress bar visible but at 100%\n    } else {\n      congratulations?.classList.add(\"hidden\");\n      remaining?.classList.remove(\"hidden\");\n\n      // Update remaining amount\n      const remainingAmount = this.convertedAmountInCents - cartTotal;\n      const remainingElement = this.querySelector(\".free-shipping-goal__remaining-amount\");\n\n      if (remainingElement && remainingAmount > 0) {\n        const moneyFormat = window.FoxTheme?.moneyFormat || window.Shopify?.currency?.money_format;\n        remainingElement.textContent = formatCurrency(remainingAmount, moneyFormat);\n      }\n    }\n  }\n}\n\n// Register components\nif (!customElements.get(\"progress-bar\")) {\n  customElements.define(\"progress-bar\", ProgressBar);\n}\nif (!customElements.get(\"free-shipping-goal\")) {\n  customElements.define(\"free-shipping-goal\", FreeShippingGoal);\n}\n"],
  "mappings": "AAAA,OAAS,cAAiB,mBAC1B,OAAS,gBAAmB,gBAC5B,OAAS,mBAAsB,mBAQ/B,MAAM,oBAAoB,SAAU,CAClC,WAAW,oBAAqB,CAC9B,MAAO,CAAC,UAAW,OAAO,CAC5B,CAEA,SAAW,GAEX,mBAAoB,CAClB,MAAM,kBAAkB,EACxB,KAAK,eAAe,EACpB,KAAK,SAAW,GAGhB,KAAK,cACH,IAAI,YAAY,qBAAsB,CACpC,QAAS,GACT,OAAQ,CAAE,QAAS,IAAK,CAC1B,CAAC,CACH,CACF,CAEA,0BAA2B,CACrB,KAAK,UACP,KAAK,eAAe,CAExB,CAKA,gBAAiB,CACf,MAAM,QAAU,KAAK,aAAa,SAAS,GAAK,IAC1C,MAAQ,KAAK,aAAa,OAAO,GAAK,GAGtC,KAAO,KAAK,cAAc,qBAAqB,EACrD,GAAI,KAAM,CACR,MAAM,aAAe,KAAK,IAAI,KAAK,IAAI,WAAW,OAAO,EAAG,CAAC,EAAG,GAAG,EACnE,KAAK,MAAM,YAAY,YAAa,GAAG,YAAY,GAAG,CACxD,CAGA,MAAM,aAAe,KAAK,cAAc,sBAAsB,EAC1D,cAAgB,QAClB,aAAa,YAAc,MAE/B,CAOA,OAAO,QAAS,MAAQ,GAAI,CAC1B,KAAK,aAAa,UAAW,QAAQ,SAAS,CAAC,EAC3C,OACF,KAAK,aAAa,QAAS,KAAK,CAEpC,CACF,CAUO,aAAM,yBAAyB,SAAU,CAC9C,oBAAsB,KAEtB,mBAAoB,CAClB,MAAM,kBAAkB,EAGxB,SAAS,iBAAiB,YAAY,WAAY,KAAK,iBAAiB,EACxE,SAAS,iBAAiB,qBAAsB,KAAK,uBAAuB,EAG5E,KAAK,YAAY,CACnB,CAEA,sBAAuB,CACrB,MAAM,qBAAqB,EAC3B,SAAS,oBAAoB,YAAY,WAAY,KAAK,iBAAiB,EAC3E,SAAS,oBAAoB,qBAAsB,KAAK,uBAAuB,CACjF,CAMA,wBAA2B,OAAU,CAE/B,MAAM,OAAO,QAAQ,QAAQ,oBAAoB,IAAM,OACzD,KAAK,oBAAsB,MAAM,OAAO,QAE5C,EAMA,kBAAqB,OAAU,CAC7B,KAAM,CAAE,IAAK,EAAI,MAAM,OAGnB,KAAK,WACP,KAAK,0BAA0B,IAAI,EAGnC,KAAK,yBAAyB,EAG9B,KAAK,eAAe,EAExB,EAKA,aAAc,CACZ,KAAK,yBAAyB,EAG9B,KAAK,oBAAsB,KAAK,cAAc,cAAc,EAE5D,sBAAsB,IAAM,CAC1B,KAAK,eAAe,CACtB,CAAC,CACH,CAMA,0BAA0B,SAAU,CAClC,GAAI,CAAC,SAAS,SAAU,OAExB,MAAM,UAAY,KAAK,QAAQ,mBAAmB,GAAG,QAAQ,UAC7D,GAAI,CAAC,WAAa,CAAC,SAAS,SAAS,SAAS,EAAG,OAMjD,MAAM,oBAJiB,IAAI,UAAU,EAAE,gBACrC,SAAS,SAAS,SAAS,EAC3B,WACF,EAC2C,cAAc,oBAAoB,EAE7E,GAAI,CAAC,oBAAqB,OAG1B,MAAM,QAAU,CACd,UAAW,oBAAoB,QAAQ,UACvC,gBAAiB,oBAAoB,QAAQ,gBAC7C,cAAe,oBAAoB,QAAQ,cAC3C,iBAAkB,oBAAoB,QAAQ,iBAC9C,aAAc,oBAAoB,QAAQ,YAC5C,EAGA,IAAI,WAAa,GACjB,SAAW,CAAC,IAAK,KAAK,IAAK,OAAO,QAAQ,OAAO,EAC3C,KAAK,QAAQ,GAAG,IAAM,QACxB,KAAK,QAAQ,GAAG,EAAI,MACpB,WAAa,GAKnB,CAKA,0BAA2B,CACzB,MAAM,cAAgB,WAAW,KAAK,QAAQ,aAAa,EACrD,iBAAmB,KAAK,QAAQ,mBAAqB,OACrD,aAAe,KAAK,QAAQ,aAC5B,gBAAkB,KAAK,QAAQ,gBAGrC,GAAI,MAAM,aAAa,GAAK,eAAiB,EAAG,CAC9C,QAAQ,KAAK,2CAA4C,KAAK,QAAQ,aAAa,EACnF,KAAK,uBAAyB,EAC9B,MACF,CAGA,MAAM,SAAW,GAAG,aAAa,IAAI,eAAe,IAAI,OAAO,SAAS,UAAU,MAAQ,CAAC,GAE3F,GAAI,KAAK,iBAAiB,MAAQ,SAKlC,IAAI,kBAAoB,kBAAoB,aAE1C,GAAI,OAAO,SAAS,UAAU,KAAM,CAClC,MAAM,KAAO,WAAW,OAAO,QAAQ,SAAS,IAAI,EAChD,CAAC,MAAM,IAAI,GAAK,KAAO,GACzB,KAAK,gBAAkB,cAAgB,KACvC,KAAK,uBAAyB,KAAK,MAAM,KAAK,gBAAkB,GAAG,IAEnE,QAAQ,KAAK,0CAA2C,OAAO,QAAQ,SAAS,IAAI,EACpF,KAAK,uBAAyB,KAAK,MAAM,cAAgB,GAAG,EAEhE,MAEE,KAAK,uBAAyB,KAAK,MAAM,cAAgB,GAAG,OAI9D,KAAK,uBAAyB,KAAK,MAAM,cAAgB,GAAG,EAI9D,KAAK,gBAAkB,CACrB,IAAK,SACL,uBAAwB,KAAK,uBAC7B,cACA,iBACA,aACA,gBACA,KAAM,OAAO,SAAS,UAAU,IAClC,EACF,CAKA,gBAAiB,CACf,MAAM,UAAY,SAAS,KAAK,QAAQ,SAAS,EAGjD,GAAI,MAAM,SAAS,GAAK,UAAY,EAAG,CACrC,QAAQ,KAAK,uCAAwC,KAAK,QAAQ,SAAS,EAC3E,MACF,CAGA,GAAI,KAAK,wBAA0B,EACjC,OAGF,MAAM,YAAc,WAAa,KAAK,uBACtC,KAAK,cAAc,YAAa,SAAS,CAC3C,CAMA,cAAe,CACb,MAAO,CACL,UAAW,SAAS,KAAK,QAAQ,SAAS,EAC1C,uBAAwB,KAAK,uBAC7B,YAAa,SAAS,KAAK,QAAQ,SAAS,GAAK,KAAK,uBACtD,gBAAiB,KAAK,uBAAyB,SAAS,KAAK,QAAQ,SAAS,EAC9E,YAAa,OAAO,UAAU,aAAe,OAAO,SAAS,UAAU,aACvE,eAAgB,KAAK,cACvB,CACF,CAMA,mBAAmB,UAAW,CAE5B,MAAM,mBAAqB,KAAK,qBAAuB,KAAK,cAAc,cAAc,EACxF,GAAI,CAAC,mBAAoB,OAEzB,MAAM,QAAU,KAAK,IAAK,UAAY,KAAK,uBAA0B,IAAK,GAAG,EAG7E,mBAAmB,aAAa,UAAW,QAAQ,SAAS,CAAC,CAC/D,CAOA,cAAc,YAAa,UAAW,CACpC,MAAM,gBAAkB,KAAK,cAAc,sCAAsC,EAC3E,UAAY,KAAK,cAAc,gCAAgC,EAKrE,GAFA,KAAK,mBAAmB,SAAS,EAE7B,YACF,iBAAiB,UAAU,OAAO,QAAQ,EAC1C,WAAW,UAAU,IAAI,QAAQ,MAE5B,CACL,iBAAiB,UAAU,IAAI,QAAQ,EACvC,WAAW,UAAU,OAAO,QAAQ,EAGpC,MAAM,gBAAkB,KAAK,uBAAyB,UAChD,iBAAmB,KAAK,cAAc,uCAAuC,EAEnF,GAAI,kBAAoB,gBAAkB,EAAG,CAC3C,MAAM,YAAc,OAAO,UAAU,aAAe,OAAO,SAAS,UAAU,aAC9E,iBAAiB,YAAc,eAAe,gBAAiB,WAAW,CAC5E,CACF,CACF,CACF,CAGK,eAAe,IAAI,cAAc,GACpC,eAAe,OAAO,eAAgB,WAAW,EAE9C,eAAe,IAAI,oBAAoB,GAC1C,eAAe,OAAO,qBAAsB,gBAAgB",
  "names": []
}
