{"version":3,"file":"pdp-spa-control-section.min.js","mappings":"ocAuBAA,iBAAiB,iCAhBjB,SAAuCC,GACrC,MAAMC,EAAM,IAAIC,IAAIC,SAASC,MAE7B,GAAIH,EAAII,aAAaC,IAAI,aAAeN,EAAMO,OAAOC,oBAAoBC,WAAY,CAInF,GAAIT,EAAMO,OAAOG,mBAAqBV,EAAMO,OAAOI,KAAOX,EAAMO,OAAOG,kBACrE,OAGFT,EAAII,aAAaO,IAAI,UAAWZ,EAAMO,OAAOC,oBAAoBC,YACjEI,QAAQC,aAAa,UAAMC,EAAWd,EAAIG,KAC5C,CACF,KCpBA,IAAAY,EAAA,GAGA,SAAAC,EAAAC,GAEA,IAAAC,EAAAH,EAAAE,GACA,QAAAH,IAAAI,EACA,OAAAA,EAAAC,QAGA,IAAAC,EAAAL,EAAAE,GAAA,CAGAE,QAAA,IAOA,OAHAE,EAAAJ,GAAAG,EAAAA,EAAAD,QAAAH,GAGAI,EAAAD,OACA,6ECpBA,IAAIG,EAuCJ,SAASC,EAA+BC,GACtC,OAAqB,gBAAdA,GAA6C,gBAAdA,CACxC,ECeO,SAAeC,EACpBC,GACA,QAD2B,IAA3BA,IAAAA,EAA8B,oBACF,aAAxBC,SAASC,YAAuC,qBAATF,GACjB,gBAAxBC,SAASC,WAA+B,CACxC,MAAMC,EAAQC,WAAWL,EAAU,GAEnC,MAD8B,IAAMM,aAAaF,EAEnD,CACE/B,iBAAiB4B,EAAMD,EAI3B,CDgDAO,CAlHA,WAEE,MACMC,EAAYN,SAASO,cADV,yDAMjB,IAAKD,EACH,OAMF,MAAME,EAAoBC,SAASH,EAAUI,QAAQF,kBAAmB,IAMxE,IAAKG,OAAOC,UAAUJ,GACpB,OAGF,MAAMK,EAAaP,EAAUQ,iBAAiB,mBAE9C,GAA+B,WAA3BR,EAAUI,QAAQX,MAAqBc,EAAWE,OAAS,EAAG,CAEhE,MAAMC,EAAW,4BAA4BR,MACvCS,EAAkBX,EAAUC,cAA8BS,GAC5DC,IACFtB,EAAkBsB,EAAgBP,QAAQQ,MAE9C,CACF,GAiFA/C,iBAAiB,iCA3EjB,SAAuCC,GACrC,IAAKA,EAAMO,OAAOG,kBAChB,OAGF,MAAMqC,EAASnB,SAASc,iBACtB,iDAAiD1C,EAAMO,OAAOI,YAEhE,IAAK,MAAMqC,KAASD,EAGlBC,EAAMC,OAASZ,SAASW,EAAMV,QAAQY,UAAW,MAAQlD,EAAMO,OAAOC,oBAGxE,IAAKe,EACH,OAIF,MAAM4B,EAAcvB,SAASO,cAC3B,uCAEID,EAAYN,SAASO,cACzB,yDAEIiB,EAAc7B,EAAgB8B,MAAM,OAE1C,IAAK,MAAMC,KAAStD,EAAMO,OAAOgD,cAAe,CAW9C,GAAmB,UAAfD,EAAME,MACRhC,EAA+BxB,EAAMO,OAAOG,mBAC5C,SAUF,MAAM+C,EAAWvB,EAAUC,cACzB,4BAA4BnC,EAAMO,OAAOuC,UAAUQ,EAAME,WAErDE,EAAQrB,SAASoB,EAASnB,QAAQqB,SAAU,IAAM,EACxD,GAAID,EAAQ,GAAKA,GAASN,EAAYT,OACpC,MAGFS,EAAYM,GAASJ,EAAMM,eAC7B,CAEArC,EAAkB6B,EAAYS,KAAK,OAGnC,MAAMC,EAAQ,+BAA+BvC,MACvCsB,EAAkBX,EAAUC,cAA8B2B,GAE5DjB,IACFX,EAAUI,QAAQF,kBAAoBS,EAAgBP,QAAQ3B,GAC9DwC,EAAYb,QAAQyB,OAASC,KAAKC,MAAMxD,WAE5C","sources":["webpack://lange/./sections/pdp-spa-control/history-control.ts","webpack://lange/webpack/bootstrap","webpack://lange/./sections/pdp-spa-control/selected-variant-control.ts","webpack://lange/./lib/dom-ready.ts"],"sourcesContent":["/**\n * Updates the variant query parameter on the product detail page in response to users selecting\n * different variant options.\n *\n * This listener does not touch the DOM so it can handle events before DOMContentLoaded, so there is\n * no need to wait to register it until after DOMContentLoaded.\n */\nfunction onProductVariantOptionChanged(event: WindowEventMap['product-variant-option-changed']) {\n  const url = new URL(location.href);\n\n  if (url.searchParams.get('variant') !== event.detail.selected_variant_id.toString()) {\n    // We must check if the event is emitted from a bundle product detail page by confirming if the\n    // bundle_product_id property is defined. If so, we confirm the variant change event was emitted\n    // for the primary tool in the bundle before updating the query parameter.\n    if (event.detail.bundle_product_id && event.detail.id !== event.detail.bundle_product_id) {\n      return;\n    }\n\n    url.searchParams.set('variant', event.detail.selected_variant_id.toString());\n    history.replaceState(null, undefined, url.href);\n  }\n}\n\naddEventListener('product-variant-option-changed', onProductVariantOptionChanged);\n","// The module cache\nlet __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tlet cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tlet module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","import { ready } from '../../lib/dom-ready.js';\n\nlet selectedOptions: string;\n\nfunction onReady() {\n  // Find the product element. We need to do this in order to determine the selected variant id.\n  const selector = '#product-detail-section add-to-cart > product-element';\n  const productEl = document.querySelector<HTMLElementTagNameMap['product-element']>(selector);\n\n  // The product element is routinely not found on 404 pages, which routinely occurs when products\n  // are unpublished.\n\n  if (!productEl) {\n    return;\n  }\n\n  // The data-selected-variant-id parameter is routinely present with no value in the product\n  // element when the variant url parameter is invalid. In that case, this will contain a value of\n  // NaN.\n  const selectedVariantId = parseInt(productEl.dataset.selectedVariantId, 10);\n\n  // This happens routinely when the product element selected variant id is not initialized. It is\n  // routinely not initialized when the incoming variant url parameter is invalid. The variant url\n  // parameter is routinely invalid because of spammers, people that copy bad links around, and\n  // because marketing changes a product's variants every so often.\n  if (!Number.isInteger(selectedVariantId)) {\n    return;\n  }\n\n  const variantEls = productEl.querySelectorAll('variant-element');\n\n  if (productEl.dataset.type === 'bundle' && variantEls.length > 1) {\n    type VariantElement = HTMLElementTagNameMap['variant-element'];\n    const selector = `variant-element[data-id=\"${selectedVariantId}\"]`;\n    const selectedVariant = productEl.querySelector<VariantElement>(selector);\n    if (selectedVariant) {\n      selectedOptions = selectedVariant.dataset.title;\n    }\n  }\n}\n\nfunction isBundleSwitchItUpOptionChange(productId: number) {\n  return productId === 8194905997412 || productId === 7901832937572;\n}\n\nfunction onProductVariantOptionChanged(event: WindowEventMap['product-variant-option-changed']) {\n  if (!event.detail.bundle_product_id) {\n    return;\n  }\n\n  const thumbs = document.querySelectorAll<HTMLElement>(\n    `.bundle-items .bundle-images[data-product-id=\"${event.detail.id}\"] img`);\n\n  for (const thumb of thumbs) {\n    // Only the thumb for the newly selected variant stays on screen. Every other one gets hidden,\n    // including the one that was showing a moment ago.\n    thumb.hidden = parseInt(thumb.dataset.variantId, 10) !== event.detail.selected_variant_id;\n  }\n\n  if (!selectedOptions) {\n    return;\n  }\n\n  type AddToCartElement = HTMLElementTagNameMap['add-to-cart'];\n  const addToCartEl = document.querySelector<AddToCartElement>(\n    '#product-detail-section add-to-cart');\n  type ProductElement = HTMLElementTagNameMap['product-element'];\n  const productEl = document.querySelector<ProductElement>(\n    '#product-detail-section add-to-cart > product-element');\n\n  const optionParts = selectedOptions.split(' / ');\n\n  for (const group of event.detail.option_groups) {\n    // Since we select by default the color options for the switch-it-up-black and switch-it-up-\n    // blush products we must check for two conditions before proceeding. First we check if the\n    // event was emitted on either of these special case bundle products. Second is to check if the\n    // option change event was sourced from a color option. If both the first and second conditions\n    // are true we use continue to skip this iteration of the for loop to prevent errors since no\n    // Color options are incorporated in the bundle product abstractions created by Shopify for\n    // these two specific products. This logic ensures existing bundles will operate normally in\n    // addition to the missing color option for the switch-it-up-black and switch-it-up-blush\n    // products.\n\n    if (group.name === 'Color' &&\n      isBundleSwitchItUpOptionChange(event.detail.bundle_product_id)) {\n      continue;\n    }\n\n    // If the emitting product is a Shopify create bundle we must query for the bundle option to get\n    // the position index to update on the product abstraction created by the Shopify Bundle app. We\n    // cannot naively assume we can query for the <bundle-option> element by using the option group\n    // name and regex, because there might be multiple products with size or color option groups in\n    // the bundle. Therefore we must favor specificity and construct an exact match using the\n    // emitting product title and option group name.\n\n    const optionEl = productEl.querySelector<HTMLElement>(\n      `bundle-option[data-name=\"${event.detail.title} (${group.name})\"]`);\n\n    const index = parseInt(optionEl.dataset.position, 10) - 1;\n    if (index < 0 || index >= optionParts.length) {\n      break;\n    }\n\n    optionParts[index] = group.selected_option;\n  }\n\n  selectedOptions = optionParts.join(' / ');\n\n  type VariantElement = HTMLElementTagNameMap['variant-element'];\n  const query = `variant-element[data-title=\"${selectedOptions}\"]`;\n  const selectedVariant = productEl.querySelector<VariantElement>(query);\n\n  if (selectedVariant) {\n    productEl.dataset.selectedVariantId = selectedVariant.dataset.id;\n    addToCartEl.dataset.update = Date.now().toString();\n  }\n}\n\nready(onReady);\n\naddEventListener('product-variant-option-changed', onProductVariantOptionChanged);\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":["addEventListener","event","url","URL","location","href","searchParams","get","detail","selected_variant_id","toString","bundle_product_id","id","set","history","replaceState","undefined","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","exports","module","__webpack_modules__","selectedOptions","isBundleSwitchItUpOptionChange","productId","callback","type","document","readyState","timer","setTimeout","clearTimeout","ready","productEl","querySelector","selectedVariantId","parseInt","dataset","Number","isInteger","variantEls","querySelectorAll","length","selector","selectedVariant","title","thumbs","thumb","hidden","variantId","addToCartEl","optionParts","split","group","option_groups","name","optionEl","index","position","selected_option","join","query","update","Date","now"],"sourceRoot":""}