]> git.proxmox.com Git - rustc.git/blob - src/librustdoc/html/static/js/settings.js
New upstream version 1.76.0+dfsg1
[rustc.git] / src / librustdoc / html / static / js / settings.js
1 // Local js definitions:
2 /* global getSettingValue, updateLocalStorage, updateTheme */
3 /* global addClass, removeClass, onEach, onEachLazy, blurHandler */
4 /* global MAIN_ID, getVar, getSettingsButton */
5
6 "use strict";
7
8 (function() {
9 const isSettingsPage = window.location.pathname.endsWith("/settings.html");
10
11 function changeSetting(settingName, value) {
12 if (settingName === "theme") {
13 const useSystem = value === "system preference" ? "true" : "false";
14 updateLocalStorage("use-system-theme", useSystem);
15 }
16 updateLocalStorage(settingName, value);
17
18 switch (settingName) {
19 case "theme":
20 case "preferred-dark-theme":
21 case "preferred-light-theme":
22 updateTheme();
23 updateLightAndDark();
24 break;
25 case "line-numbers":
26 if (value === true) {
27 window.rustdoc_add_line_numbers_to_examples();
28 } else {
29 window.rustdoc_remove_line_numbers_from_examples();
30 }
31 break;
32 case "hide-sidebar":
33 if (value === true) {
34 addClass(document.documentElement, "hide-sidebar");
35 } else {
36 removeClass(document.documentElement, "hide-sidebar");
37 }
38 break;
39 }
40 }
41
42 function showLightAndDark() {
43 removeClass(document.getElementById("preferred-light-theme"), "hidden");
44 removeClass(document.getElementById("preferred-dark-theme"), "hidden");
45 }
46
47 function hideLightAndDark() {
48 addClass(document.getElementById("preferred-light-theme"), "hidden");
49 addClass(document.getElementById("preferred-dark-theme"), "hidden");
50 }
51
52 function updateLightAndDark() {
53 const useSystem = getSettingValue("use-system-theme");
54 if (useSystem === "true" || (useSystem === null && getSettingValue("theme") === null)) {
55 showLightAndDark();
56 } else {
57 hideLightAndDark();
58 }
59 }
60
61 function setEvents(settingsElement) {
62 updateLightAndDark();
63 onEachLazy(settingsElement.querySelectorAll("input[type=\"checkbox\"]"), toggle => {
64 const settingId = toggle.id;
65 const settingValue = getSettingValue(settingId);
66 if (settingValue !== null) {
67 toggle.checked = settingValue === "true";
68 }
69 toggle.onchange = () => {
70 changeSetting(toggle.id, toggle.checked);
71 };
72 });
73 onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"), elem => {
74 const settingId = elem.name;
75 let settingValue = getSettingValue(settingId);
76 if (settingId === "theme") {
77 const useSystem = getSettingValue("use-system-theme");
78 if (useSystem === "true" || settingValue === null) {
79 // "light" is the default theme
80 settingValue = useSystem === "false" ? "light" : "system preference";
81 }
82 }
83 if (settingValue !== null && settingValue !== "null") {
84 elem.checked = settingValue === elem.value;
85 }
86 elem.addEventListener("change", ev => {
87 changeSetting(ev.target.name, ev.target.value);
88 });
89 });
90 }
91
92 /**
93 * This function builds the sections inside the "settings page". It takes a `settings` list
94 * as argument which describes each setting and how to render it. It returns a string
95 * representing the raw HTML.
96 *
97 * @param {Array<Object>} settings
98 *
99 * @return {string}
100 */
101 function buildSettingsPageSections(settings) {
102 let output = "";
103
104 for (const setting of settings) {
105 const js_data_name = setting["js_name"];
106 const setting_name = setting["name"];
107
108 if (setting["options"] !== undefined) {
109 // This is a select setting.
110 output += `\
111 <div class="setting-line" id="${js_data_name}">
112 <div class="setting-radio-name">${setting_name}</div>
113 <div class="setting-radio-choices">`;
114 onEach(setting["options"], option => {
115 const checked = option === setting["default"] ? " checked" : "";
116 const full = `${js_data_name}-${option.replace(/ /g,"-")}`;
117
118 output += `\
119 <label for="${full}" class="setting-radio">
120 <input type="radio" name="${js_data_name}"
121 id="${full}" value="${option}"${checked}>
122 <span>${option}</span>
123 </label>`;
124 });
125 output += `\
126 </div>
127 </div>`;
128 } else {
129 // This is a checkbox toggle.
130 const checked = setting["default"] === true ? " checked" : "";
131 output += `\
132 <div class="setting-line">\
133 <label class="setting-check">\
134 <input type="checkbox" id="${js_data_name}"${checked}>\
135 <span>${setting_name}</span>\
136 </label>\
137 </div>`;
138 }
139 }
140 return output;
141 }
142
143 /**
144 * This function builds the "settings page" and returns the generated HTML element.
145 *
146 * @return {HTMLElement}
147 */
148 function buildSettingsPage() {
149 const theme_names = getVar("themes").split(",").filter(t => t);
150 theme_names.push("light", "dark", "ayu");
151
152 const settings = [
153 {
154 "name": "Theme",
155 "js_name": "theme",
156 "default": "system preference",
157 "options": theme_names.concat("system preference"),
158 },
159 {
160 "name": "Preferred light theme",
161 "js_name": "preferred-light-theme",
162 "default": "light",
163 "options": theme_names,
164 },
165 {
166 "name": "Preferred dark theme",
167 "js_name": "preferred-dark-theme",
168 "default": "dark",
169 "options": theme_names,
170 },
171 {
172 "name": "Auto-hide item contents for large items",
173 "js_name": "auto-hide-large-items",
174 "default": true,
175 },
176 {
177 "name": "Auto-hide item methods' documentation",
178 "js_name": "auto-hide-method-docs",
179 "default": false,
180 },
181 {
182 "name": "Auto-hide trait implementation documentation",
183 "js_name": "auto-hide-trait-implementations",
184 "default": false,
185 },
186 {
187 "name": "Directly go to item in search if there is only one result",
188 "js_name": "go-to-only-result",
189 "default": false,
190 },
191 {
192 "name": "Show line numbers on code examples",
193 "js_name": "line-numbers",
194 "default": false,
195 },
196 {
197 "name": "Hide persistent navigation bar",
198 "js_name": "hide-sidebar",
199 "default": false,
200 },
201 {
202 "name": "Disable keyboard shortcuts",
203 "js_name": "disable-shortcuts",
204 "default": false,
205 },
206 ];
207
208 // Then we build the DOM.
209 const elementKind = isSettingsPage ? "section" : "div";
210 const innerHTML = `<div class="settings">${buildSettingsPageSections(settings)}</div>`;
211 const el = document.createElement(elementKind);
212 el.id = "settings";
213 if (!isSettingsPage) {
214 el.className = "popover";
215 }
216 el.innerHTML = innerHTML;
217
218 if (isSettingsPage) {
219 document.getElementById(MAIN_ID).appendChild(el);
220 } else {
221 el.setAttribute("tabindex", "-1");
222 getSettingsButton().appendChild(el);
223 }
224 return el;
225 }
226
227 const settingsMenu = buildSettingsPage();
228
229 function displaySettings() {
230 settingsMenu.style.display = "";
231 onEachLazy(settingsMenu.querySelectorAll("input[type='checkbox']"), el => {
232 const val = getSettingValue(el.id);
233 const checked = val === "true";
234 if (checked !== el.checked && val !== null) {
235 el.checked = checked;
236 }
237 });
238 }
239
240 function settingsBlurHandler(event) {
241 blurHandler(event, getSettingsButton(), window.hidePopoverMenus);
242 }
243
244 if (isSettingsPage) {
245 // We replace the existing "onclick" callback to do nothing if clicked.
246 getSettingsButton().onclick = event => {
247 event.preventDefault();
248 };
249 } else {
250 // We replace the existing "onclick" callback.
251 const settingsButton = getSettingsButton();
252 const settingsMenu = document.getElementById("settings");
253 settingsButton.onclick = event => {
254 if (settingsMenu.contains(event.target)) {
255 return;
256 }
257 event.preventDefault();
258 const shouldDisplaySettings = settingsMenu.style.display === "none";
259
260 window.hideAllModals();
261 if (shouldDisplaySettings) {
262 displaySettings();
263 }
264 };
265 settingsButton.onblur = settingsBlurHandler;
266 settingsButton.querySelector("a").onblur = settingsBlurHandler;
267 onEachLazy(settingsMenu.querySelectorAll("input"), el => {
268 el.onblur = settingsBlurHandler;
269 });
270 settingsMenu.onblur = settingsBlurHandler;
271 }
272
273 // We now wait a bit for the web browser to end re-computing the DOM...
274 setTimeout(() => {
275 setEvents(settingsMenu);
276 // The setting menu is already displayed if we're on the settings page.
277 if (!isSettingsPage) {
278 displaySettings();
279 }
280 removeClass(getSettingsButton(), "rotate");
281 }, 0);
282 })();