PageSourceSearch

https://home.funkystadium.app/shared/version-poll.js?v=136-1042

js funkystadium.app collected 2026-10-05 08:47:10 UTC 7,553 bytes, 128 lines download raw bytes

1// AUTO-GENERATED — DO NOT EDIT
2// Source: shared/version-poll.js
3// Regenerated by scripts/sync-workshop-modules.mjs (apps/home Hosting target — workshop reception console).
4// Hand-edits to this file will be overwritten on the next deploy.
5
6/* ── SHIROFES OS — Standalone version-polling + hard-refresh helper ─────────
7 *
8 * v135.105 — extracted from shared/state.js so pages that don't load state.js
9 * (the "exception pages" — post-event, mc, mc-script, cast, crew, portal,
10 * register, register-cast, reflect, debrief, timetable, rundown, index) get
11 * stale-tab detection consistent with admin/staff/kiosk/etc.
12 *
13 * Pre-v135.105 only `state.js`-loading pages + dj.html + live.html (rolled
14 * their own inline pollers) had the toast. mc-script.html during a live
15 * SHIROFES set could sit on stale code if the operator left a tab open
16 * across a hotfix deploy. This module closes that gap without forcing
17 * `state.js` onto pages that intentionally don't carry it (cf. EXECUTION_
18 * STATE.md "Exception-page architecture variance is intentional").
19 *
20 * Contract:
21 *   - Idempotent: `window.__versionPollActive` guards re-entry. State.js's
22 *     `setupVersionPolling` (still present for back-compat) checks the same
23 *     flag and no-ops when this module has already booted.
24 *   - Self-bootstraps: first poll seeds the local-version baseline from
25 *     `window.SHIROFES_APP_VERSION` if present (set by firebase-config.js
26 *     on script-tag-loaded pages), else from the first version.json read
27 *     itself. Subsequent polls compare against the seeded baseline.
28 *     This makes the module work uniformly on script-tag pages (admin/
29 *     staff/etc.) AND on ES-module pages (mc.html) that don't load
30 *     firebase-config.js synchronously.
31 *   - Polls `version.json` 30s after load, then every 5min.
32 *   - Banner is identical to state.js's (same id `versionUpdateBanner`, same
33 *     hard-refresh CTA) so operators see one consistent UI across all pages.
34 *
35 * Hard-refresh exposed at `window.__shirofesHardRefresh` — pages that already
36 * defined it (state.js) keep theirs; this module's definition only attaches
37 * if absent.
38 * ──────────────────────────────────────────────────────────────────────── */
39
40(function () {
41    'use strict';
42
43    if (typeof window === 'undefined') return;
44
45    // Idempotent guard — if state.js or another instance already booted the
46    // poller, don't double-schedule. Both share the same banner element id
47    // so visual state stays consistent.
48    if (window.__versionPollActive) return;
49    window.__versionPollActive = true;
50
51    var POLL_INTERVAL = 5 * 60 * 1000; // 5 minutes
52    var FIRST_CHECK_DELAY = 30 * 1000;  // 30 seconds (let page settle)
53    var _versionToastShown = false;
54    var _baselineVersion = (window.SHIROFES_APP_VERSION || '').toString() || null;
55
56    function checkVersion() {
57        if (_versionToastShown) return; // already prompted, don't spam
58        // Cache-busting query param so the SW's cache-first handler doesn't
59        // serve a stale version.json. cache:'no-store' on the fetch is a
60        // belt-and-suspenders second layer.
61        // v136.23 — the URL is configurable for the isolated apps/home Hosting
62        // target, whose pages live under /workshops/ or /club/ so a relative
63        // './version.json' would miss the one home-root version.json. Those
64        // pages set window.SHIROFES_VERSION_URL = '/version.json'. Root pages
65        // leave it unset → './version.json' as before (backward-compatible).
66        var _versionUrl = (window.SHIROFES_VERSION_URL || './version.json');
67        fetch(_versionUrl + '?_t=' + Date.now(), { cache: 'no-store' })
68            .then(function (r) { return r.ok ? r.json() : null; })
69            .then(function (data) {
70                if (!data || !data.v) return;
71                var fetched = String(data.v);
72                if (!_baselineVersion) {
73                    // First poll on a page that didn't pre-set the baseline
74                    // (ES-module pages without firebase-config.js script tag).
75                    // Seed and exit — drift gets detected on the NEXT poll.
76                    _baselineVersion = fetched;
77                    return;
78                }
79                if (fetched !== _baselineVersion && !_versionToastShown) {
80                    _versionToastShown = true;
81                    showVersionUpdateBanner();
82                }
83            })
84            .catch(function () { /* offline or fetch fail — try again next tick */ });
85    }
86
87    // Hard-refresh: unregister SW + purge Cache Storage before reload so the
88    // user actually gets the new code. Plain location.reload() goes through
89    // the SW's cache-first handler and can serve stale HTML/JS indefinitely.
90    if (typeof window.__shirofesHardRefresh !== 'function') {
91        window.__shirofesHardRefresh = async function hardRefresh() {
92            try {
93                if ('serviceWorker' in navigator) {
94                    var regs = await navigator.serviceWorker.getRegistrations();
95                    await Promise.all(regs.map(function (r) { return r.unregister(); }));
96                }
97                if (typeof caches !== 'undefined' && caches && caches.keys) {
98                    var keys = await caches.keys();
99                    await Promise.all(keys.map(function (k) { return caches.delete(k); }));
100                }
101            } catch (e) { /* best effort — reload anyway */ }
102            // Cache-busting query param forces even Chrome's HTTP disk cache to fetch fresh.
103            var u = new URL(location.href);
104            u.searchParams.set('_rv', Date.now());
105            location.replace(u.toString());
106        };
107    }
108
109    function showVersionUpdateBanner() {
110        // Don't double-render if state.js already did.
111        if (document.getElementById('versionUpdateBanner')) return;
112        var banner = document.createElement('div');
113        banner.id = 'versionUpdateBanner';
114        banner.style.cssText = 'position:fixed;top:0;left:0;right:0;z-index:9999;background:var(--info,#2563eb);color:#fff;text-align:center;padding:0.6rem 1rem;font-size:0.85rem;font-weight:600;display:flex;align-items:center;justify-content:center;gap:0.75rem;box-shadow:0 2px 8px rgba(0,0,0,0.3);';
115        banner.innerHTML = '<span>新しいバージョンがあります / New version available</span>' +
116            '<button onclick="window.__shirofesHardRefresh()" style="background:#fff;color:var(--info,#2563eb);border:none;border-radius:6px;padding:0.3rem 0.8rem;font-weight:700;font-size:0.8rem;cursor:pointer;min-height:32px;">更新 / Refresh</button>' +
117            '<button onclick="this.parentElement.remove()" style="background:transparent;color:#fff;border:1px solid rgba(255,255,255,0.4);border-radius:6px;padding:0.3rem 0.6rem;font-size:0.75rem;cursor:pointer;min-height:32px;">後で / Later</button>';
118        if (document.body) document.body.prepend(banner);
119        else document.addEventListener('DOMContentLoaded', function () { document.body.prepend(banner); });
120    }
121
122    // First check after 30s (let page settle), then every 5 minutes.
123    setTimeout(checkVersion, FIRST_CHECK_DELAY);
124    setInterval(checkVersion, POLL_INTERVAL);
125
126    // Expose for pages that want to force a check on visibility-change etc.
127    window.__shirofesCheckVersion = checkVersion;
128})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.