| // Local js definitions: |
| /* global getSettingValue, updateLocalStorage, updateTheme */ |
| /* global addClass, removeClass, onEach, onEachLazy */ |
| /* global MAIN_ID, getVar, nonnull */ |
| |
| "use strict"; |
| |
| (function() { |
| const isSettingsPage = window.location.pathname.endsWith("/settings.html"); |
| |
| /** |
| * @overload {"theme"|"preferred-dark-theme"|"preferred-light-theme"} |
| * @param {string} settingName |
| * @param {string} value |
| * @returns |
| * @param {string} settingName |
| * @param {string|boolean} value |
| */ |
| function changeSetting(settingName, value) { |
| if (settingName === "theme") { |
| const useSystem = value === "system preference" ? "true" : "false"; |
| updateLocalStorage("use-system-theme", useSystem); |
| } |
| updateLocalStorage(settingName, "" + value); |
| |
| switch (settingName) { |
| case "theme": |
| case "preferred-dark-theme": |
| case "preferred-light-theme": |
| updateTheme(); |
| updateLightAndDark(); |
| break; |
| case "line-numbers": |
| if (value === true) { |
| const f = window.rustdoc_add_line_numbers_to_examples; |
| if (f !== undefined) { |
| f(); |
| } |
| } else { |
| const f = window.rustdoc_remove_line_numbers_from_examples; |
| if (f !== undefined) { |
| f(); |
| } |
| } |
| break; |
| case "hide-sidebar": |
| if (value === true) { |
| addClass(document.documentElement, "hide-sidebar"); |
| } else { |
| removeClass(document.documentElement, "hide-sidebar"); |
| } |
| break; |
| case "hide-toc": |
| if (value === true) { |
| addClass(document.documentElement, "hide-toc"); |
| } else { |
| removeClass(document.documentElement, "hide-toc"); |
| } |
| break; |
| case "hide-modnav": |
| if (value === true) { |
| addClass(document.documentElement, "hide-modnav"); |
| } else { |
| removeClass(document.documentElement, "hide-modnav"); |
| } |
| break; |
| case "sans-serif-fonts": |
| if (value === true) { |
| addClass(document.documentElement, "sans-serif"); |
| } else { |
| removeClass(document.documentElement, "sans-serif"); |
| } |
| break; |
| case "word-wrap-source-code": |
| if (value === true) { |
| addClass(document.documentElement, "word-wrap-source-code"); |
| } else { |
| removeClass(document.documentElement, "word-wrap-source-code"); |
| } |
| break; |
| } |
| } |
| |
| function showLightAndDark() { |
| removeClass(document.getElementById("preferred-light-theme"), "hidden"); |
| removeClass(document.getElementById("preferred-dark-theme"), "hidden"); |
| } |
| |
| function hideLightAndDark() { |
| addClass(document.getElementById("preferred-light-theme"), "hidden"); |
| addClass(document.getElementById("preferred-dark-theme"), "hidden"); |
| } |
| |
| function updateLightAndDark() { |
| const useSystem = getSettingValue("use-system-theme"); |
| if (useSystem === "true" || (useSystem === null && getSettingValue("theme") === null)) { |
| showLightAndDark(); |
| } else { |
| hideLightAndDark(); |
| } |
| } |
| |
| /** |
| * @param {HTMLElement} settingsElement |
| */ |
| function setEvents(settingsElement) { |
| updateLightAndDark(); |
| onEachLazy(settingsElement.querySelectorAll("input[type=\"checkbox\"]"), toggle => { |
| const settingId = toggle.id; |
| const settingValue = getSettingValue(settingId); |
| if (settingValue !== null) { |
| toggle.checked = settingValue === "true"; |
| } |
| toggle.onchange = () => { |
| changeSetting(toggle.id, toggle.checked); |
| }; |
| }); |
| onEachLazy( |
| settingsElement.querySelectorAll("input[type=\"radio\"]"), |
| /** @param {HTMLInputElement} elem */ |
| elem => { |
| const settingId = elem.name; |
| let settingValue = getSettingValue(settingId); |
| if (settingId === "theme") { |
| const useSystem = getSettingValue("use-system-theme"); |
| if (useSystem === "true" || settingValue === null) { |
| // "light" is the default theme |
| settingValue = useSystem === "false" ? "light" : "system preference"; |
| } |
| } |
| if (settingValue !== null && settingValue !== "null") { |
| elem.checked = settingValue === elem.value; |
| } |
| elem.addEventListener("change", () => { |
| changeSetting(elem.name, elem.value); |
| }); |
| }, |
| ); |
| } |
| |
| /** |
| * This function builds the sections inside the "settings page". It takes a `settings` list |
| * as argument which describes each setting and how to render it. It returns a string |
| * representing the raw HTML. |
| * |
| * @param {Array<rustdoc.Setting>} settings |
| * |
| * @return {string} |
| */ |
| function buildSettingsPageSections(settings) { |
| let output = ""; |
| |
| for (const setting of settings) { |
| const js_data_name = setting["js_name"]; |
| const setting_name = setting["name"]; |
| |
| if (setting["options"] !== undefined) { |
| // This is a select setting. |
| output += `\ |
| <div class="setting-line" id="${js_data_name}"> |
| <div class="setting-radio-name">${setting_name}</div> |
| <div class="setting-radio-choices">`; |
| onEach(setting["options"], option => { |
| const checked = option === setting["default"] ? " checked" : ""; |
| const full = `${js_data_name}-${option.replace(/ /g,"-")}`; |
| |
| output += `\ |
| <label for="${full}" class="setting-radio"> |
| <input type="radio" name="${js_data_name}" |
| id="${full}" value="${option}"${checked}> |
| <span>${option}</span> |
| </label>`; |
| }); |
| output += `\ |
| </div> |
| </div>`; |
| } else { |
| // This is a checkbox toggle. |
| const checked = setting["default"] === true ? " checked" : ""; |
| output += `\ |
| <div class="setting-line">\ |
| <label class="setting-check">\ |
| <input type="checkbox" id="${js_data_name}"${checked}>\ |
| <span>${setting_name}</span>\ |
| </label>\ |
| </div>`; |
| } |
| } |
| return output; |
| } |
| |
| /** |
| * This function builds the "settings page" and returns the generated HTML element. |
| * |
| * @return {HTMLElement} |
| */ |
| function buildSettingsPage() { |
| const theme_list = getVar("themes"); |
| const theme_names = (theme_list === null ? "" : theme_list) |
| .split(",").filter(t => t); |
| theme_names.push("light", "dark", "ayu"); |
| |
| const settings = [ |
| { |
| "name": "Theme", |
| "js_name": "theme", |
| "default": "system preference", |
| "options": theme_names.concat("system preference"), |
| }, |
| { |
| "name": "Preferred light theme", |
| "js_name": "preferred-light-theme", |
| "default": "light", |
| "options": theme_names, |
| }, |
| { |
| "name": "Preferred dark theme", |
| "js_name": "preferred-dark-theme", |
| "default": "dark", |
| "options": theme_names, |
| }, |
| { |
| "name": "Auto-hide item contents for large items", |
| "js_name": "auto-hide-large-items", |
| "default": true, |
| }, |
| { |
| "name": "Auto-hide item methods' documentation", |
| "js_name": "auto-hide-method-docs", |
| "default": false, |
| }, |
| { |
| "name": "Auto-hide trait implementation documentation", |
| "js_name": "auto-hide-trait-implementations", |
| "default": false, |
| }, |
| { |
| "name": "Directly go to item in search if there is only one result", |
| "js_name": "go-to-only-result", |
| "default": false, |
| }, |
| { |
| "name": "Show line numbers on code examples", |
| "js_name": "line-numbers", |
| "default": false, |
| }, |
| { |
| "name": "Hide persistent navigation bar", |
| "js_name": "hide-sidebar", |
| "default": false, |
| }, |
| { |
| "name": "Hide table of contents", |
| "js_name": "hide-toc", |
| "default": false, |
| }, |
| { |
| "name": "Hide module navigation", |
| "js_name": "hide-modnav", |
| "default": false, |
| }, |
| { |
| "name": "Disable keyboard shortcuts", |
| "js_name": "disable-shortcuts", |
| "default": false, |
| }, |
| { |
| "name": "Use sans serif fonts", |
| "js_name": "sans-serif-fonts", |
| "default": false, |
| }, |
| { |
| "name": "Word wrap source code", |
| "js_name": "word-wrap-source-code", |
| "default": false, |
| }, |
| ]; |
| |
| // Then we build the DOM. |
| const elementKind = isSettingsPage ? "section" : "div"; |
| const innerHTML = `<div class="settings">${buildSettingsPageSections(settings)}</div>`; |
| const el = document.createElement(elementKind); |
| el.id = "settings"; |
| if (!isSettingsPage) { |
| el.className = "popover"; |
| } |
| el.innerHTML = innerHTML; |
| |
| if (isSettingsPage) { |
| const mainElem = document.getElementById(MAIN_ID); |
| if (mainElem !== null) { |
| mainElem.appendChild(el); |
| } |
| } else { |
| el.setAttribute("tabindex", "-1"); |
| onEachLazy(document.querySelectorAll(".settings-menu"), menu => { |
| if (menu.offsetWidth !== 0) { |
| menu.appendChild(el); |
| return true; |
| } |
| }); |
| } |
| return el; |
| } |
| |
| const settingsMenu = buildSettingsPage(); |
| |
| function displaySettings() { |
| settingsMenu.style.display = ""; |
| onEachLazy(document.querySelectorAll(".settings-menu"), menu => { |
| if (menu.offsetWidth !== 0) { |
| if (!menu.contains(settingsMenu) && settingsMenu.parentElement) { |
| settingsMenu.parentElement.removeChild(settingsMenu); |
| menu.appendChild(settingsMenu); |
| } |
| return true; |
| } |
| }); |
| onEachLazy(settingsMenu.querySelectorAll("input[type='checkbox']"), el => { |
| const val = getSettingValue(el.id); |
| const checked = val === "true"; |
| if (checked !== el.checked && val !== null) { |
| el.checked = checked; |
| } |
| }); |
| } |
| |
| /** |
| * @param {FocusEvent} event |
| */ |
| function settingsBlurHandler(event) { |
| const isInPopover = onEachLazy( |
| document.querySelectorAll(".settings-menu, .help-menu"), |
| menu => { |
| return menu.contains(document.activeElement) || menu.contains(event.relatedTarget); |
| }, |
| ); |
| if (!isInPopover) { |
| window.hidePopoverMenus(); |
| } |
| } |
| |
| if (!isSettingsPage) { |
| // We replace the existing "onclick" callback. |
| const settingsMenu = nonnull(document.getElementById("settings")); |
| onEachLazy(document.querySelectorAll(".settings-menu"), settingsButton => { |
| /** @param {MouseEvent} event */ |
| settingsButton.querySelector("a").onclick = event => { |
| if (!(event.target instanceof Element) || settingsMenu.contains(event.target)) { |
| return; |
| } |
| event.preventDefault(); |
| const shouldDisplaySettings = settingsMenu.style.display === "none"; |
| |
| window.hideAllModals(false); |
| if (shouldDisplaySettings) { |
| displaySettings(); |
| } |
| }; |
| settingsButton.onblur = settingsBlurHandler; |
| settingsButton.querySelector("a").onblur = settingsBlurHandler; |
| }); |
| onEachLazy(settingsMenu.querySelectorAll("input"), el => { |
| el.onblur = settingsBlurHandler; |
| }); |
| settingsMenu.onblur = settingsBlurHandler; |
| } |
| |
| // We now wait a bit for the web browser to end re-computing the DOM... |
| setTimeout(() => { |
| setEvents(settingsMenu); |
| // The setting menu is already displayed if we're on the settings page. |
| if (!isSettingsPage) { |
| displaySettings(); |
| } |
| onEachLazy(document.querySelectorAll(".settings-menu"), settingsButton => { |
| removeClass(settingsButton, "rotate"); |
| }); |
| }, 0); |
| })(); |