1 // Local js definitions:
2 /* global getSettingValue, getVirtualKey, updateLocalStorage, updateSystemTheme */
3 /* global addClass, removeClass, onEach, onEachLazy, blurHandler, elemIsInParent */
4 /* global MAIN_ID, getVar, getSettingsButton */
9 const isSettingsPage = window.location.pathname.endsWith("/settings.html");
11 function changeSetting(settingName, value) {
12 if (settingName === "theme") {
13 const useSystem = value === "system preference" ? "true" : "false";
14 updateLocalStorage("use-system-theme", useSystem);
16 updateLocalStorage(settingName, value);
18 switch (settingName) {
20 case "preferred-dark-theme":
21 case "preferred-light-theme":
27 window.rustdoc_add_line_numbers_to_examples();
29 window.rustdoc_remove_line_numbers_from_examples();
35 function handleKey(ev) {
36 // Don't interfere with browser shortcuts
37 if (ev.ctrlKey || ev.altKey || ev.metaKey) {
40 switch (getVirtualKey(ev)) {
44 ev.target.checked = !ev.target.checked;
50 function showLightAndDark() {
51 removeClass(document.getElementById("preferred-light-theme").parentElement, "hidden");
52 removeClass(document.getElementById("preferred-dark-theme").parentElement, "hidden");
55 function hideLightAndDark() {
56 addClass(document.getElementById("preferred-light-theme").parentElement, "hidden");
57 addClass(document.getElementById("preferred-dark-theme").parentElement, "hidden");
60 function updateLightAndDark() {
61 const useSystem = getSettingValue("use-system-theme");
62 if (useSystem === "true" || (useSystem === null && getSettingValue("theme") === null)) {
69 function setEvents(settingsElement) {
71 onEachLazy(settingsElement.querySelectorAll("input[type=\"checkbox\"]"), toggle => {
72 const settingId = toggle.id;
73 const settingValue = getSettingValue(settingId);
74 if (settingValue !== null) {
75 toggle.checked = settingValue === "true";
77 toggle.onchange = function() {
78 changeSetting(this.id, this.checked);
80 toggle.onkeyup = handleKey;
81 toggle.onkeyrelease = handleKey;
83 onEachLazy(settingsElement.getElementsByClassName("select-wrapper"), elem => {
84 const select = elem.getElementsByTagName("select")[0];
85 const settingId = select.id;
86 const settingValue = getSettingValue(settingId);
87 if (settingValue !== null) {
88 select.value = settingValue;
90 select.onchange = function() {
91 changeSetting(this.id, this.value);
94 onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"), elem => {
95 const settingId = elem.name;
96 let settingValue = getSettingValue(settingId);
97 if (settingId === "theme") {
98 const useSystem = getSettingValue("use-system-theme");
99 if (useSystem === "true" || settingValue === null) {
100 if (useSystem !== "false") {
101 settingValue = "system preference";
103 // This is the default theme.
104 settingValue = "light";
108 if (settingValue !== null && settingValue !== "null") {
109 elem.checked = settingValue === elem.value;
111 elem.addEventListener("change", ev => {
112 changeSetting(ev.target.name, ev.target.value);
118 * This function builds the sections inside the "settings page". It takes a `settings` list
119 * as argument which describes each setting and how to render it. It returns a string
120 * representing the raw HTML.
122 * @param {Array<Object>} settings
126 function buildSettingsPageSections(settings) {
129 for (const setting of settings) {
130 output += "<div class=\"setting-line\">";
131 const js_data_name = setting["js_name"];
132 const setting_name = setting["name"];
134 if (setting["options"] !== undefined) {
135 // This is a select setting.
137 <div class="radio-line" id="${js_data_name}">
138 <span class="setting-name">${setting_name}</span>
139 <div class="choices">`;
140 onEach(setting["options"], option => {
141 const checked = option === setting["default"] ? " checked" : "";
142 const full = `${js_data_name}-${option.replace(/ /g,"-")}`;
145 <label for="${full}" class="choice">
146 <input type="radio" name="${js_data_name}"
147 id="${full}" value="${option}"${checked}>
148 <span>${option}</span>
151 output += "</div></div>";
154 const checked = setting["default"] === true ? " checked" : "";
156 <label class="toggle">\
157 <input type="checkbox" id="${js_data_name}"${checked}>\
158 <span class="label">${setting_name}</span>\
167 * This function builds the "settings page" and returns the generated HTML element.
169 * @return {HTMLElement}
171 function buildSettingsPage() {
172 const theme_names = getVar("themes").split(",").filter(t => t);
173 theme_names.push("light", "dark", "ayu");
179 "default": "system preference",
180 "options": theme_names.concat("system preference"),
183 "name": "Preferred light theme",
184 "js_name": "preferred-light-theme",
186 "options": theme_names,
189 "name": "Preferred dark theme",
190 "js_name": "preferred-dark-theme",
192 "options": theme_names,
195 "name": "Auto-hide item contents for large items",
196 "js_name": "auto-hide-large-items",
200 "name": "Auto-hide item methods' documentation",
201 "js_name": "auto-hide-method-docs",
205 "name": "Auto-hide trait implementation documentation",
206 "js_name": "auto-hide-trait-implementations",
210 "name": "Directly go to item in search if there is only one result",
211 "js_name": "go-to-only-result",
215 "name": "Show line numbers on code examples",
216 "js_name": "line-numbers",
220 "name": "Disable keyboard shortcuts",
221 "js_name": "disable-shortcuts",
226 // Then we build the DOM.
227 const elementKind = isSettingsPage ? "section" : "div";
228 const innerHTML = `<div class="settings">${buildSettingsPageSections(settings)}</div>`;
229 const el = document.createElement(elementKind);
231 if (!isSettingsPage) {
232 el.className = "popover";
234 el.innerHTML = innerHTML;
236 if (isSettingsPage) {
237 document.getElementById(MAIN_ID).appendChild(el);
239 el.setAttribute("tabindex", "-1");
240 getSettingsButton().appendChild(el);
245 const settingsMenu = buildSettingsPage();
247 function displaySettings() {
248 settingsMenu.style.display = "";
251 function settingsBlurHandler(event) {
252 blurHandler(event, getSettingsButton(), window.hidePopoverMenus);
255 if (isSettingsPage) {
256 // We replace the existing "onclick" callback to do nothing if clicked.
257 getSettingsButton().onclick = function(event) {
258 event.preventDefault();
261 // We replace the existing "onclick" callback.
262 const settingsButton = getSettingsButton();
263 const settingsMenu = document.getElementById("settings");
264 settingsButton.onclick = function(event) {
265 if (elemIsInParent(event.target, settingsMenu)) {
268 event.preventDefault();
269 const shouldDisplaySettings = settingsMenu.style.display === "none";
271 window.hidePopoverMenus();
272 if (shouldDisplaySettings) {
276 settingsButton.onblur = settingsBlurHandler;
277 settingsButton.querySelector("a").onblur = settingsBlurHandler;
278 onEachLazy(settingsMenu.querySelectorAll("input"), el => {
279 el.onblur = settingsBlurHandler;
281 settingsMenu.onblur = settingsBlurHandler;
284 // We now wait a bit for the web browser to end re-computing the DOM...
286 setEvents(settingsMenu);
287 // The setting menu is already displayed if we're on the settings page.
288 if (!isSettingsPage) {
291 removeClass(getSettingsButton(), "rotate");