{
  "version": 3,
  "sources": ["/cdn/shop/t/270/assets/ef-spin.js?x=1793726474577"],
  "sourcesContent": ["/* Spin & Save.\n *\n * SINCE 5 NOV 2026 THE DRAW RUNS IN THE PAGE (see LOCAL DRAW below; owner: no gcloud, run\n * independently). The text that follows describes the earlier server design, kept for the\n * contract it still speaks when cfg.endpoint is set.\n *\n * The browser did not decide anything here. It asks the server for an index,\n * then spins the wheel to that index. Every interesting rule \u2014 the odds, one\n * spin per account, minting the discount code with its minimum-spend \u2014 lives\n * on the other side of that request, because a wheel that draws in JavaScript\n * is a wheel where the console awards itself the RM 1,999 voucher.\n *\n * TWO STEPS, matching spin.DRAW_CONTRACT. /draw reveals which prize was won\n * immediately \u2014 the customer sees what they landed on \u2014 but returns no code.\n * The code only exists once /claim is called, which happens only when the\n * customer explicitly accepts. Between those two moments they may take\n * \"One More Chance\" instead: that re-draws, and is allowed exactly once,\n * because if the code were handed over before that choice, accepting a\n * respin afterwards would leave the customer holding two codes for one\n * entry.\n *\n * PREVIEW MODE. With no endpoint configured the wheel still turns, so the\n * design can be reviewed, but it draws and \"claims\" locally, says so on\n * screen, and issues no real code.\n */\n(function () {\n  'use strict';\n\n  var root = document.querySelector('[data-ef-spin]');\n  if (!root) return;\n\n  var cfgEl = document.querySelector('[data-ef-spin-cfg]');\n  var cfg = {};\n  try { cfg = JSON.parse(cfgEl.textContent); } catch (e) { return; }\n\n  var wheel = root.querySelector('[data-ef-spin-wheel]');\n  var go = root.querySelector('[data-ef-spin-go]');\n  var claimBtn = root.querySelector('[data-ef-spin-claim]');\n  var againBtn = root.querySelector('[data-ef-spin-again]');\n  var panel = root.querySelector('[data-ef-spin-panel]');\n  var nameEl = root.querySelector('[data-ef-spin-name]');\n  var photoEl = root.querySelector('[data-ef-spin-photo]');\n  var codeWrap = root.querySelector('[data-ef-spin-code]');\n  var codeText = root.querySelector('[data-ef-spin-code-text]');\n  var copyBtn = root.querySelector('[data-ef-spin-copy]');\n  var confettiEl = root.querySelector('[data-ef-spin-confetti]');\n  var n = cfg.prizes.length;\n  var seg = 360 / n;\n  var spinning = false;\n  var respun = false;\n\n  /* 11.11: every word comes from the page (cfg.t), so /ms/ reads in Malay. */\n  var TT = cfg.t || {};\n  var t = function (k, en) { return TT[k] || en; };\n  var REASONS = {\n    already_spun: t('already_spun', 'You have already taken your spin.'),\n    already_claimed: t('already_claimed', 'This prize has already been claimed.'),\n    not_signed_in: t('not_signed_in', 'Please sign in to spin.'),\n    not_open_yet: t('not_open_yet', 'Spin & Save has not opened yet.')\n  };\n\n  function announce(text, isError) {\n    panel.hidden = false;\n    photoEl.hidden = true;\n    nameEl.textContent = text;\n    nameEl.classList.toggle('is-error', !!isError);\n  }\n\n  /* The wheel is only ever rotated forwards, and the resting angle is kept so\n   * a second spin continues from where the first stopped rather than snapping\n   * back to zero before it starts. */\n  var angle = 0;\n\n  function spinTo(index) {\n    /* The rotation needed is relative to where the wheel IS, not relative to\n     * a fresh wheel at rest \u2014 angle carries over from the previous spin, so\n     * a flat \"360 - index*seg\" delta (correct only from a zero baseline) put\n     * the PREVIOUS result's own offset back into play every respin: landing\n     * on index 6 then respinning to index 4 rotated by 4's delta on top of\n     * 6's already-resting position, so the wheel visually parked on\n     * (6 + 4) mod 7 = 3 while the reveal correctly announced 4 \u2014 a real\n     * mismatch a customer could see, found live (5,000mAh shown as landed,\n     * 10,000mAh revealed). Fixed by computing the delta FROM the wheel's\n     * current mod-360 position TO the absolute angle that puts `index` at\n     * the pointer, so every spin is correct regardless of where the wheel\n     * already was. */\n    var wantMod = (360 - index * seg) % 360;\n    var curMod = angle % 360;\n    var diff = wantMod - curMod;\n    if (diff <= 0) diff += 360;\n    var target = 360 * 5 + diff;\n    angle += target;\n    var reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;\n    if (reduce) {\n      /* No 4-second flourish for someone who asked for less motion \u2014 the\n       * result is the point, the animation is decoration. */\n      wheel.style.transition = 'none';\n      wheel.style.transform = 'rotate(' + angle + 'deg)';\n      return Promise.resolve();\n    }\n    wheel.style.transition = 'transform 4.2s cubic-bezier(0.16, 1, 0.3, 1)';\n    wheel.style.transform = 'rotate(' + angle + 'deg)';\n    return new Promise(function (res) {\n      var done = false;\n      var finish = function () { if (!done) { done = true; res(); } };\n      wheel.addEventListener('transitionend', finish, { once: true });\n      /* transitionend does not fire if the tab is hidden mid-spin, and the\n       * result would then never be announced. */\n      setTimeout(finish, 4600);\n    });\n  }\n\n  /* Fires once, the instant the wheel stops. Brand colours only \u2014 the wheel's\n   * own blue/orange plus white, never rainbow confetti, which would put\n   * hues in the page this brand doesn't have. DOM spans animated via\n   * transform/opacity only (CSS handles the animation, not JS on a timer),\n   * so this costs nothing once the elements are removed. Respects\n   * prefers-reduced-motion the same way spinTo() does \u2014 skipped entirely,\n   * not just shortened, since a burst of motion is the one thing that\n   * request is specifically asking to not see. */\n  var CONFETTI_COLORS = ['var(--ef-blue)', 'var(--ef-blaze)', 'var(--ef-white)', 'var(--ef-blue-300)'];\n  function confetti() {\n    if (!confettiEl) return;\n    if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;\n    confettiEl.textContent = '';\n    var n = 28;\n    for (var i = 0; i < n; i++) {\n      var el = document.createElement('span');\n      var angle = Math.random() * Math.PI * 2;\n      var dist = 60 + Math.random() * 90;\n      el.style.setProperty('--ef-confetti-x', (Math.cos(angle) * dist).toFixed(0) + 'px');\n      el.style.setProperty('--ef-confetti-y', (Math.sin(angle) * dist + 60).toFixed(0) + 'px');\n      el.style.setProperty('--ef-confetti-r', (Math.random() < 0.5 ? '-' : '') + (360 + Math.random() * 360).toFixed(0) + 'deg');\n      el.style.setProperty('--ef-confetti-delay', (Math.random() * 150).toFixed(0) + 'ms');\n      el.style.setProperty('--ef-confetti-c', CONFETTI_COLORS[i % CONFETTI_COLORS.length]);\n      confettiEl.appendChild(el);\n    }\n    setTimeout(function () { confettiEl.textContent = ''; }, 1400);\n  }\n\n  /* Weighted, not uniform \u2014 cfg.prizes[i].weight is how many \"tickets\" that\n   * prize holds, so a 35 next to a 4 is roughly 9x more likely, not an equal\n   * 1-in-7. Only prizes both `live` (a real code exists) and weight > 0 are\n   * eligible; the held-back backpack carries weight 0 for exactly this\n   * reason, so it can never be drawn even in preview. This mirrors\n   * spin.PRIZES in tools/theme/spin.py \u2014 change the odds there, not here.\n   * This is a rehearsal of the real draw, not the real draw: an armed server\n   * must apply the same weighting itself, since the browser is never the\n   * one deciding a live prize. */\n  function weightedIndex() {\n    var pool = [], total = 0, i;\n    for (i = 0; i < cfg.prizes.length; i++) {\n      var p = cfg.prizes[i];\n      if (p.live && p.weight > 0) { pool.push(i); total += p.weight; }\n    }\n    if (!pool.length) return Math.floor(Math.random() * n);\n    var r = Math.random() * total;\n    for (i = 0; i < pool.length; i++) {\n      r -= cfg.prizes[pool[i]].weight;\n      if (r <= 0) return pool[i];\n    }\n    return pool[pool.length - 1];\n  }\n\n  /* LOCAL DRAW (owner, 05NOV2026: \"make the spin draw no need gcloud any more, run independently\").\n   * No server: the platform CSPRNG picks the weighted prize, the device keeps the entry per campaign,\n   * and the drawn prize's code is unmasked only on claim. The codes' own Shopify limits (one use per\n   * customer, minimum spends, end date) are the real guard -- the mask only keeps them out of plain sight. */\n  /* MONTHLY (owner, 05NOV2026: \"reset everyone's spin every month\"): one entry per campaign per calendar\n   * month, Malaysia time, so on the 1st every device spins again. November 2026 also reads the entries made\n   * before the month was added, so nobody gained a second November spin from the change. */\n  var MONTH = new Date(Date.now() + 8 * 3600 * 1000).toISOString().slice(0, 7);\n  var LKEY = 'ef-spin-entry-' + (cfg.local || '') + '-' + MONTH;\n  var LEGACY = MONTH === '2026-11' ? 'ef-spin-entry-' + (cfg.local || '') : null;\n  function entry() { try { return JSON.parse(localStorage.getItem(LKEY) || (LEGACY && localStorage.getItem(LEGACY)) || 'null'); } catch (e) { return null; } }\n  function keep(e) { try { localStorage.setItem(LKEY, JSON.stringify(e)); } catch (x) {} }\n  function rand(max) {\n    var c = window.crypto || window.msCrypto, a = new Uint32Array(1);\n    if (!c || !c.getRandomValues) return Math.floor(Math.random() * max);\n    var lim = Math.floor(4294967296 / max) * max;\n    do { c.getRandomValues(a); } while (a[0] >= lim);   /* no modulo bias */\n    return a[0] % max;\n  }\n  function localIndex() {\n    var pool = [], total = 0, i, w;\n    for (i = 0; i < cfg.prizes.length; i++) {\n      w = (cfg.w || {})[cfg.prizes[i].key] || 0;\n      if (w > 0 && cfg.k && cfg.k[cfg.prizes[i].key]) { pool.push([i, w]); total += w; }\n    }\n    var r = rand(total);\n    for (i = 0; i < pool.length; i++) { if (r < pool[i][1]) return pool[i][0]; r -= pool[i][1]; }\n    return pool[pool.length - 1][0];\n  }\n  function unmask(s) {\n    var b = atob(s), k = String(cfg.local), out = '';\n    for (var i = 0; i < b.length; i++) out += String.fromCharCode(b.charCodeAt(i) ^ k.charCodeAt(i % k.length));\n    return out;\n  }\n  function localOpen() { var now = Date.now() / 1000; return now >= cfg.open && now <= cfg.close; }\n\n  function draw() {\n    if (cfg.local) {\n      if (!localOpen()) return Promise.resolve({ ok: false, reason: 'not_open_yet', status: 425 });\n      var e = entry();\n      if (e && e.c) return Promise.resolve({ ok: false, reason: 'already_claimed', status: 409 });\n      if (e && e.r) return Promise.resolve({ ok: false, reason: 'already_spun', status: 409 });\n      var li = localIndex();\n      keep({ i: li, r: !!e, c: false });\n      return Promise.resolve({ ok: true, index: li, canRespin: !e, status: 200 });\n    }\n    if (!cfg.endpoint) {\n      var i = weightedIndex();\n      return Promise.resolve({ ok: true, index: i, canRespin: !respun, preview: true });\n    }\n    return fetch(cfg.endpoint + '/draw', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      // 'include', not 'same-origin' \u2014 the draw/claim server is a\n      // different origin from the storefront (a separate Cloud Run\n      // service), and 'same-origin' silently drops the \"have you already\n      // spun\" cookie on every cross-origin request, breaking the one-spin\n      // limit without ever surfacing an error.\n      credentials: 'include',\n      body: '{}'\n    }).then(function (r) {\n      return r.json().then(function (j) { j.status = r.status; return j; });\n    });\n  }\n\n  function claim() {\n    if (cfg.local) {\n      var e = entry();\n      var key = e && cfg.prizes[e.i] && cfg.prizes[e.i].key;\n      if (!key || !cfg.k[key]) return Promise.resolve({ ok: false, reason: 'no_draw', status: 400 });\n      e.c = true; keep(e);   /* a second claim returns the same code, as the server did */\n      /* this month's own code (owner, 05NOV2026: new codes each month); November 2026 = the EF11 set */\n      var mk = (cfg.m && cfg.m[MONTH] && cfg.m[MONTH][key]) || cfg.k[key];\n      return Promise.resolve({ ok: true, code: unmask(mk), status: 200 });\n    }\n    if (!cfg.endpoint) {\n      return Promise.resolve({ ok: true, preview: true });\n    }\n    return fetch(cfg.endpoint + '/claim', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      credentials: 'include',\n      body: '{}'\n    }).then(function (r) {\n      return r.json().then(function (j) { j.status = r.status; return j; });\n    });\n  }\n\n  /* Shows what was won straight away \u2014 every spin reveals its prize. The\n   * CODE is the part that waits for an explicit accept. */\n  function reveal(prize, canRespin) {\n    nameEl.classList.remove('is-error');\n    nameEl.textContent = prize.note || prize.label || t('won', 'You won.');\n    if (prize.photo) {\n      photoEl.src = prize.photo;\n      photoEl.hidden = false;\n    } else {\n      photoEl.hidden = true;\n      photoEl.removeAttribute('src');\n    }\n    panel.hidden = false;\n    codeWrap.hidden = true;\n    go.hidden = true;\n    claimBtn.hidden = false;\n    claimBtn.disabled = false;\n    againBtn.hidden = !canRespin || respun;\n    againBtn.disabled = false;\n  }\n\n  function runDraw(onDone) {\n    spinning = true;\n    return draw().then(function (res) {\n      spinning = false;\n      if (!res || res.ok !== true) {\n        announce(REASONS[res && res.reason] || t('spinFail', 'Could not spin just now. Please try again.'), true);\n        return;\n      }\n      return spinTo(res.index).then(function () {\n        confetti();\n        onDone(cfg.prizes[res.index] || {}, !!res.canRespin);\n      });\n    }).catch(function () {\n      spinning = false;\n      announce(t('drawFail', 'Could not reach the draw. Please try again.'), true);\n    });\n  }\n\n  go.addEventListener('click', function () {\n    if (spinning) return;\n    go.disabled = true;\n    runDraw(function (prize, canRespin) { reveal(prize, canRespin); });\n  });\n\n  againBtn.addEventListener('click', function () {\n    if (spinning) return;\n    claimBtn.disabled = true;\n    againBtn.disabled = true;\n    /* Popup closes before the respin, not after \u2014 otherwise the wheel is\n     * spinning behind a scrim the customer can't see through, which reads as\n     * nothing happening. reveal() brings the panel back once it stops. */\n    panel.hidden = true;\n    runDraw(function (prize) {\n      respun = true;\n      reveal(prize, false);\n    });\n  });\n\n  claimBtn.addEventListener('click', function () {\n    claimBtn.disabled = true;\n    againBtn.disabled = true;\n    claim().then(function (res) {\n      if (!res || res.ok !== true) {\n        claimBtn.disabled = false;\n        againBtn.disabled = !againBtn.hidden;\n        announce(REASONS[res && res.reason] || t('claimFail', 'Could not claim just now. Please try again.'), true);\n        return;\n      }\n      /* Closes the entry: once claimed, neither button has anything left to\n       * do \u2014 a second click cannot mint a second code. */\n      claimBtn.hidden = true;\n      againBtn.hidden = true;\n      codeWrap.hidden = false;\n      codeText.textContent = res.preview\n        ? 'Preview only \u2014 no code issued.'\n        : res.code;\n      copyBtn.hidden = !!res.preview;\n      /* Tells the modal controller (bottom of this file) the entry is done,\n       * so it stops auto-opening the popup on future visits. A plain custom\n       * event rather than a shared function call: this code shouldn't need\n       * to know whether it's running inside the dialogue, a page, or both. */\n      document.dispatchEvent(new CustomEvent('ef-spin-claimed'));\n    }).catch(function () {\n      claimBtn.disabled = false;\n      againBtn.disabled = !againBtn.hidden;\n      announce(t('serverFail', 'Could not reach the server. Please try again.'), true);\n    });\n  });\n\n  if (copyBtn) {\n    copyBtn.addEventListener('click', function () {\n      var text = codeText.textContent;\n      if (!text || !navigator.clipboard) return;\n      navigator.clipboard.writeText(text).then(function () {\n        var was = copyBtn.textContent;\n        copyBtn.textContent = t('copied', 'Copied');\n        setTimeout(function () { copyBtn.textContent = was; }, 1500);\n      });\n    });\n  }\n})();\n\n\n/* ---------------------------------------------------------------------------\n * The dialogue controller \u2014 spin.popup()'s <dialog>, opened here.\n *\n * Deliberately NOT the same rule as ef-merdeka.js's promodal, which marks\n * itself \"seen\" the instant it's shown (right, for a one-time announcement).\n * This one is shown-until-actioned: only a successful claim \u2014 the\n * 'ef-spin-claimed' event dispatched above \u2014 marks it done. Closing it via\n * the X, Esc, or a backdrop click does NOT suppress it; it comes back next\n * visit. That's not an oversight, it's the brief: \"only pop up until it has\n * been spun and claimed, after it won't show up.\"\n * ------------------------------------------------------------------------- */\n(function () {\n  'use strict';\n\n  var dlg = document.querySelector('[data-ef-spinmodal]');\n  if (!dlg || typeof dlg.showModal !== 'function') return;\n\n  /* Keyed to the campaign's open epoch, not the site \u2014 a future Spin & Save\n   * with a different date starts fresh without anyone clearing storage. */\n  /* monthly (owner, 05NOV2026): claimed this month = no auto-open until the 1st, Malaysia time */\n  var MONTH = new Date(Date.now() + 8 * 3600 * 1000).toISOString().slice(0, 7);\n  var BASE = 'ef-spinmodal-' + (dlg.getAttribute('data-ef-spinmodal') || 'v1');\n  var KEY = BASE + '-' + MONTH;\n\n  function done() {\n    try { return localStorage.getItem(KEY) === '1' || (MONTH === '2026-11' && localStorage.getItem(BASE) === '1'); } catch (e) { return false; }\n  }\n  function remember() {\n    /* Private windows and blocked site data THROW on write; swallow it\n     * rather than leave the claim handler above in a broken state. */\n    try { localStorage.setItem(KEY, '1'); } catch (e) {}\n  }\n\n  function close() {\n    if (dlg.open) dlg.close();\n  }\n\n  Array.prototype.forEach.call(\n    dlg.querySelectorAll('[data-ef-spinmodal-close]'),\n    function (b) { b.addEventListener('click', close); });\n\n  /* Esc fires 'cancel' before 'close' \u2014 either way, no remember() here; see\n   * the file-level note on why closing must not suppress future visits. */\n  dlg.addEventListener('cancel', function () {});\n\n  /* Tells ef-merdeka.js this dialogue just closed, however that happened \u2014\n   * the X, Esc, a backdrop click, or a claim followed by any of those, since\n   * all of them fire the dialog's own native 'close' event and this needs no\n   * per-path duplicate. That dialogue reacts immediately if it's this\n   * visitor's turn (see its own gate: seen() and spin done()), rather than\n   * only re-checking on a future page load \u2014 the user's own words were\n   * \"spin pop up 1st then close then the second pop up ... will show up\". */\n  dlg.addEventListener('close', function () {\n    document.dispatchEvent(new CustomEvent('ef-spin-dialog-closed'));\n  });\n\n  /* Clicking the backdrop. A <dialog>'s backdrop is part of the element, so\n   * a click outside .ef-spin (the actual card) but inside the dialog is a\n   * backdrop click \u2014 comparing against the card's own box is what tells\n   * them apart. */\n  dlg.addEventListener('click', function (e) {\n    var box = dlg.querySelector('.ef-spin');\n    if (!box) return;\n    var r = box.getBoundingClientRect();\n    var inside = e.clientX >= r.left && e.clientX <= r.right &&\n                 e.clientY >= r.top && e.clientY <= r.bottom;\n    if (!inside) close();\n  });\n\n  document.addEventListener('ef-spin-claimed', remember);\n\n  /* The Merdeka offer dialogue's own \"Spin & Save\" button (promo.py's\n   * popup(), data-ef-spin-open) opens THIS dialog instead of linking to a\n   * page that doesn't exist. Delegated on document, not queried once at\n   * load, because that button lives in a different dialogue this file\n   * knows nothing else about. Re-checks done() at click time rather than\n   * trusting whatever it was at page load \u2014 a second, independent entry\n   * point into \"open the wheel\" is exactly the kind of place a stale check\n   * would quietly let someone spin twice. */\n  document.addEventListener('click', function (e) {\n    var opener = e.target.closest && e.target.closest('[data-ef-spin-open]');\n    if (!opener) return;\n    if (done()) return;\n    var other = opener.closest('dialog');\n    if (other && other !== dlg && other.open) other.close();\n    if (!dlg.open) dlg.showModal();\n  });\n\n  if (done()) return;\n\n  /* A beat after load, same as the promodal \u2014 opening during first paint\n   * fights the page for attention and reads as an error. */\n  /* 11.11: queue behind any open dialog (the memo opens on every landing)\n     rather than stacking on top of it. */\n  /* Owner, 04NOV2026 (phone audit): once per VISIT -- closed, it stays closed until the browser\n     is closed; a spin still holds for good through done(), and every [data-ef-spin-open]\n     button still opens it on purpose. (9.9's rule was every page load until spun.) */\n  var VISIT = KEY + '-visit';\n  function seenThisVisit() { try { return sessionStorage.getItem(VISIT) === '1'; } catch (e) { return false; } }\n  dlg.addEventListener('close', function () { try { sessionStorage.setItem(VISIT, '1'); } catch (e) {} });\n  function others() { return Array.prototype.some.call(document.querySelectorAll('dialog[open]'),\n    function (d) { return d !== dlg; }); }\n  function later() {\n    if (dlg.open || done() || seenThisVisit()) return;\n    if (others()) { window.setTimeout(later, 600); return; }\n    dlg.showModal();\n  }\n  window.setTimeout(later, 1500);\n})();\n"],
  "mappings": "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",
  "names": ["n", "angle"]
}
