{"version":3,"file":"meta-pixel-snippet.min.js","mappings":"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","sources":["webpack://lange/../node_modules/@integrabeauty/cookie/src/index.ts","webpack://lange/../node_modules/@integrabeauty/shopify-consent/src/index.ts","webpack://lange/./snippets/meta-pixel/index.ts"],"sourcesContent":["/**\n * Represents the properties of a cookie.\n */\nexport interface Cookie {\n  /**\n   * Include a leading \".\" to include subdomains.\n   *\n   * @example \".langehair.com\"\n   */\n  domain: string;\n\n  /**\n   * Indicates the maximum lifetime of the cookie as an HTTP-date timestamp. See Date for the\n   * required formatting.\n   *\n   * If unspecified, the cookie becomes a session cookie. A session finishes when the client shuts\n   * down, after which the session cookie is removed.\n   *\n   * Warning: Many web browsers have a session restore feature that will save all tabs and restore\n   * them the next time the browser is used. Session cookies will also be restored, as if the\n   * browser was never closed.\n   *\n   * When an Expires date is set, the deadline is relative to the client the cookie is being set on,\n   * not the server.\n   */\n  expires: Date;\n\n  /**\n   * A <cookie-name> can contain any US-ASCII characters except for: control characters (ASCII\n   * characters `0` up to `31` and ASCII character `127`) or separator characters (space, tab and\n   * the characters: `( ) < > @ , ; : \\ \" / [ ] ? = { })`.\n   */\n  name: string;\n\n  /**\n   * Path is generally \"/\" which is for all pages for a domain. Otherwise the cookie is specific to\n   * the given path (e.g. particular page).\n   */\n  path: string;\n\n  /**\n   * Controls whether or not a cookie is sent with cross-site requests, providing some protection\n   * against cross-site request forgery attacks (CSRF).\n   *\n   * * Strict Means that the browser sends the cookie only for same-site requests, that is, requests\n   *   originating from the same site that set the cookie. If a request originates from a different\n   *   domain or scheme (even with the same domain), no cookies with the SameSite=Strict attribute\n   *   are sent.\n   * * Lax Means that the cookie is not sent on cross-site requests, such as on requests to load\n   *   images or frames, but is sent when a user is navigating to the origin site from an external\n   *   site (for example, when following a link). This is the default behavior if the SameSite\n   *   attribute is not specified.\n   * * None means that the browser sends the cookie with both cross-site and same-site requests. The\n   *   Secure attribute must also be set when setting this value, like so SameSite=None; Secure. If\n   *   Secure is missing an error will be logged\n   *\n   * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie\n   */\n  same_site: 'Lax' | 'None' | 'Strict';\n\n  /**\n   * Indicates that the cookie is sent to the server only when a request is made with the https:\n   * scheme (except on localhost), and therefore, is more resistant to man-in-the-middle attacks.\n   */\n  secure: boolean;\n\n  /**\n   * A <cookie-value> can optionally be wrapped in double quotes and include any US-ASCII character\n   * excluding control characters (ASCII characters 0 up to 31 and ASCII character 127), Whitespace,\n   * double quotes, commas, semicolons, and backslashes.\n   */\n  value: string;\n}\n\n/**\n * Decodes a percent-encoded cookie name or value, falling back to the raw input when decoding is\n * not possible.\n *\n * Cookie storage is shared by every script on a domain, including third party libraries that write\n * raw, improperly-encoded values. A single cookie whose value contains a malformed percent-\n * sequence, such as a literal percent sign that is not followed by two hexadecimal digits, makes\n * decodeURIComponent throw a URIError. Because the whole cookie string is parsed at once, one such\n * cookie would otherwise poison every read on the page rather than just its own entry. Returning\n * the undecoded text keeps the remaining cookies readable, and a value that was never percent-\n * encoded is returned unchanged anyway.\n */\nfunction safeDecode(value: string) {\n  try {\n    return decodeURIComponent(value);\n  } catch (error) {\n    return value;\n  }\n}\n\n/**\n * Parses the value of document.cookie into an an array of cookie objects.\n *\n * **This decodes the cookie name and value. You should not decode the cookie name or value\n * yourself**.\n *\n * Cookies can be set with separate flags. When there are two cookies with the same name but with\n * different flags, the name will appear twice in document.cookie. Therefore, this returns an array\n * of Cookie objects instead of a single object where each name is a property.\n *\n * @throws {Error} if document is undefined, if document.cookie cannot be accessed for some reason\n * such as security restrictions on iframes or browser restrictions, or if document.cookie does not\n * produce a string\n */\nexport function parse() {\n  return document.cookie.split(';').map<Pick<Cookie, 'name' | 'value'>>(parts => {\n    const index = parts.indexOf('=');\n    // decodeURIComponent(undefined) produces strange results in some browsers, such as the string\n    // undefined, so we have to avoid that\n    const encodedValue = parts.slice(index + 1)?.trim();\n    return {\n      name: safeDecode(parts.slice(0, index).trim()),\n      value: typeof encodedValue === 'string' ? safeDecode(encodedValue) : undefined\n    };\n  });\n}\n\n/**\n * Retrieves the value of a particular cookie by name.\n *\n * If a cookie with the given name is not found, then undefined is returned.\n *\n * Internally this calls a private function to parse the cookie and that function accesses\n * document.cookie and for that reason this function can throw. This will almost never happen except\n * when attempting to access cookies cross-context, e.g. accessing parent window cookies from within\n * an iframe where the iframe has certain attributes. Some third party libraries do this naively and\n * without concern for the security constraints imposed by browsers. As a result, you should always\n * consider handling errors when calling this function.\n *\n * Because this can throw, **please implement proper error handling** when calling this function.\n *\n * **This decodes the cookie name and value. You should not decode the cookie name or value\n * yourself**.\n *\n * Cookie values are always strings. If you happen to be storing something like a JSON-like object\n * in the cookie value, then the raw JSON string is returned and is not parsed on your behalf. If\n * you are enclosing your value in double quotes, the leading and trailing double quote characters\n * are included in the returned value.\n *\n * **WARNING**: This is a naive read. When examining the list of cookies, this returns the first one\n * in the list. More than one cookie can have the same name when two or more cookies have the same\n * name but different flags. This is entirely dependent on the order of the names in\n * document.cookie, which is completely unknown and can change at any time and probably varies by\n * browser. Therefore, when there is more than one cookie with the same name, it is possible this\n * returns an unexpected cookie value.\n *\n * @throws {SecurityError}\n * @throws {Error}\n */\nexport function read(name: string) {\n  return readAll(name).shift();\n}\n\nexport function readAll(name: string) {\n  return parse().filter(cookie => cookie.name === name).map(cookie => cookie.value);\n}\n\n/**\n * Given a cookie, generate the string that can be supplied to document.cookie\n *\n * **This encodes the cookie name and cookie. Do not encode these yourself**.\n */\nexport function serialize(cookie: Partial<Cookie>) {\n  if (!cookie.name) {\n    throw new Error('Cookie name is required');\n  }\n\n  const parts: string[] = [];\n\n  parts.push(`${encodeURIComponent(cookie.name)}=${encodeURIComponent(cookie.value || '')}`);\n\n  if (typeof cookie.path === 'string') {\n    parts.push(`Path=${cookie.path}`);\n  }\n\n  if (typeof cookie.domain === 'string') {\n    parts.push(`Domain=${cookie.domain}`);\n  }\n\n  if (cookie.expires && typeof cookie.expires.getTime === 'function') {\n    parts.push(`Expires=${cookie.expires.toUTCString()}`);\n  }\n\n  if (typeof cookie.same_site === 'string') {\n    parts.push(`SameSite=${cookie.same_site}`);\n  }\n\n  if (cookie.secure) {\n    parts.push('Secure');\n  }\n\n  // The delimiter must include a single space after semicolon\n\n  return parts.join('; ');\n}\n\n/**\n * This function can throw because setting document.cookie sometimes throws an error.\n *\n * This function can throw on invalid input, such as when not specifying a required property such as\n * cookie name.\n *\n * **This encodes the cookie name and cookie. Do not encode these yourself**.\n */\nexport function write(cookie: Partial<Cookie>) {\n  document.cookie = serialize(cookie);\n}\n\n/**\n * Deletes a cookie.\n *\n * Of the cookie properties, only the name is required. However, some browsers are extremely strict\n * about how a cookie is deleted. To delete a cookie the exact same other properties must also be\n * provided, other than the cookie's value and expiration date. For example, if the cookie to be\n * deleted is secure, then you must specify the secure property to delete the cookie.\n *\n * If the properties do not match, this completes without error and the cookie that was to be\n * deleted still remains.\n *\n * Deleting a cookie is done by setting its value to an empty string and then assigning the full\n * cookie value to document.cookie.\n *\n * This function may throw an error because deleting a cookie is done by assigning a value to\n * document.cookie which can sometimes throw.\n *\n * The expires property is ignored. This sets expiration date to 1970 to force cookie to become\n * expired.\n *\n * Insecure cookies cannot be removed. These are cookies belonging to the http protocol.\n */\nexport function remove(cookie: Partial<Cookie>) {\n  write({ ...cookie, value: '', expires: new Date(0) });\n}\n","import { read } from '@integrabeauty/cookie';\n\ntype Limitation = 'CCPA_BLOCK_ALL' | 'GDPR';\n\n/**\n * Represents the state of consent.\n *\n * Sometimes this is serialized into a cookie value. Sometimes it is constructed from a esoteric\n * serialization format.\n */\nexport interface ConsentState {\n  con: {\n    CMP?: {\n      a: '0' | '1';\n      m: '0' | '1';\n      p: '0' | '1';\n      s: '0' | '1';\n    };\n    GDPR?: '' | '0' | '1' | 'no_interaction';\n  };\n  display_banner?: boolean;\n  lim: Limitation[];\n  purposes: {\n    /**\n     * analytics\n     */\n    a: boolean;\n\n    /**\n     * marketing\n     */\n    m: boolean;\n\n    /**\n     * personalization\n     */\n    p: boolean;\n\n    t: boolean;\n  };\n\n  reg: '' | 'CCPA' | 'GDPR' | undefined;\n\n  region?: string;\n\n  sale_of_data_region?: boolean;\n\n  v: string;\n}\n\nexport function parseConsentHash(value: string) {\n  // Check for v3 cookie or session hash. AMPS can be before or after period, all preceding\n  // underscore. lowercase is denied. uppercase is granted. it seems like initially AMPS defaults\n  // are after the period. After interacting with checkboxes, characters move to before the period.\n  //\n  // Format: 3[flags]_[region]_[other data] Where region is a 2 to 6 character code like USCA (US\n  // California) or USIL (US Illinois) I do not know about 6. I do see 5, e.g. GBNIR (Great Britain\n  // Northern Ireland).\n  //\n  // The region can also just be the country. I suppose Shopify is sometimes unable to determine the\n  // subregion.\n  //\n  // The subregion can contain digits.\n  //\n  // The region can be empty.\n  //\n  // The hash can carry trailing underscore-delimited segments after the region that we do not need,\n  // most notably a consent id and, when a metafield is written through setTrackingConsent, an\n  // appended metafield blob in raw or URL encoded JSON. This regex is anchored only at the start\n  // and reads just the flags and region, so any such trailing data is tolerated and ignored. Do not\n  // anchor it at the end or switch to a positional split, which would make those trailing segments\n  // break parsing.\n\n  const matches = /^3([amps]{0,4}\\.?[amps]{0,4})_([A-Z0-9]*)_/i.exec(value);\n\n  if (matches?.length > 2) {\n    const flags = matches[1];\n    const region = matches[2];\n    const state = <ConsentState>{\n      purposes: {},\n      region\n    };\n\n    if (flags.includes('A')) {\n      state.purposes.a = true;\n    } else if (flags.includes('a')) {\n      state.purposes.a = false;\n    }\n\n    if (flags.includes('M')) {\n      state.purposes.m = true;\n    } else if (flags.includes('m')) {\n      state.purposes.m = false;\n    }\n\n    if (flags.includes('P')) {\n      state.purposes.p = true;\n    } else if (flags.includes('p')) {\n      state.purposes.p = false;\n    }\n\n    // note that s appears as t in the object\n    if (flags.includes('S')) {\n      state.purposes.t = true;\n    } else if (flags.includes('s')) {\n      state.purposes.t = false;\n    }\n\n    return state;\n  }\n\n  // Shopify can update the cookie format at any time without coordination and the docs even mention\n  // that parsing the cookie is not advised. Log a warning message so that we get spammed with\n  // warnings about when this happens. We could eventually decide to throw here, but downstream\n  // callers are all written to expect this not to throw. So we leak the error by logging so that\n  // error diagnostics platforms that have hooked console can still observe the error. Beware this\n  // could result in false positives (e.g. genuinely bad input).\n  if (typeof value === 'string' && value && !value.startsWith('{')) {\n    console.warn('Failed to parse _tracking_consent cookie value:', value);\n    return null;\n  }\n\n  // Otherwise, assume it is a v1 or v2 cookie that is a json stringified object.\n\n  let consent: unknown;\n  try {\n    consent = JSON.parse(value);\n  } catch (error) {}\n\n  if (!isConsentState(consent)) {\n    return null;\n  }\n\n  return consent;\n}\n\nexport function readCookie() {\n  let cookie;\n  try {\n    cookie = read('_tracking_consent');\n  } catch (error) {}\n\n  if (!cookie) {\n    return null;\n  }\n\n  return cookie;\n}\n\n/**\n * Reads the consent hash from session storage. Shopify's consent library reads the consent value\n * from the navigation Server-Timing entry and stores it under this key, so the value here is the\n * same one the Server-Timing source provides, just read from Shopify's cache of it.\n */\nexport function readSession() {\n  let session;\n  try {\n    session = sessionStorage.getItem('consentHeader');\n  } catch (error) {}\n\n  if (!session) {\n    return null;\n  }\n\n  return session;\n}\n\n/**\n * Reads the consent hash from the navigation response's Server-Timing entry. Shopify's consent\n * library reads the current consent state from this entry, so this reads the same entry. Shopify\n * also copies this value into session storage, so the session storage source is a cache of this\n * one. The Performance API is the only way for script to observe this header.\n */\nexport function readServerTiming() {\n  try {\n    const entries = performance.getEntriesByType('navigation');\n    const navigation = <PerformanceNavigationTiming>entries[0];\n    if (!navigation) {\n      return null;\n    }\n\n    const timings = navigation.serverTiming;\n    if (!timings) {\n      return null;\n    }\n\n    let header: string | undefined;\n    for (const timing of timings) {\n      if (timing.name === '_cmp') {\n        header = timing.description;\n        break;\n      }\n    }\n\n    if (!header) {\n      return null;\n    }\n\n    // Decoding is best effort. A malformed value is left as is rather than discarded.\n    try {\n      header = decodeURIComponent(header);\n    } catch (error) {}\n\n    return header;\n  } catch (error) {\n    return null;\n  }\n}\n\n/**\n * Resolves the visitor's consent hash. All three sources are read, then the first available value\n * is used in priority order: cookie, then session storage, then the navigation Server-Timing entry.\n * Each source carries the same hash format. Shopify's consent library reads these same sources.\n */\nexport function readCustomerConsent() {\n  // All three are read up front so that every value is available for the diagnostic log below when\n  // none of them resolve. The cookie can be missing when it is not accessible due to same-site\n  // restrictions, in which case session storage or the Server-Timing entry can still carry a value.\n  const cookie = readCookie();\n  const session = readSession();\n  const serverTiming = readServerTiming();\n\n  let consent = cookie;\n  if (!consent) {\n    consent = session;\n  }\n\n  if (!consent) {\n    consent = serverTiming;\n  }\n\n  if (!consent) {\n    console.debug('Unable to determine consent', cookie, session, serverTiming);\n    return null;\n  }\n\n  return parseConsentHash(consent);\n}\n\n/**\n * We use the Shopify consent API to determine if a user has given consent to marketing tracking.\n * Although Shopify does provide a client-side mechanism to load the customer privacy API and\n * supporting methods/functionality, we have made the decision to use the Shopify set\n * \"_tracking_consent\" cookie to prevent pixels from having to wait for an asynchronous call to load\n * the API module.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/globalPrivacyControl\n */\nexport function hasMarketingConsent() {\n  const cookie = readCustomerConsent();\n  if (typeof cookie?.purposes?.m === 'boolean') {\n    return cookie.purposes.m;\n  }\n\n  return getDefaultConsent(cookie?.region);\n}\n\n/**\n * We use the Shopify consent API to determine if a user has given consent to analytics tracking.\n * Although Shopify does provide a client-side mechanism to load the customer privacy API and\n * supporting methods/functionality, we have made the decision to use the Shopify set\n * \"_tracking_consent\" cookie to prevent pixels from having to wait for an asynchronous call to load\n * the API module.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/globalPrivacyControl\n */\nexport function hasAnalyticsConsent() {\n  const cookie = readCustomerConsent();\n  if (typeof cookie?.purposes?.a === 'boolean') {\n    return cookie.purposes.a;\n  }\n\n  return getDefaultConsent(cookie?.region);\n}\n\nexport function hasSaleOfDataConsent() {\n  const cookie = readCustomerConsent();\n  if (typeof cookie?.purposes?.t === 'boolean') {\n    return cookie.purposes.t;\n  }\n\n  return getDefaultConsent(cookie?.region);\n}\n\nexport function isConsentState(value: any): value is ConsentState {\n  return value && 'purposes' in value;\n}\n\n/**\n * Determines the regulation type based on the region code.\n *\n * @param region country and province e.g. \"USCA\", \"DEDE\", \"GBGB\"\n * @returns The applicable regulation type\n */\nexport function determineRegulation(region: string) {\n  // Most regions are 4, but in some cases they can be longer such as FRIDF or GBNIR, and can\n  // sometimes be shorter, such as ROB. A country code is always 2, and there has to be at least one\n  // character for the subregion, so we use 3 as the cutoff for minimal validity. For example, for\n  // ROB, RO is Romania, and B is some subregion.\n  //\n  // The region can also just consist of the country. I am not sure why but I definitely see it in\n  // the wild, where the region value is just \"US\". Therefore, < 2 is the correct condition.\n  //\n  // The region can be the empty string. When the region is completely unknown, we cannot derive the\n  // regulation, so we still want to exit early here.\n\n  if (typeof region !== 'string' || region.length < 2) {\n    return;\n  }\n\n  const regionCode = region.slice(0, 2);\n\n  // All country codes are 2 digits. The province/state are all remaining characters, which is\n  // typically 2 characters, but might be 3, e.g. GBNIR for Great Britain Northern Ireland. This\n  // might not matter that much in practice as we only consider the subregion for CCPA, which only\n  // applies to US, where all subregions are 2 characters in length, but this is more correct.\n  //\n  // There may not be a subregion.\n  const subregionCode = region.length > 2 ? region.slice(2) : undefined;\n\n  // EU countries require GDPR compliance\n  const gdprCountries = [\n    'AT',\n    'BE',\n    'BG',\n    'HR',\n    'CY',\n    'CZ',\n    'DK',\n    'EE',\n    'FI',\n    'FR',\n    'DE',\n    'GR',\n    'HU',\n    'IS',\n    'IE',\n    'IT',\n    'LV',\n    'LI',\n    'LT',\n    'LU',\n    'MT',\n    'NL',\n    'NO',\n    'PL',\n    'PT',\n    'RO',\n    'SI',\n    'SK',\n    'ES',\n    'SE',\n    'GB'\n  ];\n\n  if (gdprCountries.includes(regionCode)) {\n    return 'GDPR';\n  }\n\n  // US states with CCPA-like regulations\n  const ccpaSubregions = ['CA', 'VA'];\n\n  if (regionCode === 'US' && ccpaSubregions.includes(subregionCode)) {\n    return 'CCPA';\n  }\n}\n\n/**\n * Gets the default consent value for a region when no explicit consent is given.\n *\n * @param region 4-character region code\n * @returns Default consent value based on regional privacy laws\n */\nexport function getDefaultConsent(region: string) {\n  const regulation = determineRegulation(region);\n\n  // GDPR requires opt-in consent (default = false)\n  if (regulation === 'GDPR') {\n    return false;\n  }\n\n  // CCPA allows opt-out (default = true, user can opt out). Other regions are generally permissive\n  return true;\n}\n","import type {} from '@integrabeauty/custom-events';\nimport type {} from '@integrabeauty/meta-pixel';\nimport type { VisitorConsentCollectedEvent } from '@integrabeauty/shopify-browser-types';\nimport { hasMarketingConsent, hasSaleOfDataConsent } from '@integrabeauty/shopify-consent';\n\ninterface MetaInit {\n  customer: {\n    default_address: {\n      address1: string;\n      address2: string;\n      city: string;\n      company: string;\n      country: string;\n      country_code: string;\n      country_name: string;\n      customer_id: number;\n      default: boolean;\n      first_name: string;\n      id: number;\n      last_name: string;\n      name: string;\n      phone: string;\n      province: string;\n      province_code: string;\n      zip: string;\n    };\n    email: string;\n    first_name: string;\n    id: number;\n    last_name: string;\n    phone: string;\n  };\n\n  listener: (event: Event) => void;\n  market: string;\n  pixel_id: string;\n  queue: Event[];\n}\n\ndeclare global {\n  /**\n   * This is created in liquid\n   */\n  var meta_init: MetaInit;\n}\n\n/**\n * Meta defines this as \"When a person enters the checkout flow prior to completing the checkout\n * flow.\"\n *\n * This event interacts with Meta Commerce. The product ids transmitted should match up with the ids\n * of products in the synced catalogs.\n *\n * Do not include any PII in the event.\n *\n * @see https://developers.facebook.com/docs/meta-pixel/reference\n * @see https://developers.facebook.com/docs/meta-pixel/implementation/pixel-for-official-events#initiatecheckout\n */\nfunction onCheckoutStarted(event: WindowEventMap['checkout-started']) {\n  const cart = event.detail.cart;\n\n  fbq('track', 'InitiateCheckout', {\n    content_ids: cart.items.map(item =>\n      buildCatalogId(item.product_id, item.variant_id)),\n    contents: cart.items.map(item => ({\n      id: buildCatalogId(item.product_id, item.variant_id),\n      quantity: item.quantity\n    })),\n    currency: cart.currency,\n    num_items: cart.item_count.toString(),\n    value: cart.total_price / 100\n  });\n}\n\n/**\n * This must match the configuration for synchronizing with fb commerce\n */\nfunction buildCatalogId(productId: number, variantId: number) {\n  return `shopify_${productId}_${variantId}`;\n}\n\n/**\n * When the user starts a Gladly conversation, send a Contact event to Facebook.\n *\n * The Facebook docs say a Contact event should be sent \"When a person initiates contact with your\n * business via telephone, SMS, email, chat, etc.\"\n *\n * All of the contact event properties are optional. It is not clear what we should be sending. I\n * invented a custom field to represent the method involved in the contact.\n *\n * Do not include any PII in the event.\n *\n * @see https://developers.facebook.com/docs/meta-pixel/reference\n */\nfunction onGladlyConversationStarted(_event: WindowEventMap['gladly-conversation-started']) {\n  fbq('track', 'Contact', { custom_method: 'gladly-chat' });\n}\n\n/**\n * The Meta documentation explicitly mentions this specific use as an example of a contact event:\n *\n * > When a person initiates contact with your business via telephone, SMS, email, chat, etc. > A\n * person submits a question about a product.\n *\n * We are not sending the body because the question content is not used for analysis or targeting\n * and there is a risk of PII being present.\n *\n * Do not include any PII in the event.\n *\n * @see https://developers.facebook.com/docs/meta-pixel/reference/\n */\nfunction onYotpoQuestionCreated(_event: WindowEventMap['yotpo-question-created']) {\n  fbq('track', 'Contact', { custom_method: 'yotpo-question-create-form' });\n}\n\n/**\n * Handle a review submitted event. If the review was submitted successfully, send a contact event.\n *\n * Do not include any PII in the event.\n */\nfunction onYotpoReviewSubmitted(event: WindowEventMap['yotpo-review-submitted']) {\n  if (!event.detail.error_message) {\n    fbq('track', 'Contact', { custom_method: 'yotpo-product-review-submitted' });\n\n    const email = event.detail.email;\n    if (isValidEmail(email)) {\n      try {\n        localStorage.setItem('meta_yotpo_email', email);\n      } catch (error) {\n        console.debug(error);\n      }\n    }\n  }\n}\n\n/**\n * When a product variant is viewed, send a ViewContent event to Facebook.\n *\n * The content id must align with the content id in the Facebook Commerce Center catalogue and all\n * other events that send product-related data to Facebook.\n *\n * Relevant fields are content_ids, content_category, content_name, content_type, contents,\n * currency, and value. if using dynamic ads then content_type and contents or content_ids are\n * required. We do not need to send both content_ids and contents. There is no real point to sending\n * contents here because we are not sending other properties such as quantity.\n *\n * Do not include any PII in the event.\n *\n * @see https://developers.facebook.com/docs/meta-pixel/reference#object-properties\n */\nfunction onProductVariantViewed(event: WindowEventMap['product-variant-viewed']) {\n  if (!Number.isInteger(event.detail.product_id)) {\n    return;\n  }\n\n  const contentId = buildCatalogId(event.detail.product_id, event.detail.variant_id);\n\n  fbq('track', 'ViewContent', {\n    content_category: event.detail.type,\n    content_name: event.detail.product_title,\n    content_ids: contentId ? [contentId] : undefined,\n    content_type: 'product',\n    currency: event.detail.currency,\n    value: event.detail.price / 100\n  });\n}\n\n/**\n * When debugging this event, beware that the Meta Pixel Chrome extension is sometimes incorrect.\n * Always debug using the pixel debugging tool provided by Meta. The extension is wrong.\n *\n * Do not include any PII in the event.\n */\nfunction onCartUpdated(event: GlobalEventHandlersEventMap['cart-updated']) {\n  for (const variant of event.detail.variants_added) {\n    fbq('track', 'AddToCart', {\n      content_category: variant.product_type,\n      content_name: variant.product_title,\n      content_type: 'product',\n      content_ids: [buildCatalogId(variant.product_id, variant.variant_id)],\n      currency: event.detail.cart.currency,\n      value: variant.price / 100\n    });\n  }\n}\n\n/**\n * Handle a form submit event and forward a completed registration event to the Facebook pixel.\n *\n * Do not include any PII in the event.\n *\n * @see https://developers.facebook.com/docs/meta-pixel/reference\n * @see https://www.facebook.com/business/help/402791146561655?id=1205376682832142\n */\nfunction onFormSubmit(event: WindowEventMap['form-submit']) {\n  const formType = event.detail.form_type;\n  if (formType) {\n    fbq('track', 'CompleteRegistration', {\n      registration_type: event.detail.form_type,\n      currency: event.detail.currency_code || 'USD',\n      value: 0\n    });\n  }\n}\n\n/**\n * When a customer email or phone number is captured during user interaction with the site, store it\n * in local storage for potential use in enhanced matching for Facebook Pixel.\n */\nfunction onIdentityCaptured(event: WindowEventMap['identity-captured']) {\n  const email = event.detail.email;\n  if (isValidEmail(email)) {\n    try {\n      localStorage.setItem('meta_email_capture_email', email);\n    } catch (error) {\n      console.debug(error);\n    }\n  }\n\n  const phone = event.detail.phone;\n  if (phone) {\n    try {\n      localStorage.setItem('meta_attentive_phone', phone);\n    } catch (error) {\n      console.debug(error);\n    }\n  }\n}\n\n/**\n * When the user selects a different variant for a product, send a CustomizeProduct event to\n * Facebook.\n *\n * The documentation shows that all properties are optional. It is not clear what we are supposed to\n * send to Facebook. As a guess, send the new product and variant that was selected.\n *\n * Do not include any PII in the event.\n *\n * @see https://developers.facebook.com/docs/meta-pixel/reference\n */\nfunction onProductVariantOptionChanged(event: WindowEventMap['product-variant-option-changed']) {\n  if (!Number.isInteger(event.detail.id) || !Number.isInteger(event.detail.selected_variant_id)) {\n    return;\n  }\n\n  fbq('track', 'CustomizeProduct', {\n    content_ids: [buildCatalogId(event.detail.id, event.detail.selected_variant_id)],\n    content_type: 'product'\n  });\n}\n\n/**\n * When a user searches our products using our on-site search, send a search event to Facebook.\n *\n * Do not include any PII in the event.\n *\n * @see https://developers.facebook.com/docs/meta-pixel/reference\n */\nfunction onSearched(event: WindowEventMap['searched']) {\n  const query = event.detail.query?.trim();\n  if (query) {\n    fbq('track', 'Search', { search_string: query });\n  }\n}\n\n/**\n * Handles a Typeform submit event. Send a contact event to Meta.\n */\nfunction onTypeformSubmitted(event: WindowEventMap['typeform-submitted']) {\n  if (!event.detail.error_message) {\n    fbq('track', 'Contact', { custom_method: 'typeform-survey-submitted' });\n  }\n}\n\nfunction onKlaviyoFormOpened(_event: WindowEventMap['klaviyo-form-opened']) {\n  fbq('track', 'Lead');\n}\n\n/**\n * Used to track when a user completes any of the front-end hair quiz flows. This is a custom event\n * that is not part of the standard Meta Pixel events.\n *\n * @see https://developers.facebook.com/documentation/meta-pixel/implementation/conversion-tracking#custom-events\n */\nfunction onQuizCompleted(event: WindowEventMap['quiz-completed']) {\n  if (!event.detail.event_id) {\n    return;\n  }\n\n  fbq('trackCustom', 'QuizCompleted', {\n    event_id: `QuizCompleted_lange-beauty_${event.detail.event_id}`,\n    custom_method: 'quiz-completed',\n    name: event.detail.name,\n    url: event.detail.url,\n    vendor: event.detail.vendor,\n    products: event.detail.products,\n    resp_id: event.detail.resp_id\n  });\n}\n\nfunction isValidEmail(email: string) {\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\ninterface EnhancedMatchingData {\n  /**\n   * Lowercase two letter country code\n   *\n   * @example \"us\"\n   */\n  country: string;\n\n  /**\n   * Lowercase with any spaces removed\n   */\n  ct: string;\n\n  /**\n   * Digits only with birth year, month, then day\n   */\n  db: string;\n\n  /**\n   * Unhashed lowercase or hashed SHA-256\n   */\n  em: string;\n\n  /**\n   * Any unique ID from the advertiser, such as loyalty membership ID, user ID, and external cookie\n   * ID.\n   */\n  external_id: string;\n\n  /**\n   * First name\n   */\n  fn: string;\n\n  /**\n   * Gender\n   */\n  ge: 'f' | 'm';\n\n  /**\n   * Last name\n   */\n  ln: string;\n\n  /**\n   * Digits only including country code and area code\n   *\n   * @example 12223334444\n   */\n  ph: string;\n\n  /**\n   * Lowercase two-letter state or province code\n   */\n  st: string;\n\n  /**\n   * Zip or postal code\n   */\n  zp: string;\n}\n\nfunction buildData() {\n  const customer = meta_init.customer;\n\n  /**\n   * See https://developers.facebook.com/docs/meta-pixel/advanced/advanced-matching\n   */\n  const data: Partial<EnhancedMatchingData> = {};\n\n  if (isValidEmail(customer.email)) {\n    data.em = customer.email.toLowerCase();\n  }\n\n  if (!data.em) {\n    try {\n      const email = localStorage.getItem('meta_email_capture_email');\n      if (isValidEmail(email)) {\n        data.em = email.toLowerCase();\n      }\n    } catch (error) {}\n  }\n\n  if (!data.em) {\n    try {\n      const email = localStorage.getItem('meta_attentive_email');\n      if (isValidEmail(email)) {\n        data.em = email.toLowerCase();\n      }\n    } catch (error) {}\n  }\n\n  if (!data.em) {\n    try {\n      const email = localStorage.getItem('meta_marketing_subscribe_email');\n      if (isValidEmail(email)) {\n        data.em = email.toLowerCase();\n      }\n    } catch (error) {}\n  }\n\n  if (!data.em) {\n    try {\n      const email = localStorage.getItem('meta_bis_email');\n      if (isValidEmail(email)) {\n        data.em = email.toLowerCase();\n      }\n    } catch (error) {}\n  }\n\n  if (!data.em) {\n    try {\n      const email = localStorage.getItem('meta_yotpo_email');\n      if (isValidEmail(email)) {\n        data.em = email.toLowerCase();\n      }\n    } catch (error) {}\n  }\n\n  if (customer.phone) {\n    let phone = customer.phone.replace(/[^0-9]/g, '');\n    phone = phone.length === 10 ? '1' + phone : phone;\n    data.ph = phone;\n  }\n\n  if (!data.ph) {\n    try {\n      let phone = localStorage.getItem('meta_attentive_phone');\n      if (phone) {\n        phone = phone.replace(/[^0-9]/g, '');\n        phone = phone.length === 10 ? '1' + phone : phone;\n        data.ph = phone;\n      }\n    } catch (error) {}\n  }\n\n  if (customer.first_name) {\n    data.fn = customer.first_name.trim().toLowerCase();\n  }\n\n  if (customer.last_name) {\n    data.ln = customer.last_name.trim().toLowerCase();\n  }\n\n  const address = customer.default_address || <typeof customer['default_address']>{};\n\n  if (address.city) {\n    data.ct = address.city.replace(/\\s/g, '').toLowerCase();\n  }\n\n  if (address.province_code?.length === 2) {\n    data.st = address.province_code.toLowerCase();\n  }\n\n  if (address.zip) {\n    data.zp = address.zip.trim().toLowerCase();\n  }\n\n  if (address.country_code?.length === 2) {\n    data.country = address.country_code.toLowerCase();\n  }\n\n  if (Number.isInteger(customer.id)) {\n    data.external_id = customer.id.toString();\n  }\n\n  return data;\n}\n\nfunction main() {\n  if (location.protocol !== 'https:' || location.hostname !== 'langehair.com') {\n    return;\n  }\n\n  // An automated browser is not a shopper, so anything we send for it is noise in the ad data. We\n  // stop before init, which means no pixel id, no advanced matching, no PageView, and no event\n  // listeners. The loader in liquid still runs, so fbq itself is defined and calls elsewhere do not\n  // throw, they just never reach a configured pixel. Append the webdriver_bypass query param to\n  // keep the pixel on, for a run that is actually about the pixel. That param is the general escape\n  // hatch for things we only do under webdriver.\n\n  if (navigator.webdriver === true && !/webdriver_bypass/.test(location.search)) {\n    console.debug('Not loading the Meta pixel because of automated agent');\n    return;\n  }\n\n  try {\n    if (top !== window) {\n      return;\n    }\n  } catch (error) {\n    return;\n  }\n\n  fbq('consent', 'grant');\n\n  // If the visitor is in a CCPA region, and has denied sale of data, then enable Facebook's limited\n  // data usage restrictions. We go further. If someone opts out of sale of data, regardless of CCPA\n  // or GDPR, we restrict collection, to reflect intent.\n\n  if (!hasSaleOfDataConsent()) {\n    console.log('Meta Pixel LDU activated due to CCPA restrictions');\n    fbq('dataProcessingOptions', ['LDU'], 0, 0);\n  }\n\n  if (!hasMarketingConsent()) {\n    fbq('consent', 'revoke');\n  }\n\n  const data = buildData();\n  if (data && Object.keys(data).length > 0) {\n    fbq('init', meta_init.pixel_id, data);\n  } else {\n    fbq('init', meta_init.pixel_id);\n  }\n\n  fbq('track', 'PageView');\n\n  const listenerMap: Record<string, (event: any) => void> = {\n    'cart-updated': onCartUpdated,\n    'checkout-started': onCheckoutStarted,\n    'gladly-conversation-started': onGladlyConversationStarted,\n    'identity-captured': onIdentityCaptured,\n    'klaviyo-form-opened': onKlaviyoFormOpened,\n    'form-submit': onFormSubmit,\n    'product-variant-option-changed': onProductVariantOptionChanged,\n    'product-variant-viewed': onProductVariantViewed,\n    'quiz-completed': onQuizCompleted,\n    'searched': onSearched,\n    'typeform-submitted': onTypeformSubmitted,\n    'yotpo-question-created': onYotpoQuestionCreated,\n    'yotpo-review-submitted': onYotpoReviewSubmitted\n  };\n\n  // Unregister the loader listener from all events. This expects that the loader's list of event\n  // types is properly in sync with the listener map.\n  for (const eventType in listenerMap) {\n    removeEventListener(eventType, meta_init.listener);\n  }\n\n  // Replay any prior events. We need an explicit try/catch per call here in case one call errs\n  for (const event of meta_init.queue) {\n    try {\n      listenerMap[event?.type](event);\n    } catch (error) {\n      console.warn(error);\n    }\n  }\n\n  // Reset the queue (for idempotency and memory)\n  meta_init.queue = [];\n\n  // Register the new listeners\n  for (const type in listenerMap) {\n    addEventListener(type, listenerMap[type]);\n  }\n\n  document.addEventListener('visitorConsentCollected', onVisitorConsentCollected);\n}\n\n/**\n * Shopify emitted event that indicates the user has changed their consent settings. This handler\n * should be responsible for updating the fbq consent state based on the passed in values.\n *\n * @see https://shopify.dev/docs/api/customer-privacy#use-an-event-listener\n */\nfunction onVisitorConsentCollected(event: VisitorConsentCollectedEvent) {\n  if (event.detail) {\n    if (event.detail.marketingAllowed) {\n      fbq('consent', 'grant');\n    } else {\n      fbq('consent', 'revoke');\n    }\n  }\n}\n\nmain();\n"],"names":["safeDecode","value","decodeURIComponent","error","read","name","document","cookie","split","map","parts","_parts$slice","index","indexOf","encodedValue","slice","trim","undefined","filter","readAll","shift","readCustomerConsent","readCookie","session","sessionStorage","getItem","readSession","serverTiming","navigation","performance","getEntriesByType","timings","header","timing","description","readServerTiming","consent","matches","exec","length","flags","state","purposes","region","includes","a","m","p","t","startsWith","console","warn","JSON","parse","isConsentState","parseConsentHash","debug","getDefaultConsent","regulation","regionCode","subregionCode","determineRegulation","onCheckoutStarted","event","cart","detail","fbq","content_ids","items","item","buildCatalogId","product_id","variant_id","contents","id","quantity","currency","num_items","item_count","toString","total_price","productId","variantId","onGladlyConversationStarted","_event","custom_method","onYotpoQuestionCreated","onYotpoReviewSubmitted","error_message","email","isValidEmail","localStorage","setItem","onProductVariantViewed","Number","isInteger","contentId","content_category","type","content_name","product_title","content_type","price","onCartUpdated","variant","variants_added","product_type","onFormSubmit","form_type","registration_type","currency_code","onIdentityCaptured","phone","onProductVariantOptionChanged","selected_variant_id","onSearched","_event$detail$query","query","search_string","onTypeformSubmitted","onKlaviyoFormOpened","onQuizCompleted","event_id","url","vendor","products","resp_id","test","onVisitorConsentCollected","marketingAllowed","location","protocol","hostname","navigator","webdriver","search","top","window","_cookie$purposes3","hasSaleOfDataConsent","log","_cookie$purposes","hasMarketingConsent","data","_address$province_cod","_address$country_code","customer","meta_init","em","toLowerCase","replace","ph","first_name","fn","last_name","ln","address","default_address","city","ct","province_code","st","zip","zp","country_code","country","external_id","buildData","Object","keys","pixel_id","listenerMap","searched","eventType","removeEventListener","listener","queue","addEventListener","main"],"sourceRoot":""}