git / moonbite-network / static/shell.js · raw
/* MoonBite site shell: header scroll state, phone menu, live network pill. External file (CSP: script-src 'self'), no inline handlers. */(function () { "use strict"; var h = document.querySelector("[data-mbs-header]"); if (!h) return; /* header gains its blurred background once the page scrolls */ function onScroll() { h.classList.toggle("is-scrolled", window.scrollY > 8); } onScroll(); window.addEventListener("scroll", onScroll, { passive: true }); /* phone menu */ var btn = h.querySelector("[data-mbs-menu]"), sheet = document.getElementById("mbs-sheet"); function setOpen(open) { if (!btn || !sheet) return; sheet.hidden = !open; btn.setAttribute("aria-expanded", String(open)); btn.textContent = open ? "Close" : "Menu"; h.classList.toggle("is-open", open); document.body.classList.toggle("mbs-locked", open); } if (btn) btn.addEventListener("click", function () { setOpen(sheet.hidden); }); document.addEventListener("keydown", function (e) { if (e.key === "Escape") setOpen(false); }); window.addEventListener("resize", function () { if (window.innerWidth > 900) setOpen(false); }); /* live network pill: before launch, when mining opens (genesis is stamped 2 h after launch); afterwards, the current block height */ var pills = document.querySelectorAll("[data-mbs-status]"); if (!pills.length) return; var CET = { timeZone: "Europe/Paris" }; function opensText(ms) { var d = new Date(ms); return d.toLocaleString("en-GB", Object.assign({ weekday: "short", hour: "2-digit", minute: "2-digit", timeZoneName: "short" }, CET)); } function left(ms) { var s = Math.max(0, Math.round((ms - Date.now()) / 1000)), d = Math.floor(s / 86400), hh = Math.floor(s % 86400 / 3600), m = Math.floor(s % 3600 / 60); return d ? d + "d " + hh + "h" : hh ? hh + "h " + m + "m" : m + "m"; } var state = null; function paint() { if (!state) return; pills.forEach(function (p) { var dot = p.querySelector(".mbs-dot"), txt = p.querySelector("[data-mbs-status-text]"); if (state.launchAt && Date.now() < state.launchAt) { dot.className = "mbs-dot is-soon"; txt.innerHTML = "Mining opens <b>" + opensText(state.launchAt) + "</b> · " + left(state.launchAt); p.title = "Nobody can mine before launch; the rule is in the genesis block."; } else if (state.height === 0) { dot.className = "mbs-dot is-soon"; txt.innerHTML = "Mining open · <b>block 1 unclaimed</b>"; } else { dot.className = "mbs-dot is-live"; txt.innerHTML = "Block <b>#" + state.height + "</b>"; // heights never separated (d11) p.title = "Live height of the MoonBite network"; } }); } function poll() { fetch("/api/blockchain/info", { cache: "no-store" }).then(function (r) { return r.json(); }).then(function (d) { if (!d || d.height === undefined) return; var hgt = Number(d.height); state = { height: hgt, launchAt: (hgt === 0 && d.tip_time) ? (Number(d.tip_time) - 7200) * 1000 : null }; paint(); }).catch(function () {}); } poll(); setInterval(poll, 30000); setInterval(paint, 15000);})();