48 lines (43 loc) · 1.58 KB
 1import { bindThemeControls, initTheme } from "./theme.js";
 2import { decodeCloudflareEmails } from "./email.js";
 3import { initPhotoPreview } from "./photo-preview.js";
 4import { initCards } from "./cards.js";
 5import { initToc } from "./toc.js";
 6import { initCopy, initFeedCopy } from "./copy.js";
 7import { initSrcView } from "./src-view.js";
 8import { initClouds } from "./clouds.js";
 9import { navigate, startRouter } from "./router.js";
10
11// Per-page wiring, re-runnable after every body swap. A fresh AbortController
12// each call tears down the previous page's listeners in one shot, so nothing
13// stacks across navigations.
14let pageController;
15
16const initPage = () => {
17  pageController?.abort();
18  pageController = new AbortController();
19  const { signal } = pageController;
20
21  initTheme();
22  decodeCloudflareEmails();
23  initPhotoPreview(signal);
24  initCards(signal, navigate);
25  initToc(signal);
26  initCopy(signal);
27  initFeedCopy(signal);
28  initSrcView(signal);
29  initClouds(signal);
30};
31
32// .menu-panel lives outside .site-header-outer (see menu-panel.html) and
33// isn't touched by the router's swap, so a session-level delegated listener
34// is enough - same reasoning as bindThemeControls().
35const bindMenuClose = () => {
36  document.addEventListener("click", (e) => {
37    if (!e.target.closest("[data-menu-close]")) return;
38    const details = document.querySelector(".menu-mobile");
39    if (details) details.open = false;
40  });
41};
42
43document.addEventListener("DOMContentLoaded", () => {
44  bindThemeControls();
45  bindMenuClose();
46  startRouter(initPage); // the router calls initPage() after every swap
47  initPage();
48});