PageSourceSearch

https://chronicles.igmsu.org/assets/dark-mode.js

js igmsu.org collected 2026-10-02 17:58:39 UTC 3,379 bytes, 112 lines download raw bytes

1/*
2 * Dark-mode switch for the restored Chronicles site.
3 *
4 * Added during restoration; not part of the original theme.
5 *
6 * Behaviour:
7 *   - With no stored choice, the system setting decides, and the page follows
8 *     it live if the visitor changes it.
9 *   - Clicking the switch stores an explicit choice, which then wins over the
10 *     system setting until it is cleared.
11 *
12 * The attribute itself is set by a small inline script in <head>, so the theme
13 * is correct before first paint. This file only draws the control and keeps it
14 * in sync.
15 */
16(function () {
17  "use strict";
18
19  var KEY = "chronicles-theme";
20  var root = document.documentElement;
21
22  var SUN =
23    '<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="4.2"/>' +
24    '<path d="M12 2.6v2.2M12 19.2v2.2M2.6 12h2.2M19.2 12h2.2' +
25    'M5.4 5.4l1.6 1.6M17 17l1.6 1.6M18.6 5.4L17 7M7 17l-1.6 1.6"/></svg>';
26
27  var MOON =
28    '<svg viewBox="0 0 24 24" aria-hidden="true">' +
29    '<path d="M20.5 14.2A8.4 8.4 0 1 1 9.8 3.5a6.6 6.6 0 0 0 10.7 10.7z"/></svg>';
30
31  function stored() {
32    try {
33      var v = localStorage.getItem(KEY);
34      return v === "dark" || v === "light" ? v : null;
35    } catch (e) {
36      return null; // private mode, or storage disabled
37    }
38  }
39
40  function systemPrefersDark() {
41    return (
42      window.matchMedia &&
43      window.matchMedia("(prefers-color-scheme: dark)").matches
44    );
45  }
46
47  function current() {
48    return root.getAttribute("data-theme") === "dark" ? "dark" : "light";
49  }
50
51  function apply(theme) {
52    root.setAttribute("data-theme", theme);
53    render(theme);
54  }
55
56  var button = null;
57
58  function render(theme) {
59    if (!button) return;
60    var dark = theme === "dark";
61    // Label what the click will do, not what is showing.
62    var label = dark ? "Включить светлую тему" : "Включить тёмную тему";
63    button.innerHTML = dark ? SUN : MOON;
64    button.setAttribute("aria-label", label);
65    button.setAttribute("title", label);
66    button.setAttribute("aria-pressed", dark ? "true" : "false");
67  }
68
69  function build() {
70    button = document.createElement("button");
71    button.id = "theme-toggle";
72    button.type = "button";
73    button.addEventListener("click", function () {
74      var next = current() === "dark" ? "light" : "dark";
75      try {
76        localStorage.setItem(KEY, next);
77      } catch (e) {
78        /* choice simply will not persist */
79      }
80      apply(next);
81    });
82    document.body.appendChild(button);
83    render(current());
84  }
85
86  // Follow the system setting for as long as no explicit choice is stored.
87  if (window.matchMedia) {
88    var mq = window.matchMedia("(prefers-color-scheme: dark)");
89    var onChange = function (e) {
90      if (!stored()) apply(e.matches ? "dark" : "light");
91    };
92    if (mq.addEventListener) mq.addEventListener("change", onChange);
93    else if (mq.addListener) mq.addListener(onChange); // older browsers
94  }
95
96  if (document.readyState === "loading") {
97    document.addEventListener("DOMContentLoaded", build);
98  } else {
99    build();
100  }
101
102  // Exposed so the stored choice can be cleared from the console:
103  //   chroniclesTheme.reset()
104  window.chroniclesTheme = {
105    reset: function () {
106      try {
107        localStorage.removeItem(KEY);
108      } catch (e) {}
109      apply(systemPrefersDark() ? "dark" : "light");
110    }
111  };
112})();

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.