]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/html/static/js/storage.js
Rollup merge of #92581 - Meziu:armv6k-3ds-target, r=nagisa
[rust.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('rustdoc-' + 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(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(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("rustdoc-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("rustdoc-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         // maybe the user has disabled the setting in the meantime!
191         if (getSettingValue("use-system-theme") !== "false") {
192             var lightTheme = getSettingValue("preferred-light-theme") || "light";
193             var darkTheme = getSettingValue("preferred-dark-theme") || "dark";
194
195             if (mql.matches) {
196                 // prefers a dark theme
197                 switchTheme(window.currentTheme, window.mainTheme, darkTheme, true);
198             } else {
199                 // prefers a light theme, or has no preference
200                 switchTheme(window.currentTheme, window.mainTheme, lightTheme, true);
201             }
202
203             // note: we save the theme so that it doesn't suddenly change when
204             // the user disables "use-system-theme" and reloads the page or
205             // navigates to another page
206         }
207     }
208
209     mql.addListener(handlePreferenceChange);
210
211     return function() {
212         handlePreferenceChange(mql);
213     };
214 })();
215
216 if (getSettingValue("use-system-theme") !== "false" && window.matchMedia) {
217     // update the preferred dark theme if the user is already using a dark theme
218     // See https://github.com/rust-lang/rust/pull/77809#issuecomment-707875732
219     if (getSettingValue("use-system-theme") === null
220         && getSettingValue("preferred-dark-theme") === null
221         && darkThemes.indexOf(localStoredTheme) >= 0) {
222         updateLocalStorage("rustdoc-preferred-dark-theme", localStoredTheme);
223     }
224
225     // call the function to initialize the theme at least once!
226     updateSystemTheme();
227 } else {
228     switchTheme(
229         window.currentTheme,
230         window.mainTheme,
231         getSettingValue("theme") || "light",
232         false
233     );
234 }