{"version":3,"file":"product-discount-message-section.min.js","mappings":"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","sources":["webpack://lange/./sections/product-discount-message/product-discount-message/index.ts","webpack://lange/./sections/product-discount-message/product-discount-modal/index.ts","webpack://lange/./sections/product-discount-message/index.ts","webpack://lange/./lib/dom-ready.ts"],"sourcesContent":["import type { CustomElement } from '@integrabeauty/custom-elements';\nimport styles from './index.scss';\n\n/**\n * For the standard PDP we want to add messaging just like on product cards that shows that they\n * will get a discount. The discount messaging is based on a product's metafield values. The\n * namespace used to populate this data is \"discount\" with key \"code\" representing the discount code\n * and key \"percentage\" representing discount percentage.\n *\n * The element will use these values if present to render the discount message.\n *\n * Because we have not yet converted the product details pages over to using an element to render,\n * we have to have constituent elements assembled together\n */\nclass ProductDiscountMessage extends HTMLElement implements CustomElement {\n  public static get observedAttributes() {\n    return [\n      'data-price',\n      'data-compare-at-price',\n      'data-currency-symbol',\n      'data-discount-code',\n      'data-discount-percentage',\n      'data-message-override',\n      'data-hide-saving-badge'\n    ];\n  }\n\n  declare readonly dataset: {\n    /**\n     * The value to render in the subunit of the currency. Because we mostly work with USD, this is\n     * referred to as cents.\n     */\n    compareAtPrice: string;\n\n    /**\n     * Currency ISO Code\n     */\n    currencyCode: string;\n\n    /**\n     * The symbol of the currency.\n     *\n     * @example \"$\"\n     */\n    currencySymbol: string;\n\n    /**\n     * The discount code to apply for discount\n     *\n     * @example \"USD\"\n     */\n    discountCode: string;\n\n    /**\n     * The discount percentage applied to product for discount code\n     *\n     * Floating point string.\n     */\n    discountPercentage: string;\n\n    /**\n     * Boolean as a string. When \"true\", the new design's message renders without its savings\n     * percentage badge, for where the badge is shown somewhere else.\n     */\n    hideSavingBadge: 'false' | 'true';\n\n    /**\n     * A message override to be displayed instead of standardized percentage discount\n     *\n     * @example \"USD\"\n     */\n    messageOverride: string;\n\n    /**\n     * The value to render in the subunit of the currency. Because we mostly work with USD, this is\n     * referred to as cents.\n     */\n    price: string;\n\n    /**\n     * A boolean flag to determine if the new discount message design should be used. If true, the\n     * new design will be used, otherwise the old design will be used.\n     */\n    useNewDesign: 'false' | 'true';\n  };\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n\n    this.render();\n  }\n\n  public attributeChangedCallback(_name: string, oldValue: string, newValue: string) {\n    this.initializeShadowRoot();\n\n    if (this.isConnected && oldValue !== newValue) {\n      this.render();\n    }\n  }\n\n  private initializeShadowRoot() {\n    if (!this.shadowRoot) {\n      this.attachShadow({ mode: 'open' });\n      this.shadowRoot.innerHTML = `<style>${styles}</style>`;\n    }\n  }\n\n  private render() {\n    if (this.dataset.messageOverride) {\n      this.shadowRoot.innerHTML = `<style>${styles}</style>${this.dataset.messageOverride}`;\n      return;\n    }\n\n    // We do need to apply the additional condition of discount percentage not equaling 0 to account\n    // for offer-1 overrides using just the final sale price only. This is because we are testing\n    // against a dataset value which will always be a string, and negating a value of '0' will not\n    // produce a false truthy as negating the numerical value 0.\n\n    if (!this.dataset.discountCode || !this.dataset.discountPercentage) {\n      return;\n    }\n\n    const price = parseInt(this.dataset.price, 10);\n\n    let compareAtPrice = parseInt(this.dataset.compareAtPrice, 10);\n    if (!Number.isInteger(compareAtPrice)) {\n      compareAtPrice = price;\n    }\n\n    const discountPercentage = parseFloat(this.dataset.discountPercentage);\n    const discountPrice = Math.round(price - (price * (discountPercentage / 100)));\n    const savingsPercentage = 100 - Math.round(discountPrice / compareAtPrice * 100);\n\n    // Support CSV discount code string. We only want to display the first code if there are many\n    // codes present. This assumes the code is well-formed.\n    const codes = this.dataset.discountCode.split(',');\n    // This extra conditional is just paranoia, might not be needed\n    const displayCode = codes.length === 0 || codes[0].length === 0 ?\n      this.dataset.discountCode :\n      codes[0];\n\n    let badge = `<span class=\"badge\">${savingsPercentage}% OFF</span>`;\n    if (this.dataset.hideSavingBadge === 'true') {\n      badge = '';\n    }\n\n    let text = '';\n    if (this.dataset.useNewDesign === 'true') {\n      text = `<div><span class=\"price\"><price-element\n        data-currency-code=\"${this.dataset.currencyCode}\"\n        data-currency-symbol=\"${this.dataset.currencySymbol}\"\n        data-subunits=\"${discountPrice}\"></price-element></span> use code \n        <span class=\"code\">${displayCode}</span>\n        ${badge}</div>`;\n    } else {\n      text = `Get it <span class=\"nowrap\">for <price-element\n        data-currency-code=\"${this.dataset.currencyCode}\"\n        data-currency-symbol=\"${this.dataset.currencySymbol}\"\n        data-subunits=\"${discountPrice}\"></price-element> w/</span>\n        code <span class=\"code\">${displayCode}</span>\n        (${savingsPercentage}% savings!)`;\n    }\n\n    this.shadowRoot.innerHTML = `<style>${styles}</style>${text}`;\n  }\n}\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'product-discount-message': ProductDiscountMessage;\n  }\n}\n\nif (!customElements.get('product-discount-message')) {\n  customElements.define('product-discount-message', ProductDiscountMessage);\n}\n","import type { CustomElement } from '@integrabeauty/custom-elements';\nimport html from './index.html';\nimport styles from './index.scss';\n\ntype DiscountModalShowRequestedEvent = CustomEvent<{\n  discountCode: string;\n  message: string;\n}>;\n\n/**\n * A clickable discount message on the product detail page, showing the discounted price and the\n * code that unlocks it.\n *\n * Despite its name it does not render the modal itself. When clicked it dispatches an event asking\n * the surrounding section to open a separate explanatory modal, passing the code and message along.\n */\nclass ProductDiscountModal extends HTMLElement implements CustomElement {\n  public static get observedAttributes() {\n    return [\n      'data-price',\n      'data-currency-symbol',\n      'data-discount-code',\n      'data-discount-percentage',\n      'data-discount-override'\n    ];\n  }\n\n  declare readonly dataset: {\n    /**\n     * Currency ISO Code\n     */\n    currencyCode: string;\n\n    /**\n     * The symbol of the currency.\n     *\n     * @example \"$\"\n     */\n    currencySymbol: string;\n\n    /**\n     * The discount code to apply for discount\n     *\n     * @example \"USD\"\n     */\n    discountCode: string;\n\n    /**\n     * Optional discount message override which replaces text of the discount message CTA\n     */\n    discountOverride: string;\n\n    /**\n     * The discount percentage applied to product for discount code\n     *\n     * Floating point string.\n     */\n    discountPercentage: string;\n\n    /**\n     * A message to be displayed as the content of the modal window\n     */\n    message: string;\n\n    /**\n     * The value to render in the subunit of the currency. Because we mostly work with USD, this is\n     * referred to as cents.\n     */\n    price: string;\n  };\n\n  private declare discountPrice: number;\n  private declare displayCode: string;\n  private declare displayDiscount: string;\n\n  private onButtonClickedBound = this.onButtonClicked.bind(this);\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n\n    const button = this.shadowRoot.querySelector('button');\n    button.addEventListener('click', this.onButtonClickedBound);\n\n    this.render();\n  }\n\n  public disconnectedCallback() {\n    const button = this.shadowRoot.querySelector('button');\n    button?.removeEventListener('click', this.onButtonClickedBound);\n  }\n\n  public attributeChangedCallback(_name: string, oldValue: string, newValue: string) {\n    this.initializeShadowRoot();\n\n    if (!this.isConnected || oldValue === newValue) {\n      return;\n    }\n\n    if (!this.dataset.discountCode || !this.dataset.discountPercentage) {\n      return;\n    }\n\n    const price = parseInt(this.dataset.price, 10);\n\n    const discountPercentage = parseFloat(this.dataset.discountPercentage);\n    this.displayDiscount = `${Math.round(discountPercentage).toString()}% off`;\n    this.discountPrice = Math.round(price - (price * (discountPercentage / 100)));\n\n    // Support CSV discount code string. We only want to display the first code if there are many\n    // codes present. This assumes the code is well-formed.\n    const codes = this.dataset.discountCode.split(',');\n    // This extra conditional is just paranoia, might not be needed\n    this.displayCode = codes.length === 0 || codes[0].length === 0 ?\n      this.dataset.discountCode :\n      codes[0];\n\n    this.render();\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 message = this.shadowRoot.querySelector('.message');\n    message.innerHTML = '';\n\n    if (this.dataset.discountOverride) {\n      message.textContent = this.dataset.discountOverride;\n    } else {\n      const price = document.createElement('price-element');\n      price.dataset.currencyCode = this.dataset.currencyCode;\n      price.dataset.currencySymbol = this.dataset.currencySymbol;\n      price.dataset.subunits = this.discountPrice.toString();\n\n      const code = document.createElement('span');\n      code.classList.add('code');\n      code.textContent = this.displayCode;\n\n      const discountPercentage = document.createElement('span');\n      discountPercentage.classList.add('discount');\n      discountPercentage.textContent = this.displayDiscount;\n\n      const text = document.createElement('span');\n      text.classList.add('nowrap');\n      text.textContent = 'w/ code';\n\n      message.append(price);\n      message.append(text);\n      message.append(code);\n      message.append(discountPercentage);\n    }\n\n    if ((this.discountPrice || this.dataset.discountOverride) && this.displayCode) {\n      const component = this.shadowRoot.querySelector<HTMLElement>('.discount-message');\n      component.hidden = false;\n    }\n  }\n\n  private onButtonClicked(_event: MouseEvent) {\n    this.showModal();\n  }\n\n  private getMessage() {\n    if (this.dataset.message) {\n      return this.dataset.message;\n    }\n\n    // return a default message if no message is provided\n    return '<p>Discounts are <strong>automatically</strong> applied at checkout after you click ' +\n      '<strong>\"Add to Bag.\"</strong></p>';\n  }\n\n  private showModal() {\n    type Detail = WindowEventMap['discount-modal-show-requested']['detail'];\n    const modalShowEvent = new CustomEvent<Detail>('discount-modal-show-requested', {\n      bubbles: true,\n      composed: true,\n      detail: {\n        discountCode: this.displayCode,\n        message: this.getMessage()\n      }\n    });\n    this.dispatchEvent(modalShowEvent);\n  }\n}\n\ndeclare global {\n  interface GlobalEventHandlersEventMap {\n    'discount-modal-show-requested': DiscountModalShowRequestedEvent;\n  }\n\n  interface HTMLElementTagNameMap {\n    'product-discount-modal': ProductDiscountModal;\n  }\n}\n\nif (!customElements.get('product-discount-modal')) {\n  customElements.define('product-discount-modal', ProductDiscountModal);\n}\n","import './product-discount-message/index.js';\nimport './product-discount-modal/index.js';\nimport { ready } from '../../lib/dom-ready.js';\n\ntype ModalElement = HTMLElementTagNameMap['modal-element'];\n\nfunction onDiscountModalShowRequested(event: WindowEventMap['discount-modal-show-requested']) {\n  // The modal is normally guaranteed to be on the page, but it can be removed out from under us by\n  // client-side DOM manipulation such as a content blocker, so bail instead of dereferencing null.\n  const modal = document.querySelector<ModalElement>('#discount-modal');\n  if (modal) {\n    modal.innerHTML = event.detail.message;\n    modal.dataset.open = 'true';\n  }\n}\n\nfunction onReady() {\n  addEventListener('discount-modal-show-requested', onDiscountModalShowRequested);\n}\n\nready(onReady);\n","/**\n * Releases the handle returned from the ready() function.\n *\n * This is idempotent. Additional calls are pointless but harmless.\n */\nexport type ReleaseHandle = () => void;\n\n/**\n * A subset of events that pertain to document ready state.\n */\ntype LoadingEventMap = Pick<DocumentEventMap, 'DOMContentLoaded' | 'load'>;\n\n/**\n * Calls the provided callback when the document is ready. This works both when the document is\n * already ready and when it is not, so callers do not have to care. This exists because it ensures\n * that each caller consistently expresses its dependency on document readiness, and to save a bit\n * of boilerplate.\n *\n * A document is considered ready when it has been parsed. This corresponds to the ready state of\n * 'interactive'. When the document has been fully loaded, the ready state is 'complete'. The\n * callback runs either way, so callers do not have to check which one they are in.\n *\n * Ready means the html is parsed and the DOM is there to query. It does not mean the page's other\n * scripts have run. Deferred scripts and module scripts run after the ready state turns\n * 'interactive' and before DOMContentLoaded fires, so a callback here can run before them. When a\n * caller needs some other script to have run already, this is the wrong tool, and it should wait on\n * whatever that script produces instead.\n *\n * That remedy has a catch of its own when the calling bundle is loaded with async. An async bundle\n * can execute after DOMContentLoaded has already fired, so a plain listener for that event never\n * runs and the work silently never happens. Nothing here can detect that, because by the time this\n * file executes the event is long gone. Catching it needs something registered at parse time,\n * before any bundle loads.\n *\n * There is a second trap right next to that one, and it applies only when waiting for\n * DOMContentLoaded. The ready state stays 'interactive' after DOMContentLoaded has fired, all the\n * way until 'complete', so the ready state cannot tell you whether DOMContentLoaded is still coming\n * or has already gone. Waiting for load is not exposed to this, because the ready state turns\n * 'complete' and load fires in the same task, so seeing 'complete' means load has fired too.\n *\n * When the document is already ready, the callback is invoked via setTimeout. This way, if the\n * callback throws, the error surfaces to the global error handler in a way that is similar to what\n * happens when an event listener throws.\n *\n * We might one day want to move this to an NPM library.\n *\n * Beware of a quirk here. setTimeout yields, and the busier the frontend, the longer it takes to\n * fire. Page load is exactly when the frontend is busiest. The timeout is a floor and not a\n * guarantee, so there is no real bound on how long that yield lasts. What this means is that using\n * this library can introduce more delay than you might expect. Compare code that runs on async load\n * and checks ready state and invokes immediately to code that runs after setTimeout. The former\n * runs in the same tick, the latter runs in \"some later tick\". That later tick can be surprisingly\n * much later. That can cause initial paint issues.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event\n * @see https://html.spec.whatwg.org/multipage/parsing.html#the-end\n */\nexport function ready(callback: (event?: Event) => void,\n  type: keyof LoadingEventMap = 'DOMContentLoaded') {\n  if (document.readyState === 'complete' || (type === 'DOMContentLoaded' &&\n    document.readyState === 'interactive')) {\n    const timer = setTimeout(callback, 0);\n    const handle: ReleaseHandle = () => clearTimeout(timer);\n    return handle;\n  } else {\n    addEventListener(type, callback);\n    const handle: ReleaseHandle = () => removeEventListener(type, callback);\n    return handle;\n  }\n}\n"],"names":["ProductDiscountMessage","HTMLElement","observedAttributes","connectedCallback","this","initializeShadowRoot","render","attributeChangedCallback","_name","oldValue","newValue","isConnected","shadowRoot","attachShadow","mode","innerHTML","product_discount_message_namespaceObject","dataset","messageOverride","discountCode","discountPercentage","price","parseInt","compareAtPrice","Number","isInteger","parseFloat","discountPrice","Math","round","savingsPercentage","codes","split","displayCode","length","badge","hideSavingBadge","text","useNewDesign","currencyCode","currencySymbol","customElements","get","define","ProductDiscountModal","onButtonClickedBound","onButtonClicked","bind","querySelector","addEventListener","disconnectedCallback","button","removeEventListener","displayDiscount","toString","message","discountOverride","textContent","document","createElement","subunits","code","classList","add","append","hidden","_event","showModal","getMessage","modalShowEvent","CustomEvent","bubbles","composed","detail","dispatchEvent","onDiscountModalShowRequested","event","modal","open","callback","type","readyState","timer","setTimeout","clearTimeout","ready"],"sourceRoot":""}