{
  "version": 3,
  "sources": ["/cdn/shop/t/40/assets/bfcm-3pack-cart.js?cb=1794350611127"],
  "sourcesContent": ["/* BFCM 3-PACK LINES - the drawer script of snippets/bfcm-3pack-cart.liquid (RFC 0007 Q32, R24; D-A460).\n *\n * WHAT IT DOES. It marks the UpCart drawer row of every one-time BFCM 3-pack line - a cart line whose\n * Fly offer is one the snippet names (VOL-139457, the Bath Filter 3-Pack; VOL-172786, the Water Bottle\n * Filter 3-Pack) and which is on no selling plan - with the attribute data-bfcm-3pack, and the\n * snippet's style then hides that row's \"Upgrade to Subscribe & Save\" section and any select in it.\n * Every other row is left exactly as UpCart drew it (the single filters, the everyday multi-pack deals,\n * the Fly bundles, EG's rewards, plain lines), and so is a 3-pack line that is already on a plan: it\n * has lost the 3-pack price already, and its plan select is the drawer's only sign that it renews and\n * its only way back to one-time purchase. It never writes to the cart: its only request is a GET of\n * cart.js. BFCM only: once the two offers are off after the sale (runbook E1) no new line carries them,\n * and this file marks nothing but a 3-pack line still in a cart from the sale. Until its files are\n * removed (runbook E10), though, it is not idle: it still watches the drawer and still makes its one\n * debounced GET of cart.js (at most two per key) for each line added after the page loaded, on top of\n * S70's own read (SHARED WITH S70, below).\n *\n * WHY A CACHED ASSET, NOT INLINE. The same split as its sibling assets/eg-reward-cart.js (RFC 0007\n * S70), for the same reasons: about 5 KB once the comments are stripped (as Shopify's CDN serves theme\n * JS; under 2 KB compressed), the same on every page, so as a file it is downloaded once and cached\n * across pages, while an inline script is sent again with every page's HTML, comments included\n * (inline scripts are not minified). Deferred is early enough: it runs before UpCart's own bundle,\n * and no flash needs preventing. If the file fails to load, nothing is hidden, which is exactly the\n * state before this fix. The snippet keeps the short style inline.\n *\n * ITS DATA. The loader tag carries four attributes, all from Liquid: data-bfcm-3pack-offers (the offer\n * ids, comma-separated), data-bfcm-3pack-names (the property names that carry a Fly offer id: the\n * OFFER_NAMES of assets/bfcm-sub-cart.js), data-bfcm-3pack-lines (every line key of the cart the page\n * was rendered with) and data-bfcm-3pack-marked (the keys among them of one-time 3-pack lines: the\n * rows to mark). Shopify builds a line key from the variant id and a hash of the line's properties and\n * selling plan (a plan change gives the line a new key, as assets/six-pack-cart.js notes), so a key\n * never changes what it names: once seen, it is known for good. Nothing in this file is Liquid.\n *\n * HOW A LINE'S OFFER IS READ. norm, prop and lineOffer below are assets/bfcm-sub-cart.js's own (its\n * normaliser is snippets/bundle-aware-cart-quantity.liquid's): a property's name counts when,\n * lower-cased with everything but letters and digits removed, it equals one of the names so treated;\n * the first such property of the line, in its own order, holds the offer; the offer id is that value\n * before its first underscore (Fly writes \"VOL-172786_\" and a random key). That id must equal one of\n * the configured ids exactly: no trimming, no case folding. The snippet's Liquid applies the same rule\n * to the page-load cart, so a line is classified the same way before and after a cart read.\n *\n * WHEN IT READS THE CART. Never while the drawer has no rows. Otherwise only when the drawer's row\n * signature (each row's id and quantity, as assets/bfcm-sub-cart.js and six-pack-cart.js sign it) has\n * changed AND a row holds a line key this page does not know yet: a line added after the page was\n * rendered. DEBOUNCE ms after the last such change, one read at a time; a change that lands during a\n * read gets one more read afterwards, and only if a row is still unknown then. When the rows go away\n * (UpCart draws none while its drawer is closed) the signature is cleared, so the same rows drawn\n * again count as a change: a read whose timer fired while no row was there is not lost. A read that\n * has not answered after READ_LIMIT ms is given up: aborted, its answer unused, the lock released.\n * A failed, unreadable or given-up answer changes nothing - every row keeps what UpCart drew - and is\n * not retried until the drawer changes again, and a key is read for at most TRIES times while it\n * stays unknown, so no read ever repeats by itself or keeps feeding on UpCart's redraws. A pageview\n * whose drawer shows the cart the page was rendered with therefore makes no request at all.\n *\n * TIMING. Loaded with defer from the layout, it runs once the page is parsed and before UpCart's own\n * deferred app-embed bundle further down the body, so its observer is already watching when UpCart\n * draws the rows. Rows already drawn are marked at once, later ones within a frame of each change.\n * The observer also sees a row's id change in place, so a row node UpCart reuses for another line is\n * marked or unmarked for its new line.\n *\n * SHARED WITH S70. Its read logic (the one-at-a-time lock, the debounce, the 8-second READ_LIMIT abort,\n * the TRIES cap of two reads per key, the signature reset and the observer) is a copy of\n * assets/eg-reward-cart.js's (RFC 0007 S70), names aside, so a fix to it must be made in both files,\n * and while both are in, a line added after the page loaded costs two cart reads, one by each file. If\n * Q32 is ever kept beyond BFCM, fold it into S70's script as a second matcher.\n *\n * LIMITS. Rows are UpCart V1's .upcart-product-item in the document (the light DOM this store\n * renders), whose id holds the line key \"<variant id>:<32 hex>\", the shape assets/bfcm-sub-cart.js and\n * six-pack-cart.js rely on. A V2 drawer or a shadow-root drawer shows no such row, and then this file\n * reads nothing and hides nothing. Display only, and only once a row is classified: a 3-pack added\n * after the page loaded (the usual way: from its product page, the drawer opening on it) shows\n * UpCart's button for DEBOUNCE ms plus one cart.js round trip, and keeps it after a failed read until\n * the drawer changes again. The mark follows the line's offer, not its quantity. A cart request made\n * some other way can still put a plan on a 3-pack line.\n */\n(function () {\n  'use strict';\n  try {\n    if (window.bfcm3PackCart) return;\n    var loader = document.querySelector('script[data-bfcm-3pack-offers]');\n    if (!loader) return;\n    /* A comma list from the loader, empty entries dropped. */\n    function attrList(attribute) {\n      return String(loader.getAttribute(attribute) || '').split(',').filter(Boolean);\n    }\n    /* assets/bfcm-sub-cart.js's normaliser: lower case, letters and digits only. */\n    function norm(key) {\n      return String(key || '').toLowerCase().replace(/[^a-z0-9]/g, '');\n    }\n    /* The offer ids exactly as configured; the property names as bfcm-sub-cart.js compares them. */\n    var OFFERS = attrList('data-bfcm-3pack-offers');\n    var NAMES = attrList('data-bfcm-3pack-names').map(norm).filter(Boolean);\n    if (!OFFERS.length || !NAMES.length) return;\n    var MARK = 'data-bfcm-3pack';\n    var ROW = '.upcart-product-item';\n    var KEY = /\\d+:[0-9a-f]{32}/i;\n    var DEBOUNCE = 250;\n    var READ_LIMIT = 8000;\n    var TRIES = 2;\n\n    /* Line key -> true: every key this page has classified, and the one-time 3-pack keys among them\n     * (the rows to mark). */\n    var known = {};\n    var packs = {};\n    /* Line key -> the reads made while that key was unknown (TRIES caps them). */\n    var tries = {};\n    function keyList(attribute) {\n      return attrList(attribute)\n        .map(function (key) { return key.trim().toLowerCase(); })\n        .filter(Boolean);\n    }\n    keyList('data-bfcm-3pack-lines').forEach(function (key) { known[key] = true; });\n    keyList('data-bfcm-3pack-marked').forEach(function (key) {\n      known[key] = true;\n      packs[key] = true;\n    });\n\n    function root() {\n      return (window.Shopify && window.Shopify.routes && window.Shopify.routes.root) || '/';\n    }\n    function rows() {\n      return Array.prototype.slice.call(document.querySelectorAll(ROW));\n    }\n    function rowKey(row) {\n      var match = String(row.id || '').match(KEY);\n      return match ? match[0].toLowerCase() : '';\n    }\n    function rowQty(row) {\n      var input = row.querySelector('.upcart-product-quantity-input');\n      return input ? String(input.value) : '';\n    }\n    /* A row worth a read: its key is not classified yet and has not used up its reads. */\n    function unknownRow(row) {\n      var key = rowKey(row);\n      return !!key && !known[key] && (tries[key] || 0) < TRIES;\n    }\n    /* bfcm-sub-cart.js's prop(): the value of the first property whose name is one of NAMES. */\n    function prop(properties) {\n      if (!properties || typeof properties !== 'object') return null;\n      var keys = Object.keys(properties);\n      for (var i = 0; i < keys.length; i += 1) {\n        if (NAMES.indexOf(norm(keys[i])) !== -1) return properties[keys[i]];\n      }\n      return null;\n    }\n    /* bfcm-sub-cart.js's lineOffer(): the offer id is the value before its first underscore. */\n    function lineOffer(line) {\n      var value = prop(line && line.properties);\n      return value ? String(value).split('_')[0] : '';\n    }\n    /* A BFCM 3-pack line: its offer is one of OFFERS, exactly. The same test as the snippet's Liquid. */\n    function isPack(line) {\n      var offer = lineOffer(line);\n      return !!offer && OFFERS.indexOf(offer) !== -1;\n    }\n    /* On a selling plan: cart.js gives a one-time line no selling_plan_allocation. The same test as\n     * the snippet's Liquid. */\n    function onPlan(line) {\n      return !!(line && line.selling_plan_allocation);\n    }\n    /* Compare-then-set: a row is written only when its mark is wrong for its line. */\n    function paint(list) {\n      list.forEach(function (row) {\n        var on = !!packs[rowKey(row)];\n        if (row.hasAttribute(MARK) === on) return;\n        if (on) row.setAttribute(MARK, '');\n        else row.removeAttribute(MARK);\n      });\n    }\n\n    var timer = null;\n    var busy = false;\n    var again = false;\n    function schedule() {\n      window.clearTimeout(timer);\n      timer = window.setTimeout(read, DEBOUNCE);\n    }\n    function read() {\n      timer = null;\n      if (busy) {\n        again = true;\n        return;\n      }\n      var list = rows();\n      if (!list.some(unknownRow)) return;\n      var counted = {};\n      list.forEach(function (row) {\n        if (!unknownRow(row)) return;\n        var key = rowKey(row);\n        if (counted[key]) return;\n        counted[key] = true;\n        tries[key] = (tries[key] || 0) + 1;\n      });\n      busy = true;\n      var done = false;\n      var ctl = typeof window.AbortController === 'function' ? new window.AbortController() : null;\n      /* A stalled connection would otherwise hold the one-at-a-time lock, and with it every later\n       * 3-pack's read, for as long as the browser keeps the request open. */\n      var limit = window.setTimeout(function () {\n        if (ctl) ctl.abort();\n        finish();\n      }, READ_LIMIT);\n      function finish() {\n        if (done) return;\n        done = true;\n        window.clearTimeout(limit);\n        busy = false;\n        if (again) {\n          again = false;\n          schedule();\n        }\n      }\n      var init = { cache: 'no-store', headers: { Accept: 'application/json' } };\n      if (ctl) init.signal = ctl.signal;\n      fetch(root() + 'cart.js', init)\n        .then(function (response) {\n          return !done && response && response.ok ? response.json() : null;\n        })\n        .then(function (cart) {\n          if (done || !cart || !Array.isArray(cart.items)) return;\n          cart.items.forEach(function (line) {\n            var key = line && line.key ? String(line.key).toLowerCase() : '';\n            if (!key) return;\n            known[key] = true;\n            if (isPack(line) && !onPlan(line)) packs[key] = true;\n            else delete packs[key];\n          });\n          paint(rows());\n        })\n        .catch(function () {\n          /* Nothing changes: every row keeps what UpCart drew. */\n        })\n        .then(finish);\n    }\n\n    var lastSig = '';\n    function pass() {\n      var list = rows();\n      if (!list.length) {\n        /* No rows: the drawer is closed or loading. Forget the signature, so the rows drawn next\n         * are a change even when they are the same lines. */\n        lastSig = '';\n        return;\n      }\n      paint(list);\n      var sig = list.map(function (row) { return row.id + '@' + rowQty(row); }).sort().join('|');\n      if (sig === lastSig) return;\n      lastSig = sig;\n      if (list.some(unknownRow)) schedule();\n    }\n    var queued = false;\n    function onMutate() {\n      if (queued) return;\n      queued = true;\n      window.requestAnimationFrame(function () {\n        queued = false;\n        pass();\n      });\n    }\n    /* Its own writes are the mark attribute, which this observer does not watch: it cannot feed itself. */\n    new MutationObserver(onMutate).observe(document.documentElement, {\n      childList: true,\n      subtree: true,\n      attributes: true,\n      attributeFilter: ['id']\n    });\n    pass();\n    if (document.readyState !== 'complete') {\n      document.addEventListener('DOMContentLoaded', pass, { once: true });\n      window.addEventListener('load', pass, { once: true });\n    }\n\n    /* For a console check on the live theme: the one-time 3-pack line keys this page knows (the rows\n     * it marks). */\n    window.bfcm3PackCart = {\n      packKeys: function () { return Object.keys(packs); }\n    };\n  } catch (error) {\n    if (window.console) console.warn('[bfcm-3pack-cart] not started', error);\n  }\n})();\n"],
  "mappings": "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",
  "names": []
}
