PageSourceSearch

https://www.repeaterbook.com/assets/js/theme.js?v=1782235683

js repeaterbook.com collected 2026-10-01 11:29:43 UTC 6,523 bytes, 240 lines download raw bytes

1(function () {
2  var STORAGE_KEY = 'rbTheme';
3  var MEDIA_QUERY = '(prefers-color-scheme: dark)';
4  var THEMES = { light: true, dark: true, auto: true };
5  var root = document.documentElement;
6  var media = window.matchMedia ? window.matchMedia(MEDIA_QUERY) : null;
7  var sessionPreference = 'auto';
8  var mediaListenerAttached = false;
9  var mediaChangeHandler = null;
10  var config = window.RBThemeConfig || {};
11  var canUseThemes = config.canUseThemes === true;
12
13  function readStoredTheme() {
14    try {
15      var value = window.localStorage.getItem(STORAGE_KEY);
16      if (THEMES[value]) {
17        return value;
18      }
19    } catch (error) {
20      return null;
21    }
22
23    return null;
24  }
25
26  function getSystemTheme() {
27    return media && media.matches ? 'dark' : 'light';
28  }
29
30  function getResolvedTheme(preference) {
31    if (!canUseThemes) {
32      return 'light';
33    }
34
35    return preference === 'auto' ? getSystemTheme() : preference;
36  }
37
38  function applyResolvedTheme(theme) {
39    if (root.dataset.bsTheme === theme) {
40      return false;
41    }
42
43    root.dataset.bsTheme = theme;
44    return true;
45  }
46
47  function syncControls(preference) {
48    var resolvedTheme = getResolvedTheme(preference);
49    var pickers = document.querySelectorAll('[data-rb-theme-picker]');
50    var label = preference === 'light' ? 'Light' : (preference === 'dark' ? 'Dark' : 'Auto');
51    var iconClass = preference === 'light'
52      ? 'bi bi-sun-fill'
53      : (preference === 'dark' ? 'bi bi-moon-stars-fill' : 'bi bi-circle-half');
54
55    pickers.forEach(function (picker) {
56      var current = picker.querySelector('[data-rb-theme-current]');
57      var icon = picker.querySelector('[data-rb-theme-icon]');
58      var toggle = picker.querySelector('[data-rb-theme-toggle]');
59      var options = picker.querySelectorAll('[data-rb-theme-value]');
60
61      if (current) {
62        current.textContent = label;
63      }
64
65      if (icon) {
66        icon.className = iconClass;
67      }
68
69      if (toggle) {
70        toggle.setAttribute('aria-label', 'Color theme: ' + label);
71        toggle.dataset.rbThemeActive = preference;
72        toggle.dataset.rbThemeResolved = resolvedTheme;
73      }
74
75      options.forEach(function (option) {
76        var isActive = option.dataset.rbThemeValue === preference;
77        option.setAttribute('aria-checked', isActive ? 'true' : 'false');
78        option.classList.toggle('active', isActive);
79
80        if (isActive) {
81          option.setAttribute('aria-current', 'true');
82        } else {
83          option.removeAttribute('aria-current');
84        }
85      });
86    });
87  }
88
89  function dispatchThemeChange(preference, resolvedTheme) {
90    if (typeof window.CustomEvent !== 'function') {
91      return;
92    }
93
94    window.dispatchEvent(new CustomEvent('rb:themechange', {
95      detail: {
96        canUseThemes: canUseThemes,
97        preference: preference,
98        theme: resolvedTheme
99      }
100    }));
101  }
102
103  function persistTheme(preference) {
104    try {
105      window.localStorage.setItem(STORAGE_KEY, preference);
106    } catch (error) {
107      return false;
108    }
109
110    return true;
111  }
112
113  function detachMediaListener() {
114    if (!media || !mediaListenerAttached) {
115      return;
116    }
117
118    if (typeof media.removeEventListener === 'function') {
119      media.removeEventListener('change', mediaChangeHandler);
120    } else if (typeof media.removeListener === 'function') {
121      media.removeListener(mediaChangeHandler);
122    }
123
124    mediaListenerAttached = false;
125  }
126
127  function attachMediaListener() {
128    if (!media || mediaListenerAttached || !canUseThemes) {
129      return;
130    }
131
132    if (typeof media.addEventListener === 'function') {
133      media.addEventListener('change', mediaChangeHandler);
134    } else if (typeof media.addListener === 'function') {
135      media.addListener(mediaChangeHandler);
136    }
137
138    mediaListenerAttached = true;
139  }
140
141  function updateMediaListener(preference) {
142    if (canUseThemes && preference === 'auto') {
143      attachMediaListener();
144    } else {
145      detachMediaListener();
146    }
147  }
148
149  function getPreference() {
150    if (!canUseThemes) {
151      return 'light';
152    }
153
154    return sessionPreference;
155  }
156
157  function setPreference(preference, persist) {
158    var nextPreference = canUseThemes && THEMES[preference] ? preference : 'light';
159    var resolvedTheme = getResolvedTheme(nextPreference);
160    var didApply = applyResolvedTheme(resolvedTheme);
161
162    sessionPreference = nextPreference;
163
164    if (persist && canUseThemes) {
165      persistTheme(nextPreference);
166    }
167
168    updateMediaListener(nextPreference);
169    syncControls(nextPreference);
170
171    if (didApply) {
172      dispatchThemeChange(nextPreference, resolvedTheme);
173    }
174  }
175
176  function initializeTheme() {
177    var storedTheme = readStoredTheme();
178    var preference = canUseThemes ? (storedTheme || 'auto') : 'light';
179    var currentApplied = root.dataset.bsTheme;
180    var resolvedTheme = getResolvedTheme(preference);
181
182    sessionPreference = preference;
183
184    if (currentApplied !== resolvedTheme) {
185      applyResolvedTheme(resolvedTheme);
186    }
187
188    updateMediaListener(preference);
189    syncControls(preference);
190  }
191
192  function handleMediaChange() {
193    if (!canUseThemes || getPreference() !== 'auto') {
194      return;
195    }
196
197    var resolvedTheme = getResolvedTheme('auto');
198    if (applyResolvedTheme(resolvedTheme)) {
199      dispatchThemeChange('auto', resolvedTheme);
200    }
201
202    syncControls('auto');
203  }
204
205  function bindPicker(picker) {
206    var options = picker.querySelectorAll('[data-rb-theme-value]');
207
208    options.forEach(function (option) {
209      option.addEventListener('click', function () {
210        setPreference(option.dataset.rbThemeValue, true);
211      });
212    });
213  }
214
215  mediaChangeHandler = handleMediaChange;
216
217  initializeTheme();
218
219  document.addEventListener('DOMContentLoaded', function () {
220    document.querySelectorAll('[data-rb-theme-picker]').forEach(bindPicker);
221    syncControls(getPreference());
222  });
223
224  window.RBTheme = {
225    canUseThemes: function () {
226      return canUseThemes;
227    },
228    get: function () {
229      return getPreference();
230    },
231    set: function (theme) {
232      setPreference(theme, true);
233      return getPreference();
234    },
235    sync: function () {
236      syncControls(getPreference());
237      return getPreference();
238    }
239  };
240})();

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.