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