1/* ---------------------------------------------------------------------------- 2 * app.js â boot the site 3 * 4 * Flow: resolve the restaurant key -> load SIPO data -> build one clean 5 * model -> apply the theme -> render. If the cache was stale, fresh data 6 * arrives a moment later and the page seamlessly re-renders. 7 * -------------------------------------------------------------------------- */ 8 9import { resolveOnlineKey, loadRestaurant } from "./api.js?v=22"; 10import { buildModel } from "./state.js?v=22"; 11import { applyTheme } from "./theme.js?v=22"; 12import { renderSite } from "./render.js?v=22"; 13 14// Build stamp â index.html's self-heal guard compares this to window.__BUILD__ 15// to detect a stale cached module and force a one-time cache-busting reload. 16window.__APP_BUILD__ = "22"; 17 18const ICONS = { 19 offline: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 1l22 22M16.7 16.7A6 6 0 0 0 12 15a6 6 0 0 0-4.7 1.7M5 12.5a10 10 0 0 1 3-2M19 12.5a10 10 0 0 0-5-2.6M2 8.8A16 16 0 0 1 8 5.6M22 8.8a16 16 0 0 0-6-3.2"/><circle cx="12" cy="20" r="1"/></svg>', 20 notfound: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3M8 11h6"/></svg>', 21 error: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z"/><path d="M12 9v4M12 17h.01"/></svg>', 22 config: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.6 1.6 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.6 1.6 0 0 0-2.7 1.1V21a2 2 0 1 1-4 0v-.1A1.6 1.6 0 0 0 7 19.4a1.6 1.6 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1A1.6 1.6 0 0 0 3 14a2 2 0 1 1 0-4 1.6 1.6 0 0 0 1.5-2.6 2 2 0 1 1 2.8-2.8 1.6 1.6 0 0 0 2.7-1.1V3a2 2 0 1 1 4 0 1.6 1.6 0 0 0 2.6 1.5 2 2 0 1 1 2.8 2.8A1.6 1.6 0 0 0 21 10a2 2 0 1 1 0 4Z"/></svg>' 23}; 24const COPY = { 25 offline: ["You're offline", "We can't reach the internet right now. Check your connection and try again."], 26 notfound: ["Restaurant not found", "We couldn't find a restaurant for this key. Please verify it and try again."], 27 config: ["Not configured yet", "No restaurant key is set. Open config.js and set ONLINE_KEY."], 28 error: ["Something went wrong", "We hit an unexpected problem loading this restaurant. Please try again."] 29}; 30 31function fatal(type, detail) { 32 const [title, msg] = COPY[type] || COPY.error; 33 document.getElementById("fatal-icon").innerHTML = ICONS[type] || ICONS.error; 34 document.getElementById("fatal-title").textContent = title; 35 document.getElementById("fatal-msg").textContent = detail || msg; 36 document.getElementById("boot").style.display = "none"; 37 document.getElementById("fatal").hidden = false; 38} 39 40function paint(payload) { 41 const model = buildModel(payload); 42 applyTheme(model); 43 renderSite(model); 44 // Smooth-scroll for in-page anchors (non-critical â never let it fail paint). 45 try { 46 document.querySelectorAll('a[href^="#"]').forEach((a) => 47 a.addEventListener("click", (e) => { 48 const id = a.getAttribute("href").slice(1); 49 const t = id && document.getElementById(id); 50 if (t) { e.preventDefault(); t.scrollIntoView({ behavior: "smooth", block: "start" }); 51 document.getElementById("mnav") && document.getElementById("mnav").classList.remove("open"); } 52 })); 53 } catch (e) { console.error("[anchors]", e); } 54 return model; 55} 56 57function classify(err) { 58 if (typeof navigator !== "undefined" && navigator.onLine === false) return "offline"; 59 const m = (err && err.message) || ""; 60 if (err && err.name === "TypeError") return "offline"; // fetch failed 61 if (/invalid restaurant key|no data/i.test(m)) return "notfound"; 62 return "error"; 63} 64 65async function boot() { 66 const key = resolveOnlineKey(); 67 if (!key) return fatal("config"); 68 69 try { 70 const data = await loadRestaurant(key, (fresh) => { 71 try { paint(fresh); } catch (e) { console.error(e); } 72 }); 73 paint(data); 74 } catch (err) { 75 console.error(err); 76 // Also surface on the visible diagnostic bar (handled errors don't 77 // fire window.onerror), so the real cause is always on screen. 78 if (window.__diag) window.__diag("Load error", (err && (err.stack || err.message)) || String(err)); 79 fatal(classify(err), err && err.message ? "Details: " + err.message : null); 80 } 81} 82 83// If the connection drops while browsing, let the user know on next action. 84window.addEventListener("offline", () => { 85 if (!document.body.classList.contains("ready")) fatal("offline"); 86}); 87 88boot();
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.