{"version":3,"file":"notify-me-modal-section.min.js","mappings":"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","sources":["webpack://lange/../node_modules/@integrabeauty/assert/src/index.ts","webpack://lange/../node_modules/@integrabeauty/person/dist/src/email.js","webpack://lange/./sections/notify-me-modal/notify-me-form/index.ts"],"sourcesContent":["/**\n * Throws an assertion error if the given condition is falsy.\n *\n * @param expression any expression, although one that evaluates to true or false is preferred by\n * convention\n * @param message optional error text\n */\nexport function assert(expression: unknown, message?: string) {\n  if (!expression) {\n    throw new AssertionError(message);\n  }\n}\n\nexport class AssertionError extends Error {\n  constructor(message = 'Assertion error') {\n    super(message);\n    this.name = this.constructor.name;\n  }\n}\n","/**\n * Validates an email address.\n *\n * Based on the regex used by Meta's pixel.\n */\nexport function isValidEmailAddress(email) {\n    // eslint-disable-next-line @stylistic/max-len\n    return /^[\\w!#\\$%&\\'\\*\\+\\/\\=\\?\\^`\\{\\|\\}~\\-]+(:?\\.[\\w!#\\$%&\\'\\*\\+\\/\\=\\?\\^`\\{\\|\\}~\\-]+)*@(?:[a-z0-9](?:[a-z0-9\\-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9\\-]*[a-z0-9])?$/i.test(email);\n}\n","import { assert } from '@integrabeauty/assert';\nimport type { ResponseBody, Attributes } from '@integrabeauty/back-in-stock-types';\nimport type { CustomElement } from '@integrabeauty/custom-elements';\nimport { isValidEmailAddress } from '@integrabeauty/person';\nimport html from './index.html';\nimport styles from './index.scss';\n\n/**\n * The back in stock signup form shown in the notify me modal when a visitor clicks the \"Notify Me\"\n * button for an out-of-stock product. It prompts for an email address, and on submit it subscribes\n * the visitor to hear when the product is available again. It has two views, the form and a\n * confirmation, and it sets the modal's title to match whichever one is showing.\n *\n * The form sits inside a modal-element, which handles the box, the close button, the dismiss\n * gestures, and telling the modal manager when it opens and closes.\n *\n * The back in stock doc alongside this file covers what the Back in Stock app does and doesn't\n * offer.\n */\nclass NotifyMeForm extends HTMLElement implements CustomElement {\n  declare readonly dataset: {\n    /**\n     * The ISO code of the currency. All uppercase.\n     *\n     * @example \"USD\"\n     */\n    currencyCode: string;\n  };\n\n  private declare email: string;\n  private declare productId: number;\n  private declare productTitle: string;\n  private declare productUrl: string;\n  private declare sku: string;\n  private declare variantId: number;\n  private declare variantTitle: string;\n  private declare debounceTimeout: ReturnType<typeof setTimeout>;\n  private onModalDisplayClickBound = this.onModalDisplayClick.bind(this);\n  private onFormSubmitBound = this.onFormSubmit.bind(this);\n  private onEmailInputBound = this.onEmailInput.bind(this);\n  private onEmailBlurBound = this.onEmailBlur.bind(this);\n  private validateEmailBound = this.validateEmail.bind(this);\n  private onContinueButtonClickBound = this.onContinueButtonClick.bind(this);\n  private onClearButtonClickBound = this.onClearButtonClick.bind(this);\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n\n    const emailInput = this.shadowRoot.querySelector<HTMLElement>('.email input');\n    emailInput.addEventListener('input', this.onEmailInputBound);\n    emailInput.addEventListener('blur', this.onEmailBlurBound);\n\n    const form = this.shadowRoot.querySelector<HTMLFormElement>('.before-submit form');\n    form.addEventListener('submit', this.onFormSubmitBound);\n\n    const clearButton = this.shadowRoot.querySelector<HTMLElement>('.clear');\n    clearButton.addEventListener('click', this.onClearButtonClickBound);\n\n    addEventListener('notify-me-clicked', this.onModalDisplayClickBound);\n  }\n\n  public disconnectedCallback() {\n    removeEventListener('notify-me-clicked', this.onModalDisplayClickBound);\n\n    const emailInput = this.shadowRoot.querySelector<HTMLInputElement>('.email input');\n    emailInput?.removeEventListener('input', this.onEmailInputBound);\n    emailInput?.removeEventListener('blur', this.onEmailBlurBound);\n\n    const form = this.shadowRoot.querySelector<HTMLFormElement>('.before-submit form');\n    form?.removeEventListener('submit', this.onFormSubmitBound);\n\n    const clearButton = this.shadowRoot.querySelector<HTMLElement>('.clear');\n    clearButton?.removeEventListener('click', this.onClearButtonClickBound);\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 onContinueButtonClick(_event: MouseEvent) {\n    const modal = this.closest('modal-element');\n    delete modal.dataset.open;\n  }\n\n  /**\n   * Runs email validation on input but not on every stroke\n   */\n  private onEmailInput(event: InputEvent) {\n    if (event.currentTarget instanceof HTMLInputElement) {\n      const validationError = this.shadowRoot.querySelector('.validation-error-message');\n      validationError.textContent = '';\n      event.currentTarget.classList.remove('error');\n\n      clearTimeout(this.debounceTimeout);\n      this.debounceTimeout = setTimeout(this.validateEmailBound, 500);\n    }\n  }\n\n  /**\n   * Validates the email when the input loses focus\n   */\n  private onEmailBlur(_event: FocusEvent) {\n    this.validateEmail();\n  }\n\n  /**\n   * Validates email and display error messages if necessary.\n   *\n   * This applies a stricter check than the browser's native email validation, which only blocks a\n   * missing at-sign and an empty field. Native validation accepts addresses with no top-level\n   * domain and malformed local parts, so this stricter check is not redundant. It also gives live\n   * inline feedback as the user types and on blur, which the native gate does not.\n   */\n  private validateEmail() {\n    // The blur listener and the debounced input timeout both call this, and the timeout fires\n    // without an event, so the input cannot be read from a currentTarget and is queried instead.\n    const emailInput = this.shadowRoot.querySelector<HTMLInputElement>('.email input');\n    const email = emailInput.value.trim();\n    const validationError = this.shadowRoot.querySelector('.validation-error-message');\n    const clearButton = this.shadowRoot.querySelector<HTMLElement>('.clear');\n    const checkIcon = this.shadowRoot.querySelector<HTMLElement>('.check');\n\n    // An empty field isn't an error yet, since the visitor may not have typed anything. The submit\n    // handler is what complains about an empty field, and an error it showed stays put until the\n    // visitor types.\n    if (email === '') {\n      clearButton.hidden = true;\n      checkIcon.hidden = true;\n    } else if (isValidEmailAddress(email)) {\n      validationError.textContent = '';\n      emailInput.classList.remove('error');\n      clearButton.hidden = true;\n      checkIcon.hidden = false;\n    } else {\n      validationError.textContent = 'Please enter valid email.';\n      emailInput.classList.add('error');\n      checkIcon.hidden = true;\n      clearButton.hidden = false;\n    }\n  }\n\n  /**\n   * Clears email input\n   */\n  private onClearButtonClick(_event?: MouseEvent) {\n    const emailInput = this.shadowRoot.querySelector<HTMLInputElement>('.email input');\n    emailInput.value = '';\n\n    const clearButton = this.shadowRoot.querySelector<HTMLElement>('.clear');\n    clearButton.hidden = true;\n  }\n\n  /**\n   * Fills in the form from the clicked button's product details, resets it to the first view, and\n   * opens the modal around it.\n   *\n   * The form opening its own parent modal is an interim step. Opening should move to the code that\n   * owns each Notify Me button, see issue #8715.\n   */\n  private onModalDisplayClick(event: WindowEventMap['notify-me-clicked']) {\n    this.reset();\n\n    this.email = event.detail.email;\n    this.productId = event.detail.productId;\n    this.productTitle = event.detail.productTitle;\n    this.productUrl = event.detail.productUrl;\n    this.sku = event.detail.sku;\n    this.variantId = event.detail.variantId;\n    this.variantTitle = event.detail.variantTitle;\n\n    this.renderFirstView();\n\n    const modal = this.closest('modal-element');\n    modal.dataset.open = 'true';\n\n    // The modal focuses its close button when it opens, but the email field is where the visitor\n    // starts, so focus moves there once the modal is showing.\n    const emailInput = this.shadowRoot.querySelector<HTMLInputElement>('.email input');\n    emailInput.focus();\n  }\n\n  /**\n   * Clears what the previous signup left behind, the product details, the email field, the error\n   * lines, and the icons inside the field.\n   */\n  private reset() {\n    this.email = '';\n    this.productId = 0;\n    this.productTitle = '';\n    this.productUrl = '';\n    this.sku = '';\n    this.variantId = 0;\n    this.variantTitle = '';\n\n    const emailInput = this.shadowRoot.querySelector<HTMLInputElement>('.email input');\n    emailInput.classList.remove('error');\n    emailInput.value = '';\n\n    const validationError = this.shadowRoot.querySelector('.validation-error-message');\n    validationError.textContent = '';\n\n    const submitError = this.shadowRoot.querySelector('.submit-error-message');\n    submitError.textContent = '';\n\n    const clearButton = this.shadowRoot.querySelector<HTMLElement>('.clear');\n    clearButton.hidden = true;\n\n    const checkIcon = this.shadowRoot.querySelector<HTMLElement>('.check');\n    checkIcon.hidden = true;\n  }\n\n  private renderFirstView() {\n    const modal = this.closest('modal-element');\n    modal.dataset.title = 'Keep me updated.';\n\n    const beforeSubmit = this.shadowRoot.querySelector<HTMLElement>('.before-submit');\n    beforeSubmit.hidden = false;\n    const afterSubmit = this.shadowRoot.querySelector<HTMLElement>('.after-submit');\n    afterSubmit.hidden = true;\n\n    // Pre-fill email if the user is logged in\n    const emailInput = this.shadowRoot.querySelector<HTMLInputElement>('.email input');\n    if (!emailInput.value && this.email) {\n      emailInput.value = this.email;\n    }\n  }\n\n  private async subscribe() {\n    const emailInput = this.shadowRoot.querySelector<HTMLInputElement>('.email input');\n    const marketingConsent = this.shadowRoot.querySelector<HTMLInputElement>('#marketing-consent');\n    const submitError = this.shadowRoot.querySelector('.submit-error-message');\n\n    // Reset in case of repeated attempt.\n    submitError.textContent = '';\n\n    this.email = emailInput.value.trim();\n\n    if (!isValidEmailAddress(this.email)) {\n      submitError.textContent = 'Invalid email address';\n      return;\n    }\n\n    const input: SubscribeInput = {\n      acceptsMarketing: !!marketingConsent.checked,\n      currency_code: this.dataset.currencyCode,\n      email: this.email,\n      productId: this.productId,\n      productTitle: this.productTitle,\n      productUrl: this.productUrl,\n      sku: this.sku,\n      variantId: this.variantId,\n      variantTitle: this.variantTitle\n    };\n\n    // Reset marketing consent checkbox after successful form submission\n    marketingConsent.checked = false;\n    const responseBody = await this.submitSubscribe(input);\n\n    // Back in Stock has no way to look up an existing signup, and turns down a second signup for\n    // the same email and variant with an error. That error means the visitor is already set, so it\n    // gets the confirmation view rather than a red error line.\n    if (isAlreadyRegistered(responseBody)) {\n      submitError.textContent = '';\n      emailInput.value = '';\n      this.renderSecondView(\n        'You\\'re already subscribed to the back in stock notification for this product.');\n      return;\n    }\n\n    if (responseBody.status !== 'OK') {\n      const errorMessage = findErrorMessage(responseBody);\n      if (errorMessage) {\n        submitError.textContent = errorMessage;\n        setTimeout(() => submitError.textContent = '', 5000);\n      } else {\n        submitError.textContent = 'Uh oh! Something went wrong. Please try again later.';\n        console.warn('Unexpected Back in Stock response body', responseBody);\n      }\n\n      return;\n    }\n\n    // On success, ensure there is no visible error from a previous attempt\n    submitError.textContent = '';\n\n    // Clear the input email address\n    emailInput.value = '';\n\n    // Show a new view.\n    this.renderSecondView();\n  }\n\n  private onFormSubmit(event: SubmitEvent) {\n    // Suppress the browser's native form submission so the subscription is handled here in script\n    // rather than navigating the page. This fires for any submission method, including pressing\n    // enter within the email field, not just a click on the submit button.\n    event.preventDefault();\n\n    // The form skips the browser's own validation, so an empty field reaches here and gets our own\n    // error line rather than the browser's tooltip.\n    const emailInput = this.shadowRoot.querySelector<HTMLInputElement>('.email input');\n    if (emailInput.value.trim() === '') {\n      const validationError = this.shadowRoot.querySelector('.validation-error-message');\n      validationError.textContent = 'Please enter your email.';\n      emailInput.classList.add('error');\n      emailInput.focus();\n      return;\n    }\n\n    // The subscribe routine handles its own failures internally and resolves rather than rejecting,\n    // so this is not expected to reject. The catch exists to satisfy the no-floating-promises rule\n    // and to log defensively should that expectation ever break.\n    this.subscribe().catch(console.warn);\n  }\n\n  private renderSecondView(\n    message = 'If your product comes back in stock, we\\'ll notify you by email.') {\n    const modal = this.closest('modal-element');\n    modal.dataset.title = 'You\\'re all set.';\n\n    const messageParagraph = this.shadowRoot.querySelector('.after-submit p');\n    messageParagraph.textContent = message;\n\n    const beforeSubmit = this.shadowRoot.querySelector<HTMLElement>('.before-submit');\n    beforeSubmit.hidden = true;\n    const afterSubmit = this.shadowRoot.querySelector<HTMLElement>('.after-submit');\n    afterSubmit.hidden = false;\n    const continueButton = this.shadowRoot.querySelector<HTMLElement>('.after-submit button');\n    continueButton.addEventListener('click', this.onContinueButtonClickBound);\n    continueButton.focus({ focusVisible: true });\n  }\n\n  /**\n   * Signs up a user to receive an email notification when a product is available for sale again\n   * because its available inventory increased. Returns a promise that resolves when the\n   * subscription completes.\n   *\n   * Upon a successful subscription, we record the email address in a cookie so that analytics\n   * integrations can access it on page load for purposes of enhanced matching. If the subscription\n   * was not successful then the previous cookie, if one exists, is left as is.\n   */\n  private async submitSubscribe(input: SubscribeInput) {\n    // The caller validates the email and bails out before reaching this point, so this guard should\n    // never fire. It remains as a defensive fallback in case this is ever called from elsewhere.\n    if (typeof input.email !== 'string' || input.email.length === 0) {\n      // Simulate a response so the common error handling code is simpler.\n      return <ResponseBody>{\n        status: 'Error',\n        errors: {\n          base: ['Invalid email address']\n        }\n      };\n    }\n\n    // Since the opt-in checkbox is optional and used to add customers to a specific Klaviyo list,\n    // we need to check if it is checked and set the value to true if it is, otherwise it is\n    // undefined. This will prevent the customer from being removed from the list on any subsequent\n    // notify me form submissions where they negate to check the opt-in checkbox.\n    const acceptsMarketing = input.acceptsMarketing ? input.acceptsMarketing : undefined;\n\n    const attributes: Partial<Attributes> = {\n      accepts_marketing: acceptsMarketing\n    };\n\n    let responseBody: ResponseBody;\n    try {\n      responseBody = await createSubscription(input.email, input.variantId, input.productId,\n        attributes);\n    } catch (error) {\n      // Simulate a response so the common error handling code is simpler.\n      return <ResponseBody>{\n        status: 'Error',\n        errors: {\n          base: ['Uh oh! Something went wrong. Please try again later.']\n        }\n      };\n    }\n\n    // Create combined product title unless the variant title to capture variant options in name\n    const productTitle = input.variantTitle === 'Default Title' ?\n      input.productTitle : `${input.productTitle} - ${input.variantTitle}`;\n    type FormSubmitDetail = WindowEventMap['form-submit']['detail'];\n    const event = new CustomEvent<FormSubmitDetail>('form-submit', {\n      bubbles: true,\n      composed: true,\n      detail: {\n        currency_code: input.currency_code,\n        email_provided: input.email ? true : false,\n        form_name: productTitle,\n        form_location: location.href,\n        form_type: 'back-in-stock',\n        phone_provided: false\n      }\n    });\n\n    this.dispatchEvent(event);\n\n    if (input.email) {\n      type EmailCapturedDetail = WindowEventMap['identity-captured']['detail'];\n      const identityCaptured = new CustomEvent<EmailCapturedDetail>('identity-captured', {\n        bubbles: true,\n        composed: true,\n        detail: {\n          email: input.email\n        }\n      });\n\n      this.dispatchEvent(identityCaptured);\n    }\n\n    return responseBody;\n  }\n}\n\n/**\n * Creates a Back In Stock subscription and resolves with the service's result.\n *\n * Its creation method does not return a real promise: the returned value exposes a then method but\n * no catch method. An async function always returns a native promise, and when its body returns a\n * thenable the runtime does not resolve the promise with that thenable as a value; instead it waits\n * on the thenable and settles the native promise with the thenable's eventual result. So returning\n * the foreign object from this async wrapper converts it into a genuine native promise, and callers\n * never touch the non-standard object directly.\n *\n * Throws when the service is uninitialized, a state we occasionally observe in the wild.\n */\nasync function createSubscription(email: string, variantId: number, productId: number,\n  attributes: Partial<Attributes>) {\n  // The service is only typed as a property of the global window object rather than as a true\n  // ambient global, so it is reached through window instead of by bare name.\n  assert(typeof window.BIS?.create === 'function', 'BIS.create is not a function');\n  return window.BIS.create(email, variantId, productId, attributes);\n}\n\ninterface SubscribeInput {\n  acceptsMarketing: boolean;\n  currency_code: string;\n  email: string;\n  productId: number;\n  productTitle: string;\n  productUrl: string;\n  sku: string;\n  variantId: number;\n  variantTitle: string;\n}\n\n/**\n * Whether Back in Stock turned the signup down because this email is already signed up for this\n * variant. The service only says so in the text of its error message, \"You have already registered\n * for a notification for that item.\", so this matches on that text.\n */\nfunction isAlreadyRegistered(body: Partial<ResponseBody>) {\n  const message = findErrorMessage(body);\n  return typeof message === 'string' && message.includes('already registered');\n}\n\n/**\n * Searches the response body from BackInStock for the presence of an error message and returns the\n * first error message found.\n *\n * The BIS docs say one thing but the real service does something different. It sometimes returns an\n * undocumented \"email\" property that is an array of strings representing errors, and does not\n * return a base property.\n */\nfunction findErrorMessage(body: Partial<ResponseBody>) {\n  if (Array.isArray(body.errors?.base) && body.errors.base.length > 0 &&\n    typeof body.errors.base[0] === 'string' && body.errors.base[0].length > 0) {\n    return body.errors.base[0];\n  } else if (Array.isArray(body.errors?.email) && body.errors.email.length > 0 &&\n    typeof body.errors.email[0] === 'string' && body.errors.email[0].length) {\n    return body.errors.email.join('\\n');\n  }\n}\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'notify-me-form': NotifyMeForm;\n  }\n}\n\nif (!customElements.get('notify-me-form')) {\n  customElements.define('notify-me-form', NotifyMeForm);\n}\n"],"names":["AssertionError","Error","constructor","message","super","this","name","isValidEmailAddress","email","test","NotifyMeForm","HTMLElement","onModalDisplayClickBound","onModalDisplayClick","bind","onFormSubmitBound","onFormSubmit","onEmailInputBound","onEmailInput","onEmailBlurBound","onEmailBlur","validateEmailBound","validateEmail","onContinueButtonClickBound","onContinueButtonClick","onClearButtonClickBound","onClearButtonClick","connectedCallback","initializeShadowRoot","emailInput","shadowRoot","querySelector","addEventListener","disconnectedCallback","removeEventListener","form","clearButton","attachShadow","mode","innerHTML","_event","closest","dataset","open","event","currentTarget","HTMLInputElement","textContent","classList","remove","clearTimeout","debounceTimeout","setTimeout","value","trim","validationError","checkIcon","hidden","add","reset","detail","productId","productTitle","productUrl","sku","variantId","variantTitle","renderFirstView","focus","title","subscribe","marketingConsent","submitError","input","acceptsMarketing","checked","currency_code","currencyCode","responseBody","submitSubscribe","body","findErrorMessage","includes","isAlreadyRegistered","renderSecondView","status","errorMessage","console","warn","preventDefault","catch","continueButton","focusVisible","length","errors","base","attributes","accepts_marketing","undefined","async","_window$BIS","expression","assert","window","BIS","create","createSubscription","error","CustomEvent","bubbles","composed","email_provided","form_name","form_location","location","href","form_type","phone_provided","dispatchEvent","identityCaptured","_body$errors","_body$errors2","Array","isArray","join","customElements","get","define"],"sourceRoot":""}