From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:02:58 +0200 Subject: Adding upstream version 1.64.0+dfsg1. Signed-off-by: Daniel Baumann --- src/librustdoc/html/static/js/main.js | 974 ++++++++++++++++++++++++++++++++++ 1 file changed, 974 insertions(+) create mode 100644 src/librustdoc/html/static/js/main.js (limited to 'src/librustdoc/html/static/js/main.js') diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js new file mode 100644 index 000000000..0702b2b0b --- /dev/null +++ b/src/librustdoc/html/static/js/main.js @@ -0,0 +1,974 @@ +// Local js definitions: +/* global addClass, getSettingValue, hasClass, searchState */ +/* global onEach, onEachLazy, removeClass */ + +"use strict"; + +// Get a value from the rustdoc-vars div, which is used to convey data from +// Rust to the JS. If there is no such element, return null. +function getVar(name) { + const el = document.getElementById("rustdoc-vars"); + if (el) { + return el.attributes["data-" + name].value; + } else { + return null; + } +} + +// Given a basename (e.g. "storage") and an extension (e.g. ".js"), return a URL +// for a resource under the root-path, with the resource-suffix. +function resourcePath(basename, extension) { + return getVar("root-path") + basename + getVar("resource-suffix") + extension; +} + +function hideMain() { + addClass(document.getElementById(MAIN_ID), "hidden"); +} + +function showMain() { + removeClass(document.getElementById(MAIN_ID), "hidden"); +} + +function elemIsInParent(elem, parent) { + while (elem && elem !== document.body) { + if (elem === parent) { + return true; + } + elem = elem.parentElement; + } + return false; +} + +function blurHandler(event, parentElem, hideCallback) { + if (!elemIsInParent(document.activeElement, parentElem) && + !elemIsInParent(event.relatedTarget, parentElem) + ) { + hideCallback(); + } +} + +(function() { + window.rootPath = getVar("root-path"); + window.currentCrate = getVar("current-crate"); +}()); + +function setMobileTopbar() { + // FIXME: It would be nicer to generate this text content directly in HTML, + // but with the current code it's hard to get the right information in the right place. + const mobileLocationTitle = document.querySelector(".mobile-topbar h2.location"); + const locationTitle = document.querySelector(".sidebar h2.location"); + if (mobileLocationTitle && locationTitle) { + mobileLocationTitle.innerHTML = locationTitle.innerHTML; + } +} + +// Gets the human-readable string for the virtual-key code of the +// given KeyboardEvent, ev. +// +// This function is meant as a polyfill for KeyboardEvent#key, +// since it is not supported in IE 11 or Chrome for Android. We also test for +// KeyboardEvent#keyCode because the handleShortcut handler is +// also registered for the keydown event, because Blink doesn't fire +// keypress on hitting the Escape key. +// +// So I guess you could say things are getting pretty interoperable. +function getVirtualKey(ev) { + if ("key" in ev && typeof ev.key !== "undefined") { + return ev.key; + } + + const c = ev.charCode || ev.keyCode; + if (c === 27) { + return "Escape"; + } + return String.fromCharCode(c); +} + +const MAIN_ID = "main-content"; +const SETTINGS_BUTTON_ID = "settings-menu"; +const ALTERNATIVE_DISPLAY_ID = "alternative-display"; +const NOT_DISPLAYED_ID = "not-displayed"; +const HELP_BUTTON_ID = "help-button"; + +function getSettingsButton() { + return document.getElementById(SETTINGS_BUTTON_ID); +} + +function getHelpButton() { + return document.getElementById(HELP_BUTTON_ID); +} + +// Returns the current URL without any query parameter or hash. +function getNakedUrl() { + return window.location.href.split("?")[0].split("#")[0]; +} + +/** + * This function inserts `newNode` after `referenceNode`. It doesn't work if `referenceNode` + * doesn't have a parent node. + * + * @param {HTMLElement} newNode + * @param {HTMLElement} referenceNode + */ +function insertAfter(newNode, referenceNode) { + referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); +} + +/** + * This function creates a new `
` with the given `id` and `classes` if it doesn't already + * exist. + * + * More information about this in `switchDisplayedElement` documentation. + * + * @param {string} id + * @param {string} classes + */ +function getOrCreateSection(id, classes) { + let el = document.getElementById(id); + + if (!el) { + el = document.createElement("section"); + el.id = id; + el.className = classes; + insertAfter(el, document.getElementById(MAIN_ID)); + } + return el; +} + +/** + * Returns the `
` element which contains the displayed element. + * + * @return {HTMLElement} + */ +function getAlternativeDisplayElem() { + return getOrCreateSection(ALTERNATIVE_DISPLAY_ID, "content hidden"); +} + +/** + * Returns the `
` element which contains the not-displayed elements. + * + * @return {HTMLElement} + */ +function getNotDisplayedElem() { + return getOrCreateSection(NOT_DISPLAYED_ID, "hidden"); +} + +/** + * To nicely switch between displayed "extra" elements (such as search results or settings menu) + * and to alternate between the displayed and not displayed elements, we hold them in two different + * `
` elements. They work in pair: one holds the hidden elements while the other + * contains the displayed element (there can be only one at the same time!). So basically, we switch + * elements between the two `
` elements. + * + * @param {HTMLElement} elemToDisplay + */ +function switchDisplayedElement(elemToDisplay) { + const el = getAlternativeDisplayElem(); + + if (el.children.length > 0) { + getNotDisplayedElem().appendChild(el.firstElementChild); + } + if (elemToDisplay === null) { + addClass(el, "hidden"); + showMain(); + return; + } + el.appendChild(elemToDisplay); + hideMain(); + removeClass(el, "hidden"); +} + +function browserSupportsHistoryApi() { + return window.history && typeof window.history.pushState === "function"; +} + +// eslint-disable-next-line no-unused-vars +function loadCss(cssFileName) { + const link = document.createElement("link"); + link.href = resourcePath(cssFileName, ".css"); + link.type = "text/css"; + link.rel = "stylesheet"; + document.getElementsByTagName("head")[0].appendChild(link); +} + +(function() { + function loadScript(url) { + const script = document.createElement("script"); + script.src = url; + document.head.append(script); + } + + getSettingsButton().onclick = event => { + addClass(getSettingsButton(), "rotate"); + event.preventDefault(); + // Sending request for the CSS and the JS files at the same time so it will + // hopefully be loaded when the JS will generate the settings content. + loadCss("settings"); + loadScript(resourcePath("settings", ".js")); + }; + + window.searchState = { + loadingText: "Loading search results...", + input: document.getElementsByClassName("search-input")[0], + outputElement: () => { + let el = document.getElementById("search"); + if (!el) { + el = document.createElement("section"); + el.id = "search"; + getNotDisplayedElem().appendChild(el); + } + return el; + }, + title: document.title, + titleBeforeSearch: document.title, + timeout: null, + // On the search screen, so you remain on the last tab you opened. + // + // 0 for "In Names" + // 1 for "In Parameters" + // 2 for "In Return Types" + currentTab: 0, + // tab and back preserves the element that was focused. + focusedByTab: [null, null, null], + clearInputTimeout: () => { + if (searchState.timeout !== null) { + clearTimeout(searchState.timeout); + searchState.timeout = null; + } + }, + isDisplayed: () => searchState.outputElement().parentElement.id === ALTERNATIVE_DISPLAY_ID, + // Sets the focus on the search bar at the top of the page + focus: () => { + searchState.input.focus(); + }, + // Removes the focus from the search bar. + defocus: () => { + searchState.input.blur(); + }, + showResults: search => { + if (search === null || typeof search === "undefined") { + search = searchState.outputElement(); + } + switchDisplayedElement(search); + searchState.mouseMovedAfterSearch = false; + document.title = searchState.title; + }, + hideResults: () => { + switchDisplayedElement(null); + document.title = searchState.titleBeforeSearch; + // We also remove the query parameter from the URL. + if (browserSupportsHistoryApi()) { + history.replaceState(null, window.currentCrate + " - Rust", + getNakedUrl() + window.location.hash); + } + }, + getQueryStringParams: () => { + const params = {}; + window.location.search.substring(1).split("&"). + map(s => { + const pair = s.split("="); + params[decodeURIComponent(pair[0])] = + typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]); + }); + return params; + }, + setup: () => { + const search_input = searchState.input; + if (!searchState.input) { + return; + } + let searchLoaded = false; + function loadSearch() { + if (!searchLoaded) { + searchLoaded = true; + loadScript(resourcePath("search", ".js")); + loadScript(resourcePath("search-index", ".js")); + } + } + + search_input.addEventListener("focus", () => { + search_input.origPlaceholder = search_input.placeholder; + search_input.placeholder = "Type your search here."; + loadSearch(); + }); + + if (search_input.value !== "") { + loadSearch(); + } + + const params = searchState.getQueryStringParams(); + if (params.search !== undefined) { + const search = searchState.outputElement(); + search.innerHTML = "

" + + searchState.loadingText + "

"; + searchState.showResults(search); + loadSearch(); + } + }, + }; + + function getPageId() { + if (window.location.hash) { + const tmp = window.location.hash.replace(/^#/, ""); + if (tmp.length > 0) { + return tmp; + } + } + return null; + } + + const toggleAllDocsId = "toggle-all-docs"; + let savedHash = ""; + + function handleHashes(ev) { + if (ev !== null && searchState.isDisplayed() && ev.newURL) { + // This block occurs when clicking on an element in the navbar while + // in a search. + switchDisplayedElement(null); + const hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1); + if (browserSupportsHistoryApi()) { + // `window.location.search`` contains all the query parameters, not just `search`. + history.replaceState(null, "", + getNakedUrl() + window.location.search + "#" + hash); + } + const elem = document.getElementById(hash); + if (elem) { + elem.scrollIntoView(); + } + } + // This part is used in case an element is not visible. + if (savedHash !== window.location.hash) { + savedHash = window.location.hash; + if (savedHash.length === 0) { + return; + } + expandSection(savedHash.slice(1)); // we remove the '#' + } + } + + function onHashChange(ev) { + // If we're in mobile mode, we should hide the sidebar in any case. + const sidebar = document.getElementsByClassName("sidebar")[0]; + removeClass(sidebar, "shown"); + handleHashes(ev); + } + + function openParentDetails(elem) { + while (elem) { + if (elem.tagName === "DETAILS") { + elem.open = true; + } + elem = elem.parentNode; + } + } + + function expandSection(id) { + openParentDetails(document.getElementById(id)); + } + + function handleEscape(ev) { + searchState.clearInputTimeout(); + switchDisplayedElement(null); + if (browserSupportsHistoryApi()) { + history.replaceState(null, window.currentCrate + " - Rust", + getNakedUrl() + window.location.hash); + } + ev.preventDefault(); + searchState.defocus(); + window.hidePopoverMenus(); + } + + function handleShortcut(ev) { + // Don't interfere with browser shortcuts + const disableShortcuts = getSettingValue("disable-shortcuts") === "true"; + if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts) { + return; + } + + if (document.activeElement.tagName === "INPUT" && + document.activeElement.type !== "checkbox") { + switch (getVirtualKey(ev)) { + case "Escape": + handleEscape(ev); + break; + } + } else { + switch (getVirtualKey(ev)) { + case "Escape": + handleEscape(ev); + break; + + case "s": + case "S": + ev.preventDefault(); + searchState.focus(); + break; + + case "+": + case "-": + ev.preventDefault(); + toggleAllDocs(); + break; + + case "?": + showHelp(); + break; + + default: + break; + } + } + } + + document.addEventListener("keypress", handleShortcut); + document.addEventListener("keydown", handleShortcut); + + function addSidebarItems() { + if (!window.SIDEBAR_ITEMS) { + return; + } + const sidebar = document.getElementsByClassName("sidebar-elems")[0]; + + /** + * Append to the sidebar a "block" of links - a heading along with a list (`
    `) of items. + * + * @param {string} shortty - A short type name, like "primitive", "mod", or "macro" + * @param {string} id - The HTML id of the corresponding section on the module page. + * @param {string} longty - A long, capitalized, plural name, like "Primitive Types", + * "Modules", or "Macros". + */ + function block(shortty, id, longty) { + const filtered = window.SIDEBAR_ITEMS[shortty]; + if (!filtered) { + return; + } + + const div = document.createElement("div"); + div.className = "block " + shortty; + const h3 = document.createElement("h3"); + h3.innerHTML = `${longty}`; + div.appendChild(h3); + const ul = document.createElement("ul"); + + for (const item of filtered) { + const name = item[0]; + const desc = item[1]; // can be null + + let klass = shortty; + let path; + if (shortty === "mod") { + path = name + "/index.html"; + } else { + path = shortty + "." + name + ".html"; + } + const current_page = document.location.href.split("/").pop(); + if (path === current_page) { + klass += " current"; + } + const link = document.createElement("a"); + link.href = path; + link.title = desc; + link.className = klass; + link.textContent = name; + const li = document.createElement("li"); + li.appendChild(link); + ul.appendChild(li); + } + div.appendChild(ul); + sidebar.appendChild(div); + } + + if (sidebar) { + block("primitive", "primitives", "Primitive Types"); + block("mod", "modules", "Modules"); + block("macro", "macros", "Macros"); + block("struct", "structs", "Structs"); + block("enum", "enums", "Enums"); + block("union", "unions", "Unions"); + block("constant", "constants", "Constants"); + block("static", "static", "Statics"); + block("trait", "traits", "Traits"); + block("fn", "functions", "Functions"); + block("type", "types", "Type Definitions"); + block("foreigntype", "foreign-types", "Foreign Types"); + block("keyword", "keywords", "Keywords"); + block("traitalias", "trait-aliases", "Trait Aliases"); + } + } + + window.register_implementors = imp => { + const implementors = document.getElementById("implementors-list"); + const synthetic_implementors = document.getElementById("synthetic-implementors-list"); + const inlined_types = new Set(); + + if (synthetic_implementors) { + // This `inlined_types` variable is used to avoid having the same implementation + // showing up twice. For example "String" in the "Sync" doc page. + // + // By the way, this is only used by and useful for traits implemented automatically + // (like "Send" and "Sync"). + onEachLazy(synthetic_implementors.getElementsByClassName("impl"), el => { + const aliases = el.getAttribute("data-aliases"); + if (!aliases) { + return; + } + aliases.split(",").forEach(alias => { + inlined_types.add(alias); + }); + }); + } + + let currentNbImpls = implementors.getElementsByClassName("impl").length; + const traitName = document.querySelector("h1.fqn > .in-band > .trait").textContent; + const baseIdName = "impl-" + traitName + "-"; + const libs = Object.getOwnPropertyNames(imp); + // We don't want to include impls from this JS file, when the HTML already has them. + // The current crate should always be ignored. Other crates that should also be + // ignored are included in the attribute `data-ignore-extern-crates`. + const ignoreExternCrates = document + .querySelector("script[data-ignore-extern-crates]") + .getAttribute("data-ignore-extern-crates"); + for (const lib of libs) { + if (lib === window.currentCrate || ignoreExternCrates.indexOf(lib) !== -1) { + continue; + } + const structs = imp[lib]; + + struct_loop: + for (const struct of structs) { + const list = struct.synthetic ? synthetic_implementors : implementors; + + if (struct.synthetic) { + for (const struct_type of struct.types) { + if (inlined_types.has(struct_type)) { + continue struct_loop; + } + inlined_types.add(struct_type); + } + } + + const code = document.createElement("h3"); + code.innerHTML = struct.text; + addClass(code, "code-header"); + addClass(code, "in-band"); + + onEachLazy(code.getElementsByTagName("a"), elem => { + const href = elem.getAttribute("href"); + + if (href && href.indexOf("http") !== 0) { + elem.setAttribute("href", window.rootPath + href); + } + }); + + const currentId = baseIdName + currentNbImpls; + const anchor = document.createElement("a"); + anchor.href = "#" + currentId; + addClass(anchor, "anchor"); + + const display = document.createElement("div"); + display.id = currentId; + addClass(display, "impl"); + display.appendChild(anchor); + display.appendChild(code); + list.appendChild(display); + currentNbImpls += 1; + } + } + }; + if (window.pending_implementors) { + window.register_implementors(window.pending_implementors); + } + + function addSidebarCrates() { + if (!window.ALL_CRATES) { + return; + } + const sidebarElems = document.getElementsByClassName("sidebar-elems")[0]; + if (!sidebarElems) { + return; + } + // Draw a convenient sidebar of known crates if we have a listing + const div = document.createElement("div"); + div.className = "block crate"; + div.innerHTML = "

    Crates

    "; + const ul = document.createElement("ul"); + div.appendChild(ul); + + for (const crate of window.ALL_CRATES) { + let klass = "crate"; + if (window.rootPath !== "./" && crate === window.currentCrate) { + klass += " current"; + } + const link = document.createElement("a"); + link.href = window.rootPath + crate + "/index.html"; + link.className = klass; + link.textContent = crate; + + const li = document.createElement("li"); + li.appendChild(link); + ul.appendChild(li); + } + sidebarElems.appendChild(div); + } + + + function labelForToggleButton(sectionIsCollapsed) { + if (sectionIsCollapsed) { + // button will expand the section + return "+"; + } + // button will collapse the section + // note that this text is also set in the HTML template in ../render/mod.rs + return "\u2212"; // "\u2212" is "−" minus sign + } + + function toggleAllDocs() { + const innerToggle = document.getElementById(toggleAllDocsId); + if (!innerToggle) { + return; + } + let sectionIsCollapsed = false; + if (hasClass(innerToggle, "will-expand")) { + removeClass(innerToggle, "will-expand"); + onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => { + if (!hasClass(e, "type-contents-toggle")) { + e.open = true; + } + }); + innerToggle.title = "collapse all docs"; + } else { + addClass(innerToggle, "will-expand"); + onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => { + if (e.parentNode.id !== "implementations-list" || + (!hasClass(e, "implementors-toggle") && + !hasClass(e, "type-contents-toggle")) + ) { + e.open = false; + } + }); + sectionIsCollapsed = true; + innerToggle.title = "expand all docs"; + } + innerToggle.children[0].innerText = labelForToggleButton(sectionIsCollapsed); + } + + (function() { + const toggles = document.getElementById(toggleAllDocsId); + if (toggles) { + toggles.onclick = toggleAllDocs; + } + + const hideMethodDocs = getSettingValue("auto-hide-method-docs") === "true"; + const hideImplementations = getSettingValue("auto-hide-trait-implementations") === "true"; + const hideLargeItemContents = getSettingValue("auto-hide-large-items") !== "false"; + + function setImplementorsTogglesOpen(id, open) { + const list = document.getElementById(id); + if (list !== null) { + onEachLazy(list.getElementsByClassName("implementors-toggle"), e => { + e.open = open; + }); + } + } + + if (hideImplementations) { + setImplementorsTogglesOpen("trait-implementations-list", false); + setImplementorsTogglesOpen("blanket-implementations-list", false); + } + + onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => { + if (!hideLargeItemContents && hasClass(e, "type-contents-toggle")) { + e.open = true; + } + if (hideMethodDocs && hasClass(e, "method-toggle")) { + e.open = false; + } + + }); + + const pageId = getPageId(); + if (pageId !== null) { + expandSection(pageId); + } + }()); + + (function() { + // To avoid checking on "rustdoc-line-numbers" value on every loop... + let lineNumbersFunc = () => {}; + if (getSettingValue("line-numbers") === "true") { + lineNumbersFunc = x => { + const count = x.textContent.split("\n").length; + const elems = []; + for (let i = 0; i < count; ++i) { + elems.push(i + 1); + } + const node = document.createElement("pre"); + addClass(node, "line-number"); + node.innerHTML = elems.join("\n"); + x.parentNode.insertBefore(node, x); + }; + } + onEachLazy(document.getElementsByClassName("rust-example-rendered"), e => { + if (hasClass(e, "compile_fail")) { + e.addEventListener("mouseover", function() { + this.parentElement.previousElementSibling.childNodes[0].style.color = "#f00"; + }); + e.addEventListener("mouseout", function() { + this.parentElement.previousElementSibling.childNodes[0].style.color = ""; + }); + } else if (hasClass(e, "ignore")) { + e.addEventListener("mouseover", function() { + this.parentElement.previousElementSibling.childNodes[0].style.color = "#ff9200"; + }); + e.addEventListener("mouseout", function() { + this.parentElement.previousElementSibling.childNodes[0].style.color = ""; + }); + } + lineNumbersFunc(e); + }); + }()); + + function hideSidebar() { + const sidebar = document.getElementsByClassName("sidebar")[0]; + removeClass(sidebar, "shown"); + } + + function handleClick(id, f) { + const elem = document.getElementById(id); + if (elem) { + elem.addEventListener("click", f); + } + } + handleClick(MAIN_ID, () => { + hideSidebar(); + }); + + onEachLazy(document.getElementsByTagName("a"), el => { + // For clicks on internal links ( tags with a hash property), we expand the section we're + // jumping to *before* jumping there. We can't do this in onHashChange, because it changes + // the height of the document so we wind up scrolled to the wrong place. + if (el.hash) { + el.addEventListener("click", () => { + expandSection(el.hash.slice(1)); + hideSidebar(); + }); + } + }); + + onEachLazy(document.querySelectorAll(".rustdoc-toggle > summary:not(.hideme)"), el => { + el.addEventListener("click", e => { + if (e.target.tagName !== "SUMMARY" && e.target.tagName !== "A") { + e.preventDefault(); + } + }); + }); + + onEachLazy(document.getElementsByClassName("notable-traits"), e => { + e.onclick = function() { + this.getElementsByClassName("notable-traits-tooltiptext")[0] + .classList.toggle("force-tooltip"); + }; + }); + + const sidebar_menu_toggle = document.getElementsByClassName("sidebar-menu-toggle")[0]; + if (sidebar_menu_toggle) { + sidebar_menu_toggle.addEventListener("click", () => { + const sidebar = document.getElementsByClassName("sidebar")[0]; + if (!hasClass(sidebar, "shown")) { + addClass(sidebar, "shown"); + } else { + removeClass(sidebar, "shown"); + } + }); + } + + function helpBlurHandler(event) { + blurHandler(event, getHelpButton(), window.hidePopoverMenus); + } + + function buildHelpMenu() { + const book_info = document.createElement("span"); + book_info.className = "top"; + book_info.innerHTML = "You can find more information in \ + the rustdoc book."; + + const shortcuts = [ + ["?", "Show this help dialog"], + ["S", "Focus the search field"], + ["↑", "Move up in search results"], + ["↓", "Move down in search results"], + ["← / →", "Switch result tab (when results focused)"], + ["⏎", "Go to active search result"], + ["+", "Expand all sections"], + ["-", "Collapse all sections"], + ].map(x => "
    " + + x[0].split(" ") + .map((y, index) => ((index & 1) === 0 ? "" + y + "" : " " + y + " ")) + .join("") + "
    " + x[1] + "
    ").join(""); + const div_shortcuts = document.createElement("div"); + addClass(div_shortcuts, "shortcuts"); + div_shortcuts.innerHTML = "

    Keyboard Shortcuts

    " + shortcuts + "
    "; + + const infos = [ + "Prefix searches with a type followed by a colon (e.g., fn:) to \ + restrict the search to a given item kind.", + "Accepted kinds are: fn, mod, struct, \ + enum, trait, type, macro, \ + and const.", + "Search functions by type signature (e.g., vec -> usize or \ + -> vec)", + "Search multiple things at once by splitting your query with comma (e.g., \ + str,u8 or String,struct:Vec,test)", + "You can look for items with an exact name by putting double quotes around \ + your request: \"string\"", + "Look for items inside another one by searching for a path: vec::Vec", + ].map(x => "

    " + x + "

    ").join(""); + const div_infos = document.createElement("div"); + addClass(div_infos, "infos"); + div_infos.innerHTML = "

    Search Tricks

    " + infos; + + const rustdoc_version = document.createElement("span"); + rustdoc_version.className = "bottom"; + const rustdoc_version_code = document.createElement("code"); + rustdoc_version_code.innerText = "rustdoc " + getVar("rustdoc-version"); + rustdoc_version.appendChild(rustdoc_version_code); + + const container = document.createElement("div"); + container.className = "popover"; + container.style.display = "none"; + + const side_by_side = document.createElement("div"); + side_by_side.className = "side-by-side"; + side_by_side.appendChild(div_shortcuts); + side_by_side.appendChild(div_infos); + + container.appendChild(book_info); + container.appendChild(side_by_side); + container.appendChild(rustdoc_version); + + const help_button = getHelpButton(); + help_button.appendChild(container); + + container.onblur = helpBlurHandler; + container.onclick = event => { + event.preventDefault(); + }; + help_button.onblur = helpBlurHandler; + help_button.children[0].onblur = helpBlurHandler; + + return container; + } + + /** + * Hide all the popover menus. + */ + window.hidePopoverMenus = function() { + onEachLazy(document.querySelectorAll(".search-container .popover"), elem => { + elem.style.display = "none"; + }); + }; + + /** + * Returns the help menu element (not the button). + * + * @param {boolean} buildNeeded - If this argument is `false`, the help menu element won't be + * built if it doesn't exist. + * + * @return {HTMLElement} + */ + function getHelpMenu(buildNeeded) { + let menu = getHelpButton().querySelector(".popover"); + if (!menu && buildNeeded) { + menu = buildHelpMenu(); + } + return menu; + } + + /** + * Show the help popup menu. + */ + function showHelp() { + const menu = getHelpMenu(true); + if (menu.style.display === "none") { + window.hidePopoverMenus(); + menu.style.display = ""; + } + } + + document.querySelector(`#${HELP_BUTTON_ID} > button`).addEventListener("click", event => { + const target = event.target; + if (target.tagName !== "BUTTON" || target.parentElement.id !== HELP_BUTTON_ID) { + return; + } + const menu = getHelpMenu(true); + const shouldShowHelp = menu.style.display === "none"; + if (shouldShowHelp) { + showHelp(); + } else { + window.hidePopoverMenus(); + } + }); + + setMobileTopbar(); + addSidebarItems(); + addSidebarCrates(); + onHashChange(null); + window.addEventListener("hashchange", onHashChange); + searchState.setup(); +}()); + +(function() { + let reset_button_timeout = null; + + window.copy_path = but => { + const parent = but.parentElement; + const path = []; + + onEach(parent.childNodes, child => { + if (child.tagName === "A") { + path.push(child.textContent); + } + }); + + const el = document.createElement("textarea"); + el.value = path.join("::"); + el.setAttribute("readonly", ""); + // To not make it appear on the screen. + el.style.position = "absolute"; + el.style.left = "-9999px"; + + document.body.appendChild(el); + el.select(); + document.execCommand("copy"); + document.body.removeChild(el); + + // There is always one children, but multiple childNodes. + but.children[0].style.display = "none"; + + let tmp; + if (but.childNodes.length < 2) { + tmp = document.createTextNode("✓"); + but.appendChild(tmp); + } else { + onEachLazy(but.childNodes, e => { + if (e.nodeType === Node.TEXT_NODE) { + tmp = e; + return true; + } + }); + tmp.textContent = "✓"; + } + + if (reset_button_timeout !== null) { + window.clearTimeout(reset_button_timeout); + } + + function reset_button() { + tmp.textContent = ""; + reset_button_timeout = null; + but.children[0].style.display = ""; + } + + reset_button_timeout = window.setTimeout(reset_button, 1000); + }; +}()); -- cgit v1.2.3