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