diff --git a/docs/script.js b/docs/script.js index c6d2034..1639a2a 100644 --- a/docs/script.js +++ b/docs/script.js @@ -2,11 +2,13 @@ Core-Monitor site behaviour Everything here is a progressive enhancement: the page is fully readable, - navigable and usable with this file blocked or broken. Notably the - scroll reveal animations are pure CSS now, so no content can ever be left - stuck at opacity 0 by a JavaScript failure. + navigable and usable with this file blocked or broken. Nothing here is + load-bearing, so no content can ever be left hidden by a JavaScript + failure. - No third-party libraries and no network requests. + No third-party libraries. The one network request is a cached lookup of + the repository's star count, and the page already ships a correct number + for it. ========================================================================== */ (function () { @@ -180,6 +182,81 @@ }); }); + /* ---------- live github star count ---------- + The count is already in the markup, so this only refreshes it. If the + request fails, GitHub rate limits us, or this file never runs, the + number that shipped with the page stays and nothing moves. Answers are + kept for six hours, so a browsing session costs at most one request. */ + + (function () { + var targets = doc.querySelectorAll("[data-star-count]"); + if (!targets.length || !window.fetch) return; + + var ENDPOINT = "https://api.github.com/repos/offyotto/Core-Monitor"; + var KEY = "core-monitor:stars"; + var MAX_AGE = 6 * 60 * 60 * 1000; + + var recall = function () { + try { + return window.localStorage.getItem(KEY); + } catch (error) { + // Private windows and blocked storage both throw here. + return null; + } + }; + + var remember = function (value) { + try { + window.localStorage.setItem(KEY, value); + } catch (error) { + // Caching is an optimisation; carry on without it. + } + }; + + var abbreviate = function (count) { + if (count < 1000) return String(count); + var thousands = count / 1000; + return ( + (thousands >= 10 + ? Math.round(thousands) + : Math.round(thousands * 10) / 10) + "k" + ); + }; + + var paint = function (count) { + if (typeof count !== "number" || !isFinite(count) || count < 0) return; + Array.prototype.slice.call(targets).forEach(function (target) { + target.textContent = abbreviate(count); + }); + }; + + var stored = recall(); + + if (stored) { + var halves = stored.split(":"); + var checkedAt = parseInt(halves[0], 10); + var lastCount = parseInt(halves[1], 10); + if (isFinite(checkedAt) && isFinite(lastCount)) { + paint(lastCount); + if (Date.now() - checkedAt < MAX_AGE) return; + } + } + + window + .fetch(ENDPOINT, { headers: { Accept: "application/vnd.github+json" } }) + .then(function (response) { + return response.ok ? response.json() : null; + }) + .then(function (data) { + if (!data || typeof data.stargazers_count !== "number") return; + paint(data.stargazers_count); + remember(Date.now() + ":" + data.stargazers_count); + }) + .catch(function () { + // Offline, rate limited or blocked. The shipped number stands. + }); + })(); + /* ---------- screenshot lightbox ---------- The gallery tiles are ordinary links to the full-size image, so with this disabled they still open the screenshot. When is