{
  "version": 3,
  "sources": ["/cdn/shop/t/14/assets/product-recommendations.js?ab=3"],
  "sourcesContent": ["// Buiten de class: caches die het vervangen van het element overleven.\n// De cart drawer wordt bij elke cart-wijziging server-side herrenderd met\n// placeholder-kaarten; zonder deze caches flitst de upsell daardoor telkens\n// kort weg terwijl de aanbevelingen opnieuw geladen worden.\nconst recommendationsResponseCache = {};\nconst recommendationsLastRendered = {};\n\nclass ProductRecommendations extends HTMLElement {\n  /**\n   * The observer for the product recommendations\n   * @type {IntersectionObserver}\n   */\n  #intersectionObserver = new IntersectionObserver(\n    (entries, observer) => {\n      if (!entries[0]?.isIntersecting) return;\n\n      observer.disconnect();\n      this.#loadRecommendations();\n    },\n    { rootMargin: \"0px 0px 400px 0px\" }\n  );\n\n  /**\n   * Observing changes to the elements attributes\n   * @type {MutationObserver}\n   */\n  #mutationObserver = new MutationObserver((mutations) => {\n    for (const mutation of mutations) {\n      // Only attribute changes are interesting\n      if (mutation.target !== this || mutation.type !== \"attributes\") continue;\n\n      // Ignore error attribute changes\n      if (mutation.attributeName === \"data-error\") continue;\n\n      // Ignore addition of hidden class because it means there's an error with the display\n      if (mutation.attributeName === \"class\" && this.classList.contains(\"hidden\")) continue;\n\n      // Ignore when the data-recommendations-performed attribute has been set to 'true'\n      if (\n        mutation.attributeName === \"data-recommendations-performed\" &&\n        this.dataset.recommendationsPerformed === \"true\"\n      )\n        continue;\n\n      // All other attribute changes trigger a reload\n      this.#loadRecommendations();\n      break;\n    }\n  });\n\n  /**\n   * An abort controller for the active fetch (if there is one)\n   * @type {AbortController | null}\n   */\n  #activeFetch = null;\n\n  connectedCallback() {\n    // Synchroon, v\u00F3\u00F3r de eerste paint: als dit element net (opnieuw) in de\n    // DOM is gezet door een drawer-herrender, staan er server-placeholders\n    // in. De laadroute via de IntersectionObserver komt pas n\u00E1 de paint,\n    // waardoor die placeholders 1-2 frames zichtbaar flitsen. Daarom hier\n    // direct de laatst geladen kaarten terugzetten.\n    const previousHtml = recommendationsLastRendered[this.id];\n    if (\n      previousHtml &&\n      this.dataset.recommendationsPerformed !== \"true\" &&\n      this.dataset.useManualProducts !== \"true\" &&\n      !Shopify.designMode\n    ) {\n      this.innerHTML = previousHtml;\n      if (this.classList.contains(\"hidden\")) this.classList.remove(\"hidden\");\n    }\n    this.#intersectionObserver.observe(this);\n    this.#mutationObserver.observe(this, { attributes: true });\n  }\n\n  /**\n   * Load the product recommendations\n   */\n  #loadRecommendations() {\n    const { productId, recommendationsPerformed, sectionId, intent, useManualProducts } =\n      this.dataset;\n    const id = this.id;\n\n    if (!productId || !id) {\n      throw new Error(\"Product ID and an ID attribute are required\");\n    }\n\n    // If manual products are being used, skip API fetch\n    if (useManualProducts === \"true\") {\n      return;\n    }\n\n    // If the recommendations have already been loaded, accounts for the case where the Theme Editor\n    // is loaded the section from the editor's visual preview context.\n    if (recommendationsPerformed === \"true\") {\n      return;\n    }\n\n    // Toon direct de laatst geladen kaarten (van v\u00F3\u00F3r de drawer-herrender)\n    // in plaats van de server-placeholders; een eventuele nieuwe lading\n    // vervangt ze hieronder stil. innerHTML herhalen is veilig (de observer\n    // hierboven kijkt alleen naar attributen), maar de hidden-class alleen\n    // verwijderen als hij er echt op staat: classList.remove zet het\n    // class-attribuut ook zonder wijziging opnieuw en dat zou de observer\n    // in een oneindige herlaad-lus brengen.\n    const previousHtml = recommendationsLastRendered[id];\n    if (previousHtml && !Shopify.designMode && this.innerHTML !== previousHtml) {\n      this.innerHTML = previousHtml;\n      if (this.classList.contains(\"hidden\")) this.classList.remove(\"hidden\");\n    }\n\n    this.#fetchCachedRecommendations(productId, sectionId, intent)\n      .then((result) => {\n        if (!result.success) {\n          // The Theme Editor will place a section element element in the DOM whose section_id is not available\n          // to the Section Renderer API. In this case, we can safely ignore the error.\n          if (!Shopify.designMode) {\n            this.#handleError(new Error(`Server returned ${result.status}`));\n          }\n          return;\n        }\n\n        const html = document.createElement(\"div\");\n        html.innerHTML = result.data || \"\";\n        const recommendations = html.querySelector(`product-recommendations[id=\"${id}\"]`);\n\n        if (recommendations?.innerHTML && recommendations.innerHTML.trim().length) {\n          this.dataset.recommendationsPerformed = \"true\";\n          this.innerHTML = recommendations.innerHTML;\n          recommendationsLastRendered[id] = recommendations.innerHTML;\n          // herstel na een eerdere (tijdelijke) fout, anders blijft de\n          // upsell verborgen terwijl de nieuwe lading wel gelukt is\n          // (conditioneel: een kale classList.remove muteert het attribuut\n          // ook zonder wijziging en zou de observer opnieuw laten vuren)\n          if (this.classList.contains(\"hidden\")) this.classList.remove(\"hidden\");\n          if (this.dataset.error) delete this.dataset.error;\n        } else {\n          this.#handleError(new Error(\"No recommendations available\"));\n        }\n      })\n      .catch((e) => {\n        // een door een nieuwere load afgebroken fetch is geen fout; de\n        // nieuwe load neemt het over (dit verborg de upsell in de cart\n        // drawer wanneer twee cart-updates elkaar snel opvolgden)\n        if (e && e.name === \"AbortError\") return;\n        this.#handleError(e);\n      });\n  }\n\n  /**\n   * Fetches the recommendations and cached the result for future use\n   * @param {string} productId\n   * @param {string | undefined} sectionId\n   * @param {string | undefined} intent\n   * @returns {Promise<{ success: true, data: string } | { success: false, status: number }>}\n   */\n  async #fetchCachedRecommendations(productId, sectionId, intent) {\n    const url = `${this.dataset.url}&product_id=${productId}&section_id=${sectionId}&intent=${intent}`;\n\n    // Skip cache in design mode to always show fresh content when merchant edits settings\n    // (module-cache i.p.v. instance-cache: de drawer vervangt dit element bij\n    // elke cart-wijziging, waardoor een instance-cache nooit iets raakte)\n    if (!Shopify.designMode) {\n      const cachedResponse = recommendationsResponseCache[url];\n      if (cachedResponse) {\n        return { success: true, data: cachedResponse };\n      }\n    }\n\n    this.#activeFetch?.abort();\n    this.#activeFetch = new AbortController();\n\n    try {\n      const response = await fetch(url, { signal: this.#activeFetch.signal });\n      if (!response.ok) {\n        return { success: false, status: response.status };\n      }\n\n      const text = await response.text();\n      // Only cache if not in design mode\n      if (!Shopify.designMode) {\n        recommendationsResponseCache[url] = text;\n      }\n      return { success: true, data: text };\n    } finally {\n      this.#activeFetch = null;\n    }\n  }\n\n  /**\n   * Handle errors in a consistent way\n   * @param {Error} error\n   */\n  #handleError(error) {\n    console.error(\"Product recommendations error:\", error.message);\n    this.classList.add(\"hidden\");\n    this.dataset.error = \"Error loading product recommendations\";\n  }\n}\n\nif (!customElements.get(\"product-recommendations\")) {\n  customElements.define(\"product-recommendations\", ProductRecommendations);\n}\n"],
  "mappings": "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",
  "names": []
}
