]> git.proxmox.com Git - rustc.git/blob - src/librustdoc/html/static/js/storage.js
New upstream version 1.60.0+dfsg1
[rustc.git] / src / librustdoc / html / static / js / storage.js
1 var darkThemes = ["dark", "ayu"];
2 window.currentTheme = document.getElementById("themeStyle");
3 window.mainTheme = document.getElementById("mainThemeStyle");
4
5 var settingsDataset = (function () {
6 var settingsElement = document.getElementById("default-settings");
7 if (settingsElement === null) {
8 return null;
9 }
10 var dataset = settingsElement.dataset;
11 if (dataset === undefined) {
12 return null;
13 }
14 return dataset;
15 })();
16
17 function getSettingValue(settingName) {
18 var current = getCurrentValue(settingName);
19 if (current !== null) {
20 return current;
21 }
22 if (settingsDataset !== null) {
23 // See the comment for `default_settings.into_iter()` etc. in
24 // `Options::from_matches` in `librustdoc/config.rs`.
25 var def = settingsDataset[settingName.replace(/-/g,'_')];
26 if (def !== undefined) {
27 return def;
28 }
29 }
30 return null;
31 }
32
33 var localStoredTheme = getSettingValue("theme");
34
35 var savedHref = [];
36
37 // eslint-disable-next-line no-unused-vars
38 function hasClass(elem, className) {
39 return elem && elem.classList && elem.classList.contains(className);
40 }
41
42 // eslint-disable-next-line no-unused-vars
43 function addClass(elem, className) {
44 if (!elem || !elem.classList) {
45 return;
46 }
47 elem.classList.add(className);
48 }
49
50 // eslint-disable-next-line no-unused-vars
51 function removeClass(elem, className) {
52 if (!elem || !elem.classList) {
53 return;
54 }
55 elem.classList.remove(className);
56 }
57
58 /**
59 * Run a callback for every element of an Array.
60 * @param {Array<?>} arr - The array to iterate over
61 * @param {function(?)} func - The callback
62 * @param {boolean} [reversed] - Whether to iterate in reverse
63 */
64 function onEach(arr, func, reversed) {
65 if (arr && arr.length > 0 && func) {
66 var length = arr.length;
67 var i;
68 if (reversed) {
69 for (i = length - 1; i >= 0; --i) {
70 if (func(arr[i])) {
71 return true;
72 }
73 }
74 } else {
75 for (i = 0; i < length; ++i) {
76 if (func(arr[i])) {
77 return true;
78 }
79 }
80 }
81 }
82 return false;
83 }
84
85 /**
86 * Turn an HTMLCollection or a NodeList into an Array, then run a callback
87 * for every element. This is useful because iterating over an HTMLCollection
88 * or a "live" NodeList while modifying it can be very slow.
89 * https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection
90 * https://developer.mozilla.org/en-US/docs/Web/API/NodeList
91 * @param {NodeList<?>|HTMLCollection<?>} lazyArray - An array to iterate over
92 * @param {function(?)} func - The callback
93 * @param {boolean} [reversed] - Whether to iterate in reverse
94 */
95 function onEachLazy(lazyArray, func, reversed) {
96 return onEach(
97 Array.prototype.slice.call(lazyArray),
98 func,
99 reversed);
100 }
101
102 // eslint-disable-next-line no-unused-vars
103 function hasOwnPropertyRustdoc(obj, property) {
104 return Object.prototype.hasOwnProperty.call(obj, property);
105 }
106
107 function updateLocalStorage(name, value) {
108 try {
109 window.localStorage.setItem("rustdoc-" + name, value);
110 } catch(e) {
111 // localStorage is not accessible, do nothing
112 }
113 }
114
115 function getCurrentValue(name) {
116 try {
117 return window.localStorage.getItem("rustdoc-" + name);
118 } catch(e) {
119 return null;
120 }
121 }
122
123 function switchTheme(styleElem, mainStyleElem, newTheme, saveTheme) {
124 var newHref = mainStyleElem.href.replace(
125 /\/rustdoc([^/]*)\.css/, "/" + newTheme + "$1" + ".css");
126
127 // If this new value comes from a system setting or from the previously
128 // saved theme, no need to save it.
129 if (saveTheme) {
130 updateLocalStorage("theme", newTheme);
131 }
132
133 if (styleElem.href === newHref) {
134 return;
135 }
136
137 var found = false;
138 if (savedHref.length === 0) {
139 onEachLazy(document.getElementsByTagName("link"), function(el) {
140 savedHref.push(el.href);
141 });
142 }
143 onEach(savedHref, function(el) {
144 if (el === newHref) {
145 found = true;
146 return true;
147 }
148 });
149 if (found) {
150 styleElem.href = newHref;
151 }
152 }
153
154 // This function is called from "main.js".
155 // eslint-disable-next-line no-unused-vars
156 function useSystemTheme(value) {
157 if (value === undefined) {
158 value = true;
159 }
160
161 updateLocalStorage("use-system-theme", value);
162
163 // update the toggle if we're on the settings page
164 var toggle = document.getElementById("use-system-theme");
165 if (toggle && toggle instanceof HTMLInputElement) {
166 toggle.checked = value;
167 }
168 }
169
170 var updateSystemTheme = (function() {
171 if (!window.matchMedia) {
172 // fallback to the CSS computed value
173 return function() {
174 var cssTheme = getComputedStyle(document.documentElement)
175 .getPropertyValue('content');
176
177 switchTheme(
178 window.currentTheme,
179 window.mainTheme,
180 JSON.parse(cssTheme) || "light",
181 true
182 );
183 };
184 }
185
186 // only listen to (prefers-color-scheme: dark) because light is the default
187 var mql = window.matchMedia("(prefers-color-scheme: dark)");
188
189 function handlePreferenceChange(mql) {
190 let use = function(theme) {
191 switchTheme(window.currentTheme, window.mainTheme, theme, true);
192 };
193 // maybe the user has disabled the setting in the meantime!
194 if (getSettingValue("use-system-theme") !== "false") {
195 var lightTheme = getSettingValue("preferred-light-theme") || "light";
196 var darkTheme = getSettingValue("preferred-dark-theme") || "dark";
197
198 if (mql.matches) {
199 use(darkTheme);
200 } else {
201 // prefers a light theme, or has no preference
202 use(lightTheme);
203 }
204 // note: we save the theme so that it doesn't suddenly change when
205 // the user disables "use-system-theme" and reloads the page or
206 // navigates to another page
207 } else {
208 use(getSettingValue("theme"));
209 }
210 }
211
212 mql.addListener(handlePreferenceChange);
213
214 return function() {
215 handlePreferenceChange(mql);
216 };
217 })();
218
219 function switchToSavedTheme() {
220 switchTheme(
221 window.currentTheme,
222 window.mainTheme,
223 getSettingValue("theme") || "light",
224 false
225 );
226 }
227
228 if (getSettingValue("use-system-theme") !== "false" && window.matchMedia) {
229 // update the preferred dark theme if the user is already using a dark theme
230 // See https://github.com/rust-lang/rust/pull/77809#issuecomment-707875732
231 if (getSettingValue("use-system-theme") === null
232 && getSettingValue("preferred-dark-theme") === null
233 && darkThemes.indexOf(localStoredTheme) >= 0) {
234 updateLocalStorage("preferred-dark-theme", localStoredTheme);
235 }
236
237 // call the function to initialize the theme at least once!
238 updateSystemTheme();
239 } else {
240 switchToSavedTheme();
241 }
242
243 // If we navigate away (for example to a settings page), and then use the back or
244 // forward button to get back to a page, the theme may have changed in the meantime.
245 // But scripts may not be re-loaded in such a case due to the bfcache
246 // (https://web.dev/bfcache/). The "pageshow" event triggers on such navigations.
247 // Use that opportunity to update the theme.
248 // We use a setTimeout with a 0 timeout here to put the change on the event queue.
249 // For some reason, if we try to change the theme while the `pageshow` event is
250 // running, it sometimes fails to take effect. The problem manifests on Chrome,
251 // specifically when talking to a remote website with no caching.
252 window.addEventListener("pageshow", function(ev) {
253 if (ev.persisted) {
254 setTimeout(switchToSavedTheme, 0);
255 }
256 });