src/librustdoc/html/static/js/main.js JAVASCRIPT 2,294 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 2,294.
1// Local js definitions:2/* global addClass, getSettingValue, hasClass, updateLocalStorage */3/* global onEachLazy, removeClass, getVar, nonnull */45"use strict";67// The amount of time that the cursor must remain still over a hover target before8// revealing a tooltip.9//10// https://www.nngroup.com/articles/timing-exposing-content/11window.RUSTDOC_TOOLTIP_HOVER_MS = 300;12window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS = 450;1314/**15 * Given a basename (e.g. "storage") and an extension (e.g. ".js"), return a URL16 * for a resource under the root-path, with the resource-suffix.17 *18 * @param {string} basename19 * @param {string} extension20 */21function resourcePath(basename, extension) {22    return getVar("root-path") + basename + getVar("resource-suffix") + extension;23}2425function hideMain() {26    addClass(document.getElementById(MAIN_ID), "hidden");27    const toggle = document.getElementById("toggle-all-docs");28    if (toggle) {29        toggle.setAttribute("disabled", "disabled");30    }31}3233function showMain() {34    const main = document.getElementById(MAIN_ID);35    if (!main) {36        return;37    }38    removeClass(main, "hidden");39    const mainHeading = main.querySelector(".main-heading");40    if (mainHeading && window.searchState.rustdocToolbar) {41        if (window.searchState.rustdocToolbar.parentElement) {42            window.searchState.rustdocToolbar.parentElement.removeChild(43                window.searchState.rustdocToolbar,44            );45        }46        mainHeading.appendChild(window.searchState.rustdocToolbar);47    }48    const toggle = document.getElementById("toggle-all-docs");49    if (toggle) {50        toggle.removeAttribute("disabled");51    }52}5354window.rootPath = getVar("root-path");55window.currentCrate = getVar("current-crate");5657/**58 * Gets the human-readable string for the virtual-key code of the59 * given KeyboardEvent, ev.60 *61 * This function is meant as a polyfill for KeyboardEvent#key,62 * since it is not supported in IE 11 or Chrome for Android. We also test for63 * KeyboardEvent#keyCode because the handleShortcut handler is64 * also registered for the keydown event, because Blink doesn't fire65 * keypress on hitting the Escape key.66 *67 * So I guess you could say things are getting pretty interoperable.68 *69 * @param {KeyboardEvent} ev70 * @returns {string}71 */72function getVirtualKey(ev) {73    if ("key" in ev && typeof ev.key !== "undefined") {74        return ev.key;75    }7677    const c = ev.charCode || ev.keyCode;78    if (c === 27) {79        return "Escape";80    }81    return String.fromCharCode(c);82}8384const MAIN_ID = "main-content";85const ALTERNATIVE_DISPLAY_ID = "alternative-display";86const NOT_DISPLAYED_ID = "not-displayed";8788// Returns the current URL without any query parameter or hash.89function getNakedUrl() {90    return window.location.href.split("?")[0].split("#")[0];91}9293/**94 * This function inserts `newNode` after `referenceNode`. It doesn't work if `referenceNode`95 * doesn't have a parent node.96 *97 * @param {HTMLElement} newNode98 * @param {HTMLElement & { parentNode: HTMLElement }} referenceNode99 */100function insertAfter(newNode, referenceNode) {101    referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);102}103104/**105 * This function creates a new `<section>` with the given `id` and `classes` if it doesn't already106 * exist.107 *108 * More information about this in `switchDisplayedElement` documentation.109 *110 * @param {string} id111 * @param {string} classes112 */113function getOrCreateSection(id, classes) {114    let el = document.getElementById(id);115116    if (!el) {117        el = document.createElement("section");118        el.id = id;119        el.className = classes;120        // MAIN_ID exists, and is not the root121        // @ts-expect-error122        insertAfter(el, document.getElementById(MAIN_ID));123    }124    return el;125}126127/**128 * Returns the `<section>` element which contains the displayed element.129 *130 * @return {HTMLElement}131 */132function getAlternativeDisplayElem() {133    return getOrCreateSection(ALTERNATIVE_DISPLAY_ID, "content hidden");134}135136/**137 * Returns the `<section>` element which contains the not-displayed elements.138 *139 * @return {HTMLElement}140 */141function getNotDisplayedElem() {142    return getOrCreateSection(NOT_DISPLAYED_ID, "hidden");143}144145/**146 * To nicely switch between displayed "extra" elements (such as search results or settings menu)147 * and to alternate between the displayed and not displayed elements, we hold them in two different148 * `<section>` elements. They work in pair: one holds the hidden elements while the other149 * contains the displayed element (there can be only one at the same time!). So basically, we switch150 * elements between the two `<section>` elements.151 *152 * @param {Element|null} elemToDisplay153 */154function switchDisplayedElement(elemToDisplay) {155    const el = getAlternativeDisplayElem();156157    if (el.children.length > 0) {158        getNotDisplayedElem().appendChild(nonnull(el.firstElementChild));159    }160    if (elemToDisplay === null) {161        addClass(el, "hidden");162        showMain();163        return;164    }165    el.appendChild(elemToDisplay);166    hideMain();167    removeClass(el, "hidden");168169    const mainHeading = elemToDisplay.querySelector(".main-heading");170    if (mainHeading && window.searchState.rustdocToolbar) {171        if (window.searchState.rustdocToolbar.parentElement) {172            window.searchState.rustdocToolbar.parentElement.removeChild(173                window.searchState.rustdocToolbar,174            );175        }176        mainHeading.appendChild(window.searchState.rustdocToolbar);177    }178}179180function browserSupportsHistoryApi() {181    return window.history && typeof window.history.pushState === "function";182}183184/**185 * Download CSS from the web without making it the active stylesheet.186 * We use this in the settings popover so that you don't get FOUC when switching.187 *188 * @param {string} cssUrl189 */190function preLoadCss(cssUrl) {191    // https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload192    const link = document.createElement("link");193    link.href = cssUrl;194    link.rel = "preload";195    link.as = "style";196    document.getElementsByTagName("head")[0].appendChild(link);197}198199(function() {200    const isHelpPage = window.location.pathname.endsWith("/help.html");201202    /**203     * Run a JavaScript file asynchronously.204     * @param {string} url205     * @param {function(): any} [errorCallback]206     */207    function loadScript(url, errorCallback) {208        const script = document.createElement("script");209        script.src = url;210        if (errorCallback !== undefined) {211            script.onerror = errorCallback;212        }213        document.head.append(script);214    }215216    onEachLazy(document.querySelectorAll(".settings-menu"), settingsMenu => {217        /** @param {MouseEvent} event */218        settingsMenu.querySelector("a").onclick = event => {219            if (event.ctrlKey || event.altKey || event.metaKey) {220                return;221            }222            window.hideAllModals(false);223            addClass(settingsMenu, "rotate");224            event.preventDefault();225            // Sending request for the CSS and the JS files at the same time so it will226            // hopefully be loaded when the JS will generate the settings content.227            loadScript(getVar("static-root-path") + getVar("settings-js"));228            // Pre-load all theme CSS files, so that switching feels seamless.229            //230            // When loading settings.html as a standalone page, the equivalent HTML is231            // generated in context.rs.232            setTimeout(() => {233                const themes = getVar("themes").split(",");234                for (const theme of themes) {235                    // if there are no themes, do nothing236                    // "".split(",") == [""]237                    if (theme !== "") {238                        preLoadCss(getVar("root-path") + theme + ".css");239                    }240                }241            }, 0);242        };243    });244245    window.searchState = {246        rustdocToolbar: document.querySelector("rustdoc-toolbar"),247        loadingText: "Loading search results...",248        inputElement: () => {249            let el = document.getElementsByClassName("search-input")[0];250            if (!el) {251                const out = nonnull(nonnull(window.searchState.outputElement()).parentElement);252                const hdr = document.createElement("div");253                hdr.className = "main-heading search-results-main-heading";254                const params = window.searchState.getQueryStringParams();255                const autofocusParam = params.search === "" ? "autofocus" : "";256                hdr.innerHTML = `<nav class="sub">257                    <form class="search-form loading">258                        <span></span> <!-- This empty span is a hacky fix for Safari: see #93184 -->259                        <input260                            ${autofocusParam}261                            class="search-input"262                            name="search"263                            aria-label="Run search in the documentation"264                            autocomplete="off"265                            spellcheck="false"266                            placeholder="Type ‘S’ or ‘/’ to search, ‘?’ for more options…"267                            type="search">268                    </form>269                </nav><div class="search-switcher"></div>`;270                out.insertBefore(hdr, window.searchState.outputElement());271                el = document.getElementsByClassName("search-input")[0];272            }273            if (el instanceof HTMLInputElement) {274                return el;275            }276            return null;277        },278        containerElement: () => {279            let el = document.getElementById("search");280            if (!el) {281                el = document.createElement("section");282                el.id = "search";283                getNotDisplayedElem().appendChild(el);284            }285            return el;286        },287        outputElement: () => {288            const container = window.searchState.containerElement();289            if (!container) {290                return null;291            }292            let el = container.querySelector(".search-out");293            if (!el) {294                el = document.createElement("div");295                el.className = "search-out";296                container.appendChild(el);297            }298            return el;299        },300        title: document.title,301        titleBeforeSearch: document.title,302        timeout: null,303        // On the search screen, so you remain on the last tab you opened.304        //305        // 0 for "In Names"306        // 1 for "In Parameters"307        // 2 for "In Return Types"308        currentTab: 0,309        // tab and back preserves the element that was focused.310        focusedByTab: [null, null, null],311        clearInputTimeout: () => {312            if (window.searchState.timeout !== null) {313                clearTimeout(window.searchState.timeout);314                window.searchState.timeout = null;315            }316        },317        isDisplayed: () => {318            const container = window.searchState.containerElement();319            if (!container) {320                return false;321            }322            return !!container.parentElement && container.parentElement.id ===323                ALTERNATIVE_DISPLAY_ID;324        },325        // Sets the focus on the search bar at the top of the page326        focus: () => {327            const inputElement = window.searchState.inputElement();328            window.searchState.showResults();329            if (inputElement) {330                inputElement.focus();331                // Avoid glitch if something focuses the search button after clicking.332                requestAnimationFrame(() => inputElement.focus());333            }334        },335        // Removes the focus from the search bar.336        defocus: () => {337            nonnull(window.searchState.inputElement()).blur();338        },339        toggle: () => {340            if (window.searchState.isDisplayed()) {341                window.searchState.defocus();342                window.searchState.hideResults();343            } else {344                window.searchState.focus();345            }346        },347        showResults: () => {348            document.title = window.searchState.title;349            if (window.searchState.isDisplayed()) {350                return;351            }352            const search = window.searchState.containerElement();353            switchDisplayedElement(search);354            const btn = document.querySelector("#search-button a");355            if (browserSupportsHistoryApi() && btn instanceof HTMLAnchorElement &&356                window.searchState.getQueryStringParams().search === undefined357            ) {358                history.pushState(null, "", btn.href);359            }360            const btnLabel = document.querySelector("#search-button a span.label");361            if (btnLabel) {362                btnLabel.innerHTML = "Exit";363            }364        },365        removeQueryParameters: () => {366            // We change the document title.367            document.title = window.searchState.titleBeforeSearch;368            if (browserSupportsHistoryApi()) {369                history.replaceState(null, "", getNakedUrl() + window.location.hash);370            }371        },372        hideResults: () => {373            switchDisplayedElement(null);374            // We also remove the query parameter from the URL.375            window.searchState.removeQueryParameters();376            const btnLabel = document.querySelector("#search-button a span.label");377            if (btnLabel) {378                btnLabel.innerHTML = "Search";379            }380        },381        getQueryStringParams: () => {382            /** @type {Object.<any, string>} */383            const params = {};384            window.location.search.substring(1).split("&").385                map(s => {386                    // https://github.com/rust-lang/rust/issues/119219387                    const pair = s.split("=").map(x => x.replace(/\+/g, " "));388                    params[decodeURIComponent(pair[0])] =389                        typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]);390                });391            return params;392        },393        setup: () => {394            let searchLoaded = false;395            const search_input = window.searchState.inputElement();396            if (!search_input) {397                return;398            }399            // If you're browsing the nightly docs, the page might need to be refreshed for the400            // search to work because the hash of the JS scripts might have changed.401            function sendSearchForm() {402                // @ts-expect-error403                document.getElementsByClassName("search-form")[0].submit();404            }405            function loadSearch() {406                if (!searchLoaded) {407                    searchLoaded = true;408                    window.rr_ = data => {409                        window.searchIndex = data;410                    };411                    if (!window.StringdexOnload) {412                        window.StringdexOnload = [];413                    }414                    window.StringdexOnload.push(() => {415                        loadScript(416                            getVar("static-root-path") + getVar("search-js"),417                            sendSearchForm,418                        );419                    });420                    loadScript(getVar("static-root-path") + getVar("stringdex-js"), sendSearchForm);421                    loadScript(resourcePath("search.index/root", ".js"), sendSearchForm);422                }423            }424425            search_input.addEventListener("focus", () => {426                loadSearch();427            });428429            const btn = document.getElementById("search-button");430            if (btn) {431                btn.onclick = event => {432                    if (event.ctrlKey || event.altKey || event.metaKey) {433                        return;434                    }435                    event.preventDefault();436                    window.searchState.toggle();437                    loadSearch();438                };439            }440441            // Push and pop states are used to add search results to the browser442            // history.443            if (browserSupportsHistoryApi()) {444                // Store the previous <title> so we can revert back to it later.445                const previousTitle = document.title;446447                window.addEventListener("popstate", e => {448                    const params = window.searchState.getQueryStringParams();449                    // Revert to the previous title manually since the History450                    // API ignores the title parameter.451                    document.title = previousTitle;452                    // Synchronize search bar with query string state and453                    // perform the search. This will empty the bar if there's454                    // nothing there, which lets you really go back to a455                    // previous state with nothing in the bar.456                    const inputElement = window.searchState.inputElement();457                    if (params.search !== undefined && inputElement !== null) {458                        loadSearch();459                        inputElement.value = params.search;460                        // Some browsers fire "onpopstate" for every page load461                        // (Chrome), while others fire the event only when actually462                        // popping a state (Firefox), which is why search() is463                        // called both here and at the end of the startSearch()464                        // function.465                        e.preventDefault();466                        window.searchState.showResults();467                        if (params.search === "") {468                            window.searchState.focus();469                        }470                    } else {471                        // When browsing back from search results the main page472                        // visibility must be reset.473                        window.searchState.hideResults();474                    }475                });476            }477478            // This is required in firefox to avoid this problem: Navigating to a search result479            // with the keyboard, hitting enter, and then hitting back would take you back to480            // the doc page, rather than the search that should overlay it.481            // This was an interaction between the back-forward cache and our handlers482            // that try to sync state between the URL and the search input. To work around it,483            // do a small amount of re-init on page show.484            window.onpageshow = () => {485                const inputElement = window.searchState.inputElement();486                const qSearch = window.searchState.getQueryStringParams().search;487                if (qSearch !== undefined && inputElement !== null) {488                    if (inputElement.value === "") {489                        inputElement.value = qSearch;490                    }491                    window.searchState.showResults();492                    if (qSearch === "") {493                        loadSearch();494                        window.searchState.focus();495                    }496                } else {497                    window.searchState.hideResults();498                }499            };500501            const params = window.searchState.getQueryStringParams();502            if (params.search !== undefined) {503                window.searchState.setLoadingSearch();504                loadSearch();505            }506        },507        setLoadingSearch: () => {508            const search = window.searchState.outputElement();509            nonnull(search).innerHTML = "<h3 class=\"search-loading\">" +510                window.searchState.loadingText + "</h3>";511            window.searchState.showResults();512        },513        descShards: new Map(),514        loadDesc: async function({descShard, descIndex}) {515            if (descShard.promise === null) {516                descShard.promise = new Promise((resolve, reject) => {517                    // The `resolve` callback is stored in the `descShard`518                    // object, which is itself stored in `this.descShards` map.519                    // It is called in `loadedDescShard` by the520                    // search.desc script.521                    descShard.resolve = resolve;522                    const ds = descShard;523                    const fname = `${ds.crate}-desc-${ds.shard}-`;524                    const url = resourcePath(525                        `search.desc/${descShard.crate}/${fname}`,526                        ".js",527                    );528                    loadScript(url, reject);529                });530            }531            const list = await descShard.promise;532            return list[descIndex];533        },534        loadedDescShard: function(crate, shard, data) {535            // If loadedDescShard gets called, then the library must have been declared.536            // @ts-expect-error537            this.descShards.get(crate)[shard].resolve(data.split("\n"));538        },539    };540541    const toggleAllDocsId = "toggle-all-docs";542    let savedHash = "";543544    /**545     * @param {HashChangeEvent|null} ev546     */547    function handleHashes(ev) {548        if (ev !== null && window.searchState.isDisplayed() && ev.newURL) {549            // This block occurs when clicking on an element in the navbar while550            // in a search.551            switchDisplayedElement(null);552            const hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1);553            if (browserSupportsHistoryApi()) {554                // `window.location.search`` contains all the query parameters, not just `search`.555                history.replaceState(null, "",556                    getNakedUrl() + window.location.search + "#" + hash);557            }558            const elem = document.getElementById(hash);559            if (elem) {560                elem.scrollIntoView();561            }562        }563        // This part is used in case an element is not visible.564        const pageId = window.location.hash.replace(/^#/, "");565        if (savedHash !== pageId) {566            savedHash = pageId;567            if (pageId !== "") {568                expandSection(pageId);569            }570        }571        if (savedHash.startsWith("impl-")) {572            // impl-disambiguated links, used by the search engine573            // format: impl-X[-for-Y]/method.WHATEVER574            // turn this into method.WHATEVER[-NUMBER]575            const splitAt = savedHash.indexOf("/");576            if (splitAt !== -1) {577                const implId = savedHash.slice(0, splitAt);578                const assocId = savedHash.slice(splitAt + 1);579                const implElems = document.querySelectorAll(580                    `details > summary > section[id^="${implId}"]`,581                );582                onEachLazy(implElems, implElem => {583                    const numbered = /^(.+?)-([0-9]+)$/.exec(implElem.id);584                    if (implElem.id !== implId && (!numbered || numbered[1] !== implId)) {585                        return false;586                    }587                    return onEachLazy(implElem.parentElement.parentElement.querySelectorAll(588                        `[id^="${assocId}"]`),589                        item => {590                            const numbered = /^(.+?)-([0-9]+)$/.exec(item.id);591                            if (item.id === assocId || (numbered && numbered[1] === assocId)) {592                                openParentDetails(item);593                                item.scrollIntoView();594                                // Let the section expand itself before trying to highlight595                                setTimeout(() => {596                                    window.location.replace("#" + item.id);597                                }, 0);598                                return true;599                            }600                        },601                    );602                });603            }604        }605    }606607    /**608     * @param {HashChangeEvent|null} ev609     */610    function onHashChange(ev) {611        // If we're in mobile mode, we should hide the sidebar in any case.612        hideSidebar();613        handleHashes(ev);614    }615616    /**617     * @param {HTMLElement|null} elem618     */619    function openParentDetails(elem) {620        while (elem) {621            if (elem instanceof HTMLDetailsElement) {622                elem.open = true;623            }624            elem = elem.parentElement;625        }626    }627628    /**629     * @param {string} id630     */631    function expandSection(id) {632        openParentDetails(document.getElementById(id));633    }634635    /**636     * @param {KeyboardEvent} ev637     */638    function handleEscape(ev) {639        window.searchState.clearInputTimeout();640        window.searchState.hideResults();641        ev.preventDefault();642        window.searchState.defocus();643        window.hideAllModals(true); // true = reset focus for tooltips644    }645646    /**647     * @param {KeyboardEvent} ev648     */649    function handleShortcut(ev) {650        // Don't interfere with browser shortcuts651        const disableShortcuts = getSettingValue("disable-shortcuts") === "true";652        if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts) {653            return;654        }655656        if (document.activeElement &&657            document.activeElement instanceof HTMLInputElement &&658            document.activeElement.type !== "checkbox" &&659            document.activeElement.type !== "radio") {660            switch (getVirtualKey(ev)) {661            case "Escape":662                handleEscape(ev);663                break;664            }665        } else {666            switch (getVirtualKey(ev)) {667            case "Escape":668                handleEscape(ev);669                break;670671            case "s":672            case "S":673            case "/":674                ev.preventDefault();675                window.searchState.focus();676                break;677678            case "+":679            case "=":680                ev.preventDefault();681                expandAllDocs();682                break;683            case "-":684                ev.preventDefault();685                collapseAllDocs(false);686                break;687            case "_":688                ev.preventDefault();689                collapseAllDocs(true);690                break;691692            case "?":693                showHelp();694                break;695696            default:697                break;698            }699        }700    }701702    document.addEventListener("keypress", handleShortcut);703    document.addEventListener("keydown", handleShortcut);704705    function addSidebarItems() {706        if (!window.SIDEBAR_ITEMS) {707            return;708        }709        const sidebar = document.getElementById("rustdoc-modnav");710711        /**712         * Append to the sidebar a "block" of links - a heading along with a list (`<ul>`) of items.713         *714         * @param {string} shortty - A short type name, like "primitive", "mod", or "macro"715         * @param {string} id - The HTML id of the corresponding section on the module page.716         * @param {string} longty - A long, capitalized, plural name, like "Primitive Types",717         *                          "Modules", or "Macros".718         */719        function block(shortty, id, longty) {720            // @ts-expect-error721            const filtered = window.SIDEBAR_ITEMS[shortty];722            if (!filtered) {723                return;724            }725726            const modpath = hasClass(document.querySelector(".rustdoc"), "mod") ? "../" : "";727728            const h3 = document.createElement("h3");729            h3.innerHTML = `<a href="${modpath}index.html#${id}">${longty}</a>`;730            const ul = document.createElement("ul");731            ul.className = "block " + shortty;732733            for (const item of filtered) {734                const [name, isMacro] = Array.isArray(item) ? [item[0], true] : [item, false];735                let path;736                if (shortty === "mod") {737                    path = `${modpath}${name}/index.html`;738                } else if (!isMacro) {739                    path = `${modpath}${shortty}.${name}.html`;740                } else {741                    path = `${modpath}macro.${name}.html`;742                }743                let current_page = document.location.href.toString();744                if (current_page.endsWith("/")) {745                    current_page += "index.html";746                }747                const link = document.createElement("a");748                link.href = path;749                link.textContent = name;750                const li = document.createElement("li");751                // Don't "optimize" this to just use `path`.752                // We want the browser to normalize this into an absolute URL.753                if (link.href === current_page) {754                    li.classList.add("current");755                }756                li.appendChild(link);757                ul.appendChild(li);758            }759            // @ts-expect-error760            sidebar.appendChild(h3);761            // @ts-expect-error762            sidebar.appendChild(ul);763        }764765        if (sidebar) {766            // keep this synchronized with ItemSection::ALL in html/render/mod.rs767            // Re-exports aren't shown here, because they don't have child pages768            //block("reexport", "reexports", "Re-exports");769            block("primitive", "primitives", "Primitive Types");770            block("mod", "modules", "Modules");771            block("macro", "macros", "Macros");772            block("struct", "structs", "Structs");773            block("enum", "enums", "Enums");774            block("constant", "constants", "Constants");775            block("static", "static", "Statics");776            block("trait", "traits", "Traits");777            block("fn", "functions", "Functions");778            block("type", "types", "Type Aliases");779            block("union", "unions", "Unions");780            // No point, because these items don't appear in modules781            //block("impl", "impls", "Implementations");782            //block("tymethod", "tymethods", "Type Methods");783            //block("method", "methods", "Methods");784            //block("structfield", "fields", "Fields");785            //block("variant", "variants", "Variants");786            //block("associatedtype", "associated-types", "Associated Types");787            //block("associatedconstant", "associated-consts", "Associated Constants");788            block("foreigntype", "foreign-types", "Foreign Types");789            block("keyword", "keywords", "Keywords");790            block("attribute", "attribute-docs", "Attributes");791            block("attr", "attributes", "Attribute Macros");792            block("derive", "derives", "Derive Macros");793            block("traitalias", "trait-aliases", "Trait Aliases");794        }795    }796797    // <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+trait.impl&type=code>798    window.register_implementors = imp => {799        /** Takes an ID as input and returns a list of two elements. The first element is the DOM800         * element with the given ID and the second is the "negative marker", meaning the location801         * between the negative and non-negative impls.802         *803         * @param {string} id: ID of the DOM element.804         *805         * @return {[HTMLElement|null, HTMLElement|null]}806         */807        function implementorsElems(id) {808            const elem = document.getElementById(id);809            return [elem, elem ? elem.querySelector(".negative-marker") : null];810        }811        const implementors = implementorsElems("implementors-list");812        const syntheticImplementors = implementorsElems("synthetic-implementors-list");813        /** @type {Set<string>} */814        const inlined_types = new Set();815816        const TEXT_IDX = 0;817        const IS_NEG_IDX = 1;818        const SYNTHETIC_IDX = 2;819        const TYPES_IDX = 3;820821        if (syntheticImplementors[0]) {822            // This `inlined_types` variable is used to avoid having the same implementation823            // showing up twice. For example "String" in the "Sync" doc page.824            //825            // By the way, this is only used by and useful for traits implemented automatically826            // (like "Send" and "Sync").827            onEachLazy(syntheticImplementors[0].getElementsByClassName("impl"), el => {828                const aliases = el.getAttribute("data-aliases");829                if (!aliases) {830                    return;831                }832                aliases.split(",").forEach(/** @param {string} alias */ alias => {833                    inlined_types.add(alias);834                });835            });836        }837838        let currentNbImpls = nonnull(implementors[0]).getElementsByClassName("impl").length;839        const traitName = nonnull(document.querySelector(".main-heading h1 > .trait")).textContent;840        const baseIdName = "impl-" + traitName + "-";841        const libs = Object.getOwnPropertyNames(imp);842        // We don't want to include impls from this JS file, when the HTML already has them.843        // The current crate should always be ignored. Other crates that should also be844        // ignored are included in the attribute `data-ignore-extern-crates`.845        const script = document846            .querySelector("script[data-ignore-extern-crates]");847        const ignoreExternCrates = new Set(848            // @ts-expect-error849            (script ? script.getAttribute("data-ignore-extern-crates") : "").split(","),850        );851        for (const lib of libs) {852            if (lib === window.currentCrate || ignoreExternCrates.has(lib)) {853                continue;854            }855            const structs = imp[lib];856857            struct_loop:858            for (const struct of structs) {859                const [impList, negImpMarker] =860                    struct[SYNTHETIC_IDX] ? syntheticImplementors : implementors;861862                // The types list is only used for synthetic impls.863                // If this changes, `main.js` and `write_shared.rs` both need changed.864                if (struct[SYNTHETIC_IDX]) {865                    for (const struct_type of struct[TYPES_IDX]) {866                        if (inlined_types.has(struct_type)) {867                            continue struct_loop;868                        }869                        inlined_types.add(struct_type);870                    }871                }872873                const code = document.createElement("h3");874                code.innerHTML = struct[TEXT_IDX];875                addClass(code, "code-header");876877                onEachLazy(code.getElementsByTagName("a"), elem => {878                    const href = elem.getAttribute("href");879880                    if (href && !href.startsWith("#") && !/^(?:[a-z+]+:)?\/\//.test(href)) {881                        elem.setAttribute("href", window.rootPath + href);882                    }883                });884885                const currentId = baseIdName + currentNbImpls;886                const anchor = document.createElement("a");887                anchor.href = "#" + currentId;888                addClass(anchor, "anchor");889890                const display = document.createElement("div");891                display.id = currentId;892                addClass(display, "impl");893                display.appendChild(anchor);894                display.appendChild(code);895896                // If this is a negative implementor, we put it into the right location (just897                // before the negative impl marker).898                if (struct[IS_NEG_IDX]) {899                    nonnull(negImpMarker).before(display);900                } else {901                    nonnull(impList).appendChild(display);902                }903                currentNbImpls += 1;904            }905        }906        if (implementors[0]) {907            implementors[0].classList.add("loaded");908        }909        if (syntheticImplementors[0]) {910            syntheticImplementors[0].classList.add("loaded");911        }912    };913    if (window.pending_implementors) {914        window.register_implementors(window.pending_implementors);915    }916917    /**918     * <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+type.impl&type=code>919     *920     * [RUSTDOCIMPL] type.impl921     *922     * This code inlines implementations into the type alias docs at runtime. It's done at923     * runtime because some crates have many type aliases and many methods, and we don't want924     * to generate *O*`(types*methods)` HTML text. The data inside is mostly HTML fragments,925     * wrapped in JSON.926     *927     * - It only includes docs generated for the current crate. This function accepts an928     *   object mapping crate names to the set of impls.929     *930     * - It filters down to the set of applicable impls. The Rust type checker is used to931     *   tag each HTML blob with the set of type aliases that can actually use it, so the932     *   JS only needs to consult the attached list of type aliases.933     *934     * - It renames the ID attributes, to avoid conflicting IDs in the resulting DOM.935     *936     * - It adds the necessary items to the sidebar. If it's an inherent impl, that means937     *   adding methods, associated types, and associated constants. If it's a trait impl,938     *   that means adding it to the trait impl sidebar list.939     *940     * - It adds the HTML block itself. If it's an inherent impl, it goes after the type941     *   alias's own inherent impls. If it's a trait impl, it goes in the Trait942     *   Implementations section.943     *944     * - After processing all of the impls, it sorts the sidebar items by name.945     *946     * @param {rustdoc.TypeImpls} imp947     */948    window.register_type_impls = imp => {949        // @ts-expect-error950        if (!imp || !imp[window.currentCrate]) {951            return;952        }953        window.pending_type_impls = undefined;954        const idMap = new Map();955956        let implementations = document.getElementById("implementations-list");957        let trait_implementations = document.getElementById("trait-implementations-list");958        let trait_implementations_header = document.getElementById("trait-implementations");959960        // We want to include the current type alias's impls, and no others.961        const script = document.querySelector("script[data-self-path]");962        const selfPath = script ? script.getAttribute("data-self-path") : null;963964        // These sidebar blocks need filled in, too.965        const mainContent = nonnull(document.querySelector("#main-content"));966        const sidebarSection = nonnull(document.querySelector(".sidebar section"));967        let methods = document.querySelector(".sidebar .block.method");968        let associatedTypes = document.querySelector(".sidebar .block.associatedtype");969        let associatedConstants = document.querySelector(".sidebar .block.associatedconstant");970        let sidebarTraitList = document.querySelector(".sidebar .block.trait-implementation");971972        for (const impList of imp[nonnull(window.currentCrate)]) {973            const types = impList.slice(2);974            const text = impList[0];975            const traitName = impList[1];976            const isTrait = typeof traitName === "string";977            if (selfPath === null || types.indexOf(selfPath) === -1) {978                continue;979            }980            let outputList = isTrait ? trait_implementations : implementations;981            if (outputList === null) {982                const outputListName = isTrait ? "Trait Implementations" : "Implementations";983                const outputListId = isTrait ?984                    "trait-implementations-list" :985                    "implementations-list";986                const outputListHeaderId = isTrait ? "trait-implementations" : "implementations";987                const outputListHeader = document.createElement("h2");988                outputListHeader.id = outputListHeaderId;989                outputListHeader.innerText = outputListName;990                outputList = document.createElement("div");991                outputList.id = outputListId;992                if (isTrait) {993                    const link = document.createElement("a");994                    link.href = `#${outputListHeaderId}`;995                    link.innerText = "Trait Implementations";996                    const h = document.createElement("h3");997                    h.appendChild(link);998                    trait_implementations = outputList;999                    trait_implementations_header = outputListHeader;1000                    sidebarSection.appendChild(h);1001                    sidebarTraitList = document.createElement("ul");1002                    sidebarTraitList.className = "block trait-implementation";1003                    sidebarSection.appendChild(sidebarTraitList);1004                    mainContent.appendChild(outputListHeader);1005                    mainContent.appendChild(outputList);1006                } else {1007                    implementations = outputList;1008                    if (trait_implementations) {1009                        mainContent.insertBefore(outputListHeader, trait_implementations_header);1010                        mainContent.insertBefore(outputList, trait_implementations_header);1011                    } else {1012                        mainContent.appendChild(outputListHeader);1013                        mainContent.appendChild(outputList);1014                    }1015                }1016            }1017            const template = document.createElement("template");1018            template.innerHTML = text;10191020            onEachLazy(template.content.querySelectorAll("a"), elem => {1021                const href = elem.getAttribute("href");10221023                if (href && !href.startsWith("#") && !/^(?:[a-z+]+:)?\/\//.test(href)) {1024                    elem.setAttribute("href", window.rootPath + href);1025                }1026            });1027            onEachLazy(template.content.querySelectorAll("[id]"), el => {1028                let i = 0;1029                if (idMap.has(el.id)) {1030                    i = idMap.get(el.id);1031                } else if (document.getElementById(el.id)) {1032                    i = 1;1033                    while (document.getElementById(`${el.id}-${2 * i}`)) {1034                        i = 2 * i;1035                    }1036                    while (document.getElementById(`${el.id}-${i}`)) {1037                        i += 1;1038                    }1039                }1040                if (i !== 0) {1041                    const oldHref = `#${el.id}`;1042                    const newHref = `#${el.id}-${i}`;1043                    el.id = `${el.id}-${i}`;1044                    onEachLazy(template.content.querySelectorAll("a[href]"), link => {1045                        if (link.getAttribute("href") === oldHref) {1046                            link.href = newHref;1047                        }1048                    });1049                }1050                idMap.set(el.id, i + 1);1051            });1052            const templateAssocItems = template.content.querySelectorAll("section.tymethod, " +1053                "section.method, section.associatedtype, section.associatedconstant");1054            if (isTrait) {1055                const li = document.createElement("li");1056                const a = document.createElement("a");1057                a.href = `#${nonnull(template.content.querySelector(".impl")).id}`;1058                a.textContent = traitName;1059                li.appendChild(a);1060                // @ts-expect-error1061                sidebarTraitList.append(li);1062            } else {1063                onEachLazy(templateAssocItems, item => {1064                    let block = hasClass(item, "associatedtype") ? associatedTypes : (1065                        hasClass(item, "associatedconstant") ? associatedConstants : (1066                        methods));1067                    if (!block) {1068                        const blockTitle = hasClass(item, "associatedtype") ? "Associated Types" : (1069                            hasClass(item, "associatedconstant") ? "Associated Constants" : (1070                            "Methods"));1071                        const blockClass = hasClass(item, "associatedtype") ? "associatedtype" : (1072                            hasClass(item, "associatedconstant") ? "associatedconstant" : (1073                            "method"));1074                        const blockHeader = document.createElement("h3");1075                        const blockLink = document.createElement("a");1076                        blockLink.href = "#implementations";1077                        blockLink.innerText = blockTitle;1078                        blockHeader.appendChild(blockLink);1079                        block = document.createElement("ul");1080                        block.className = `block ${blockClass}`;1081                        const insertionReference = methods || sidebarTraitList;1082                        if (insertionReference) {1083                            const insertionReferenceH = insertionReference.previousElementSibling;1084                            sidebarSection.insertBefore(blockHeader, insertionReferenceH);1085                            sidebarSection.insertBefore(block, insertionReferenceH);1086                        } else {1087                            sidebarSection.appendChild(blockHeader);1088                            sidebarSection.appendChild(block);1089                        }1090                        if (hasClass(item, "associatedtype")) {1091                            associatedTypes = block;1092                        } else if (hasClass(item, "associatedconstant")) {1093                            associatedConstants = block;1094                        } else {1095                            methods = block;1096                        }1097                    }1098                    const li = document.createElement("li");1099                    const a = document.createElement("a");1100                    a.innerText = item.id.split("-")[0].split(".")[1];1101                    a.href = `#${item.id}`;1102                    li.appendChild(a);1103                    block.appendChild(li);1104                });1105            }1106            outputList.appendChild(template.content);1107        }11081109        for (const list of [methods, associatedTypes, associatedConstants, sidebarTraitList]) {1110            if (!list) {1111                continue;1112            }1113            const newChildren = Array.prototype.slice.call(list.children);1114            newChildren.sort((a, b) => {1115                const aI = a.innerText;1116                const bI = b.innerText;1117                return aI < bI ? -1 :1118                    aI > bI ? 1 :1119                    0;1120            });1121            list.replaceChildren(...newChildren);1122        }1123    };1124    if (window.pending_type_impls) {1125        window.register_type_impls(window.pending_type_impls);1126    }11271128    function addSidebarCrates() {1129        // @ts-expect-error1130        if (!window.ALL_CRATES) {1131            return;1132        }1133        const sidebarElems = document.getElementById("rustdoc-modnav");1134        if (!sidebarElems) {1135            return;1136        }1137        // Draw a convenient sidebar of known crates if we have a listing1138        const h3 = document.createElement("h3");1139        h3.innerHTML = "Crates";1140        const ul = document.createElement("ul");1141        ul.className = "block crate";11421143        // @ts-expect-error1144        for (const crate of window.ALL_CRATES) {1145            const link = document.createElement("a");1146            link.href = window.rootPath + crate + "/index.html";1147            link.textContent = crate;11481149            const li = document.createElement("li");1150            if (window.rootPath !== "./" && crate === window.currentCrate) {1151                li.className = "current";1152            }1153            li.appendChild(link);1154            ul.appendChild(li);1155        }1156        sidebarElems.appendChild(h3);1157        sidebarElems.appendChild(ul);1158    }11591160    function expandAllDocs() {1161        const innerToggle = document.getElementById(toggleAllDocsId);1162        removeClass(innerToggle, "will-expand");1163        onEachLazy(document.getElementsByClassName("toggle"), e => {1164            if (!hasClass(e, "type-contents-toggle") && !hasClass(e, "more-examples-toggle")) {1165                e.open = true;1166            }1167        });1168        // @ts-expect-error1169        innerToggle.children[0].innerText = "Summary";1170    }11711172    /**1173     * @param {boolean} collapseImpls - also collapse impl blocks if set to true1174     */1175    function collapseAllDocs(collapseImpls) {1176        const innerToggle = document.getElementById(toggleAllDocsId);1177        addClass(innerToggle, "will-expand");1178        onEachLazy(document.getElementsByClassName("toggle"), e => {1179            if ((collapseImpls || e.parentNode.id !== "implementations-list") ||1180                (!hasClass(e, "implementors-toggle") &&1181                 !hasClass(e, "type-contents-toggle"))1182            ) {1183                e.open = false;1184            }1185        });1186        // @ts-expect-error1187        innerToggle.children[0].innerText = "Show all";1188    }11891190    /**1191     * @param {MouseEvent=} ev1192     */1193    function toggleAllDocs(ev) {1194        const innerToggle = document.getElementById(toggleAllDocsId);1195        if (!innerToggle) {1196            return;1197        }1198        if (hasClass(innerToggle, "will-expand")) {1199            expandAllDocs();1200        } else {1201            collapseAllDocs(ev !== undefined && ev.shiftKey);1202        }1203    }12041205    (function() {1206        const toggles = document.getElementById(toggleAllDocsId);1207        if (toggles) {1208            toggles.onclick = toggleAllDocs;1209        }12101211        const hideMethodDocs = getSettingValue("auto-hide-method-docs") === "true";1212        const hideImplementations = getSettingValue("auto-hide-trait-implementations") === "true";1213        const hideLargeItemContents = getSettingValue("auto-hide-large-items") !== "false";12141215        // @ts-expect-error1216        function setImplementorsTogglesOpen(id, open) {1217            const list = document.getElementById(id);1218            if (list !== null) {1219                onEachLazy(list.getElementsByClassName("implementors-toggle"), e => {1220                    e.open = open;1221                });1222            }1223        }12241225        if (hideImplementations) {1226            setImplementorsTogglesOpen("trait-implementations-list", false);1227            setImplementorsTogglesOpen("blanket-implementations-list", false);1228        }12291230        onEachLazy(document.getElementsByClassName("toggle"), e => {1231            if (!hideLargeItemContents && hasClass(e, "type-contents-toggle")) {1232                e.open = true;1233            }1234            if (hideMethodDocs && hasClass(e, "method-toggle")) {1235                e.open = false;1236            }12371238        });1239    }());12401241    window.rustdoc_add_line_numbers_to_examples = () => {1242        // @ts-expect-error1243        function generateLine(nb) {1244            return `<span data-nosnippet>${nb}</span>`;1245        }12461247        onEachLazy(document.querySelectorAll(1248            ".rustdoc:not(.src) :not(.scraped-example) > .example-wrap > pre > code",1249        ), code => {1250            if (hasClass(code.parentElement.parentElement, "hide-lines")) {1251                removeClass(code.parentElement.parentElement, "hide-lines");1252                return;1253            }1254            const lines = code.innerHTML.split("\n");1255            const digits = (lines.length + "").length;1256            // @ts-expect-error1257            code.innerHTML = lines.map((line, index) => generateLine(index + 1) + line).join("\n");1258            addClass(code.parentElement.parentElement, `digits-${digits}`);1259        });1260    };12611262    window.rustdoc_remove_line_numbers_from_examples = () => {1263        onEachLazy(1264            document.querySelectorAll(".rustdoc:not(.src) :not(.scraped-example) > .example-wrap"),1265            x => addClass(x, "hide-lines"),1266        );1267    };12681269    if (getSettingValue("line-numbers") === "true") {1270        window.rustdoc_add_line_numbers_to_examples();1271    }12721273    function showSidebar() {1274        window.hideAllModals(false);1275        const sidebar = document.getElementsByClassName("sidebar")[0];1276        addClass(sidebar, "shown");1277    }12781279    function hideSidebar() {1280        const sidebar = document.getElementsByClassName("sidebar")[0];1281        removeClass(sidebar, "shown");1282    }12831284    window.addEventListener("resize", () => {1285        if (window.CURRENT_TOOLTIP_ELEMENT) {1286            // As a workaround to the behavior of `contains: layout` used in doc togglers,1287            // tooltip popovers are positioned using javascript.1288            //1289            // This means when the window is resized, we need to redo the layout.1290            const base = window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE;1291            const force_visible = base.TOOLTIP_FORCE_VISIBLE;1292            hideTooltip(false);1293            if (force_visible) {1294                showTooltip(base);1295                base.TOOLTIP_FORCE_VISIBLE = true;1296            }1297        }1298    });12991300    const mainElem = document.getElementById(MAIN_ID);1301    if (mainElem) {1302        mainElem.addEventListener("click", hideSidebar);1303    }13041305    onEachLazy(document.querySelectorAll("a[href^='#']"), el => {1306        // For clicks on internal links (<A> tags with a hash property), we expand the section we're1307        // jumping to *before* jumping there. We can't do this in onHashChange, because it changes1308        // the height of the document so we wind up scrolled to the wrong place.1309        el.addEventListener("click", () => {1310            expandSection(el.hash.slice(1));1311            hideSidebar();1312        });1313    });13141315    onEachLazy(document.querySelectorAll(".toggle > summary:not(.hideme)"), el => {1316        // @ts-expect-error1317        // Clicking on the summary's contents should not collapse it,1318        // but links within should still fire.1319        el.addEventListener("click", e => {1320            if (!e.target.matches("summary, a, a *")) {1321                e.preventDefault();1322            }1323        });1324    });13251326    /**1327     * Show a tooltip immediately.1328     *1329     * @param {HTMLElement} e - The tooltip's anchor point. The DOM is consulted to figure1330     *                          out what the tooltip should contain, and where it should be1331     *                          positioned.1332     */1333    function showTooltip(e) {1334        const notable_ty = e.getAttribute("data-notable-ty");1335        if (!window.NOTABLE_TRAITS && notable_ty) {1336            const data = document.getElementById("notable-traits-data");1337            if (data) {1338                window.NOTABLE_TRAITS = JSON.parse(data.innerText);1339            } else {1340                throw new Error("showTooltip() called with notable without any notable traits!");1341            }1342        }1343        // Make this function idempotent. If the tooltip is already shown, avoid doing extra work1344        // and leave it alone.1345        if (window.CURRENT_TOOLTIP_ELEMENT && window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE === e) {1346            clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);1347            return;1348        }1349        window.hideAllModals(false);1350        // use Object.assign to make sure the object has the correct type1351        // with all of the correct fields before it is assigned to a variable,1352        // as typescript has no way to change the type of a variable once it is initialized.1353        const wrapper = Object.assign(document.createElement("div"), {TOOLTIP_BASE: e});1354        if (notable_ty) {1355            wrapper.innerHTML = "<div class=\"content\">" +1356                // @ts-expect-error1357                window.NOTABLE_TRAITS[notable_ty] + "</div>";1358        } else {1359            // Replace any `title` attribute with `data-title` to avoid double tooltips.1360            const ttl = e.getAttribute("title");1361            if (ttl !== null) {1362                e.setAttribute("data-title", ttl);1363                e.removeAttribute("title");1364            }1365            const dttl = e.getAttribute("data-title");1366            if (dttl !== null) {1367                const titleContent = document.createElement("div");1368                titleContent.className = "content";1369                titleContent.appendChild(document.createTextNode(dttl));1370                wrapper.appendChild(titleContent);1371            }1372        }1373        wrapper.className = "tooltip popover";1374        const focusCatcher = document.createElement("div");1375        focusCatcher.setAttribute("tabindex", "0");1376        // @ts-expect-error1377        focusCatcher.onfocus = hideTooltip;1378        wrapper.appendChild(focusCatcher);1379        const pos = e.getBoundingClientRect();1380        // 5px overlap so that the mouse can easily travel from place to place1381        wrapper.style.top = (pos.top + window.scrollY + pos.height) + "px";1382        // @ts-expect-error1383        wrapper.style.left = 0;1384        wrapper.style.right = "auto";1385        wrapper.style.visibility = "hidden";1386        document.body.appendChild(wrapper);1387        const wrapperPos = wrapper.getBoundingClientRect();1388        // offset so that the arrow points at the center of the "(i)"1389        const finalPos = pos.left + window.scrollX - wrapperPos.width + 24;1390        if (finalPos > 0) {1391            wrapper.style.left = finalPos + "px";1392        } else {1393            wrapper.style.setProperty(1394                "--popover-arrow-offset",1395                (wrapperPos.right - pos.right + 4) + "px",1396            );1397        }1398        wrapper.style.visibility = "";1399        window.CURRENT_TOOLTIP_ELEMENT = wrapper;1400        clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);1401        wrapper.onpointerenter = ev => {1402            // If this is a synthetic touch event, ignore it. A click event will be along shortly.1403            if (ev.pointerType !== "mouse") {1404                return;1405            }1406            clearTooltipHoverTimeout(e);1407        };1408        wrapper.onpointerleave = ev => {1409            // If this is a synthetic touch event, ignore it. A click event will be along shortly.1410            if (ev.pointerType !== "mouse" || !(ev.relatedTarget instanceof HTMLElement)) {1411                return;1412            }1413            if (!e.TOOLTIP_FORCE_VISIBLE && !e.contains(ev.relatedTarget)) {1414                // See "Tooltip pointer leave gesture" below.1415                setTooltipHoverTimeout(e, false);1416                addClass(wrapper, "fade-out");1417            }1418        };1419    }14201421    /**1422     * Show or hide the tooltip after a timeout. If a timeout was already set before this function1423     * was called, that timeout gets cleared. If the tooltip is already in the requested state,1424     * this function will still clear any pending timeout, but otherwise do nothing.1425     *1426     * @param {HTMLElement} element - The tooltip's anchor point. The DOM is consulted to figure1427     *                                out what the tooltip should contain, and where it should be1428     *                                positioned.1429     * @param {boolean}    show    - If true, the tooltip will be made visible. If false, it will1430     *                               be hidden.1431     */1432    function setTooltipHoverTimeout(element, show) {1433        clearTooltipHoverTimeout(element);1434        if (!show && !window.CURRENT_TOOLTIP_ELEMENT) {1435            // To "hide" an already hidden element, just cancel its timeout.1436            return;1437        }1438        if (show && window.CURRENT_TOOLTIP_ELEMENT) {1439            // To "show" an already visible element, just cancel its timeout.1440            return;1441        }1442        if (window.CURRENT_TOOLTIP_ELEMENT &&1443            window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE !== element) {1444            // Don't do anything if another tooltip is already visible.1445            return;1446        }1447        element.TOOLTIP_HOVER_TIMEOUT = setTimeout(() => {1448            if (show) {1449                showTooltip(element);1450            } else if (!element.TOOLTIP_FORCE_VISIBLE) {1451                hideTooltip(false);1452            }1453        }, show ? window.RUSTDOC_TOOLTIP_HOVER_MS : window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS);1454    }14551456    /**1457     * If a show/hide timeout was set by `setTooltipHoverTimeout`, cancel it. If none exists,1458     * do nothing.1459     *1460     * @param {HTMLElement} element - The tooltip's anchor point,1461     *                                as passed to `setTooltipHoverTimeout`.1462     */1463    function clearTooltipHoverTimeout(element) {1464        if (element.TOOLTIP_HOVER_TIMEOUT !== undefined) {1465            removeClass(window.CURRENT_TOOLTIP_ELEMENT, "fade-out");1466            clearTimeout(element.TOOLTIP_HOVER_TIMEOUT);1467            delete element.TOOLTIP_HOVER_TIMEOUT;1468        }1469    }14701471    /**1472     * @param {Event & { relatedTarget: Node }} event1473     */1474    function tooltipBlurHandler(event) {1475        if (window.CURRENT_TOOLTIP_ELEMENT &&1476            !window.CURRENT_TOOLTIP_ELEMENT.contains(document.activeElement) &&1477            !window.CURRENT_TOOLTIP_ELEMENT.contains(event.relatedTarget) &&1478            !window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.contains(document.activeElement) &&1479            !window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.contains(event.relatedTarget)1480        ) {1481            // Work around a difference in the focus behaviour between Firefox, Chrome, and Safari.1482            // When I click the button on an already-opened tooltip popover, Safari1483            // hides the popover and then immediately shows it again, while everyone else hides it1484            // and it stays hidden.1485            //1486            // To work around this, make sure the click finishes being dispatched before1487            // hiding the popover. Since `hideTooltip()` is idempotent, this makes Safari behave1488            // consistently with the other two.1489            setTimeout(() => hideTooltip(false), 0);1490        }1491    }14921493    /**1494     * Hide the current tooltip immediately.1495     *1496     * @param {boolean} focus - If set to `true`, move keyboard focus to the tooltip anchor point.1497     *                          If set to `false`, leave keyboard focus alone.1498     */1499    function hideTooltip(focus) {1500        if (window.CURRENT_TOOLTIP_ELEMENT) {1501            if (window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE) {1502                if (focus) {1503                    window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.focus();1504                }1505                window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE = false;1506            }1507            document.body.removeChild(window.CURRENT_TOOLTIP_ELEMENT);1508            clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);1509            window.CURRENT_TOOLTIP_ELEMENT = undefined;1510        }1511    }15121513    onEachLazy(document.getElementsByClassName("tooltip"), e => {1514        e.onclick = () => {1515            e.TOOLTIP_FORCE_VISIBLE = e.TOOLTIP_FORCE_VISIBLE ? false : true;1516            if (window.CURRENT_TOOLTIP_ELEMENT && !e.TOOLTIP_FORCE_VISIBLE) {1517                hideTooltip(true);1518            } else {1519                showTooltip(e);1520                // @ts-expect-error1521                window.CURRENT_TOOLTIP_ELEMENT.setAttribute("tabindex", "0");1522                // @ts-expect-error1523                window.CURRENT_TOOLTIP_ELEMENT.focus();1524                // @ts-expect-error1525                window.CURRENT_TOOLTIP_ELEMENT.onblur = tooltipBlurHandler;1526            }1527            return false;1528        };1529        // @ts-expect-error1530        e.onpointerenter = ev => {1531            // If this is a synthetic touch event, ignore it. A click event will be along shortly.1532            if (ev.pointerType !== "mouse") {1533                return;1534            }1535            setTooltipHoverTimeout(e, true);1536        };1537        // @ts-expect-error1538        e.onpointermove = ev => {1539            // If this is a synthetic touch event, ignore it. A click event will be along shortly.1540            if (ev.pointerType !== "mouse") {1541                return;1542            }1543            setTooltipHoverTimeout(e, true);1544        };1545        // @ts-expect-error1546        e.onpointerleave = ev => {1547            // If this is a synthetic touch event, ignore it. A click event will be along shortly.1548            if (ev.pointerType !== "mouse") {1549                return;1550            }1551            if (!e.TOOLTIP_FORCE_VISIBLE && window.CURRENT_TOOLTIP_ELEMENT &&1552                !window.CURRENT_TOOLTIP_ELEMENT.contains(ev.relatedTarget)) {1553                // Tooltip pointer leave gesture:1554                //1555                // Designing a good hover microinteraction is a matter of guessing user1556                // intent from what are, literally, vague gestures. In this case, guessing if1557                // hovering in or out of the tooltip base is intentional or not.1558                //1559                // To figure this out, a few different techniques are used:1560                //1561                // * When the mouse pointer enters a tooltip anchor point, its hitbox is grown1562                //   on the bottom, where the popover is/will appear. Search "hover tunnel" in1563                //   rustdoc.css for the implementation.1564                // * There's a delay when the mouse pointer enters the popover base anchor, in1565                //   case the mouse pointer was just passing through and the user didn't want1566                //   to open it.1567                // * Similarly, a delay is added when exiting the anchor, or the popover1568                //   itself, before hiding it.1569                // * A fade-out animation is layered onto the pointer exit delay to immediately1570                //   inform the user that they successfully dismissed the popover, while still1571                //   providing a way for them to cancel it if it was a mistake and they still1572                //   wanted to interact with it.1573                // * No animation is used for revealing it, because we don't want people to try1574                //   to interact with an element while it's in the middle of fading in: either1575                //   they're allowed to interact with it while it's fading in, meaning it can't1576                //   serve as mistake-proofing for the popover, or they can't, but1577                //   they might try and be frustrated.1578                //1579                // See also:1580                // * https://www.nngroup.com/articles/timing-exposing-content/1581                // * https://www.nngroup.com/articles/tooltip-guidelines/1582                // * https://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown1583                setTooltipHoverTimeout(e, false);1584                addClass(window.CURRENT_TOOLTIP_ELEMENT, "fade-out");1585            }1586        };1587    });15881589    const sidebar_menu_toggle = document.getElementsByClassName("sidebar-menu-toggle")[0];1590    if (sidebar_menu_toggle) {1591        sidebar_menu_toggle.addEventListener("click", () => {1592            const sidebar = document.getElementsByClassName("sidebar")[0];1593            // @ts-expect-error1594            if (!hasClass(sidebar, "shown")) {1595                showSidebar();1596            } else {1597                hideSidebar();1598            }1599        });1600    }16011602    // @ts-expect-error1603    function helpBlurHandler(event) {1604        const isInPopover = onEachLazy(1605            document.querySelectorAll(".settings-menu, .help-menu"),1606            menu => {1607                return menu.contains(document.activeElement) || menu.contains(event.relatedTarget);1608            },1609        );1610        if (!isInPopover) {1611            window.hidePopoverMenus();1612        }1613    }16141615    function buildHelpMenu() {1616        const book_info = document.createElement("span");1617        const drloChannel = `https://doc.rust-lang.org/${getVar("channel")}`;1618        book_info.className = "top";1619        book_info.innerHTML = `You can find more information in \1620<a href="${drloChannel}/rustdoc/">the rustdoc book</a>.`;16211622        const shortcuts = [1623            ["?", "Show this help dialog"],1624            ["S / /", "Focus the search field"],1625            ["↑", "Move up in search results"],1626            ["↓", "Move down in search results"],1627            ["← / →", "Switch result tab (when results focused)"],1628            ["&#9166;", "Go to active search result"],1629            ["+ / =", "Expand all sections"],1630            ["-", "Collapse all sections"],1631            // for the sake of brevity, we don't say "inherit impl blocks",1632            // although that would be more correct,1633            // since trait impl blocks are collapsed by -1634            ["_", "Collapse all sections, including impl blocks"],1635        ].map(x => "<dt>" +1636            x[0].split(" ")1637                .map((y, index) => ((index & 1) === 0 ? "<kbd>" + y + "</kbd>" : " " + y + " "))1638                .join("") + "</dt><dd>" + x[1] + "</dd>").join("");1639        const div_shortcuts = document.createElement("div");1640        addClass(div_shortcuts, "shortcuts");1641        div_shortcuts.innerHTML = "<h2>Keyboard Shortcuts</h2><dl>" + shortcuts + "</dl></div>";16421643        const infos = [1644            `For a full list of all search features, take a look \1645             <a href="${drloChannel}/rustdoc/read-documentation/search.html">here</a>.`,1646            "Prefix searches with a type followed by a colon (e.g., <code>fn:</code>) to \1647             restrict the search to a given item kind.",1648            "Accepted kinds are: <code>fn</code>, <code>mod</code>, <code>struct</code>, \1649             <code>enum</code>, <code>trait</code>, <code>type</code>, <code>macro</code>, \1650             and <code>constant</code>.",1651            "Search functions by type signature (e.g., <code>vec -&gt; usize</code> or \1652             <code>-&gt; vec</code> or <code>String, enum:Cow -&gt; bool</code>)",1653            "You can look for items with an exact name by putting double quotes around \1654             your request: <code>\"string\"</code>",1655             `Look for functions that accept or return \1656              <a href="${drloChannel}/std/primitive.slice.html">slices</a> and \1657              <a href="${drloChannel}/std/primitive.array.html">arrays</a> by writing square \1658              brackets (e.g., <code>-&gt; [u8]</code> or <code>[] -&gt; Option</code>)`,1659            "Look for items inside another one by searching for a path: <code>vec::Vec</code>",1660        ].map(x => "<p>" + x + "</p>").join("");1661        const div_infos = document.createElement("div");1662        addClass(div_infos, "infos");1663        div_infos.innerHTML = "<h2>Search Tricks</h2>" + infos;16641665        const rustdoc_version = document.createElement("span");1666        rustdoc_version.className = "bottom";1667        const rustdoc_version_code = document.createElement("code");1668        rustdoc_version_code.innerText = "rustdoc " + getVar("rustdoc-version");1669        rustdoc_version.appendChild(rustdoc_version_code);16701671        const container = document.createElement("div");1672        if (!isHelpPage) {1673            container.className = "popover";1674        }1675        container.id = "help";16761677        const side_by_side = document.createElement("div");1678        side_by_side.className = "side-by-side";1679        side_by_side.appendChild(div_shortcuts);1680        side_by_side.appendChild(div_infos);16811682        const content = document.createElement("div");1683        content.className = "content";16841685        content.appendChild(book_info);1686        content.appendChild(side_by_side);1687        content.appendChild(rustdoc_version);16881689        container.appendChild(content);16901691        if (isHelpPage) {1692            const help_section = document.createElement("section");1693            help_section.appendChild(container);1694            nonnull(document.getElementById("main-content")).appendChild(help_section);1695        } else {1696            onEachLazy(document.getElementsByClassName("help-menu"), menu => {1697                if (menu.offsetWidth !== 0) {1698                    menu.appendChild(container);1699                    container.onblur = helpBlurHandler;1700                    menu.onblur = helpBlurHandler;1701                    menu.children[0].onblur = helpBlurHandler;1702                    return true;1703                }1704            });1705        }17061707        return container;1708    }17091710    /**1711     * Hide popover menus, clickable tooltips, and the sidebar (if applicable).1712     *1713     * Pass `true` to reset focus for tooltip popovers.1714     */1715    window.hideAllModals = switchFocus => {1716        hideSidebar();1717        window.hidePopoverMenus();1718        hideTooltip(switchFocus);1719    };17201721    /**1722     * Hide all the popover menus.1723     */1724    window.hidePopoverMenus = () => {1725        onEachLazy(document.querySelectorAll(".settings-menu .popover"), elem => {1726            elem.style.display = "none";1727        });1728        onEachLazy(document.querySelectorAll(".help-menu .popover"), elem => {1729            elem.parentElement.removeChild(elem);1730        });1731    };17321733    /**1734     * Show the help popup menu.1735     */1736    function showHelp() {1737        window.hideAllModals(false);1738        // Prevent `blur` events from being dispatched as a result of closing1739        // other modals.1740        onEachLazy(document.querySelectorAll(".help-menu a"), menu => {1741            if (menu.offsetWidth !== 0) {1742                menu.focus();1743                return true;1744            }1745        });1746        buildHelpMenu();1747    }17481749    if (isHelpPage) {1750        buildHelpMenu();1751    } else {1752        onEachLazy(document.querySelectorAll(".help-menu > a"), helpLink => {1753            helpLink.addEventListener(1754                "click",1755                /** @param {MouseEvent} event */1756                event => {1757                    // By default, have help button open docs in a popover.1758                    // If user clicks with a moderator, though, use default browser behavior,1759                    // probably opening in a new window or tab.1760                    if (event.ctrlKey ||1761                        event.altKey ||1762                        event.metaKey) {1763                        return;1764                    }1765                    event.preventDefault();1766                    if (document.getElementById("help")) {1767                        window.hidePopoverMenus();1768                    } else {1769                        showHelp();1770                    }1771                },1772            );1773        });1774    }17751776    addSidebarItems();1777    addSidebarCrates();1778    onHashChange(null);1779    window.addEventListener("hashchange", onHashChange);1780    window.searchState.setup();1781}());17821783// Hide, show, and resize the sidebar1784//1785// The body class and CSS variable are initially set up in storage.js,1786// but in this file, we implement:1787//1788//   * the show sidebar button, which appears if the sidebar is hidden1789//     and, by clicking on it, will bring it back1790//   * the sidebar resize handle, which appears only on large viewports1791//     with a [fine precision pointer] to allow the user to change1792//     the size of the sidebar1793//1794// [fine precision pointer]: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pointer1795(function() {1796    // 100 is the size of the logo1797    // don't let the sidebar get smaller than that, or it'll get squished1798    const SIDEBAR_MIN = 100;1799    // Don't let the sidebar get bigger than this1800    const SIDEBAR_MAX = 500;1801    // Don't let the body (including the gutter) get smaller than this1802    //1803    // WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY1804    // Acceptable values for BODY_MIN are constrained by the mobile breakpoint1805    // (which is the minimum size of the whole page where the sidebar exists)1806    // and the default sidebar width:1807    //1808    //     BODY_MIN <= RUSTDOC_MOBILE_BREAKPOINT - DEFAULT_SIDEBAR_WIDTH1809    //1810    // At the time of this writing, the DEFAULT_SIDEBAR_WIDTH on src pages is1811    // 300px, and the RUSTDOC_MOBILE_BREAKPOINT is 700px, so BODY_MIN must be1812    // at most 400px. Otherwise, it would start out at the default size, then1813    // grabbing the resize handle would suddenly cause it to jank to1814    // its constraint-generated maximum.1815    const RUSTDOC_MOBILE_BREAKPOINT = 700;1816    const BODY_MIN = 400;1817    // At half-way past the minimum size, vanish the sidebar entirely1818    const SIDEBAR_VANISH_THRESHOLD = SIDEBAR_MIN / 2;18191820    // Toolbar button to show the sidebar.1821    //1822    // On small, "mobile-sized" viewports, it's not persistent and it1823    // can only be activated by going into Settings and hiding the nav bar.1824    // On larger, "desktop-sized" viewports (though that includes many1825    // tablets), it's fixed-position, appears in the left side margin,1826    // and it can be activated by resizing the sidebar into nothing.1827    let sidebarButton = document.getElementById("sidebar-button");1828    const body = document.querySelector(".main-heading");1829    if (!sidebarButton && body) {1830        sidebarButton = document.createElement("div");1831        sidebarButton.id = "sidebar-button";1832        const path = `${window.rootPath}${window.currentCrate}/all.html`;1833        sidebarButton.innerHTML = `<a href="${path}" title="show sidebar"></a>`;1834        body.insertBefore(sidebarButton, body.firstChild);1835    }1836    if (sidebarButton) {1837        sidebarButton.addEventListener("click", e => {1838            removeClass(document.documentElement, "hide-sidebar");1839            updateLocalStorage("hide-sidebar", "false");1840            if (window.rustdocToggleSrcSidebar) {1841                window.rustdocToggleSrcSidebar();1842            }1843            e.preventDefault();1844        });1845    }18461847    /**1848     * Pointer capture.1849     *1850     * Resizing is a single-pointer gesture. Any secondary pointer is ignored1851     *1852     * @type {null|number}1853     */1854    let currentPointerId = null;18551856    /**1857     * "Desired" sidebar size.1858     *1859     * This is stashed here for window resizing. If the sidebar gets1860     * shrunk to maintain BODY_MIN, and then the user grows the window again,1861     * it gets the sidebar to restore its size.1862     *1863     * @type {null|number}1864     */1865    let desiredSidebarSize = null;18661867    /**1868     * Sidebar resize debouncer.1869     *1870     * The sidebar itself is resized instantly, but the body HTML can be too1871     * big for that, causing reflow jank. To reduce this, we queue up a separate1872     * animation frame and throttle it.1873     *1874     * @type {false|ReturnType<typeof setTimeout>}1875     */1876    let pendingSidebarResizingFrame = false;18771878    /** @type {HTMLElement|null} */1879    const resizer = document.querySelector(".sidebar-resizer");1880    /** @type {HTMLElement|null} */1881    const sidebar = document.querySelector(".sidebar");1882    // If this page has no sidebar at all, bail out.1883    if (!resizer || !sidebar) {1884        return;1885    }18861887    // src page and docs page use different variables, because the contents of1888    // the sidebar are so different that it's reasonable to thing the user1889    // would want them to have different sizes1890    const isSrcPage = hasClass(document.body, "src");18911892    // Call this function to hide the sidebar when using the resize handle1893    //1894    // This function also nulls out the sidebar width CSS variable and setting,1895    // causing it to return to its default. This does not happen if you do it1896    // from settings.js, which uses a separate function. It's done here because1897    // the minimum sidebar size is rather uncomfortable, and it must pass1898    // through that size when using the shrink-to-nothing gesture.1899    const hideSidebar = function() {1900        if (isSrcPage) {1901            window.rustdocCloseSourceSidebar();1902            updateLocalStorage("src-sidebar-width", null);1903            // [RUSTDOCIMPL] CSS variable fast path1904            //1905            // The sidebar width variable is attached to the <html> element by1906            // storage.js, because the sidebar and resizer don't exist yet.1907            // But the resize code, in `resize()`, sets the property on the1908            // sidebar and resizer elements (which are the only elements that1909            // use the variable) to avoid recalculating CSS on the entire1910            // document on every frame.1911            //1912            // So, to clear it, we need to clear all three.1913            document.documentElement.style.removeProperty("--src-sidebar-width");1914            sidebar.style.removeProperty("--src-sidebar-width");1915            resizer.style.removeProperty("--src-sidebar-width");1916        } else {1917            addClass(document.documentElement, "hide-sidebar");1918            updateLocalStorage("hide-sidebar", "true");1919            updateLocalStorage("desktop-sidebar-width", null);1920            document.documentElement.style.removeProperty("--desktop-sidebar-width");1921            sidebar.style.removeProperty("--desktop-sidebar-width");1922            resizer.style.removeProperty("--desktop-sidebar-width");1923        }1924    };19251926    // Call this function to show the sidebar from the resize handle.1927    // On docs pages, this can only happen if the user has grabbed the resize1928    // handle, shrunk the sidebar down to nothing, and then pulls back into1929    // the visible range without releasing it. You can, however, grab the1930    // resize handle on a source page with the sidebar closed, because it1931    // remains visible all the time on there.1932    const showSidebar = function() {1933        if (isSrcPage) {1934            window.rustdocShowSourceSidebar();1935        } else {1936            removeClass(document.documentElement, "hide-sidebar");1937            updateLocalStorage("hide-sidebar", "false");1938        }1939    };19401941    /**1942     * Call this to set the correct CSS variable and setting.1943     * This function doesn't enforce size constraints. Do that before calling it!1944     *1945     * @param {number} size - CSS px width of the sidebar.1946     */1947    const changeSidebarSize = function(size) {1948        if (isSrcPage) {1949            updateLocalStorage("src-sidebar-width", size.toString());1950            // [RUSTDOCIMPL] CSS variable fast path1951            //1952            // While this property is set on the HTML element at load time,1953            // because the sidebar isn't actually loaded yet,1954            // we scope this update to the sidebar to avoid hitting a slow1955            // path in WebKit.1956            sidebar.style.setProperty("--src-sidebar-width", size + "px");1957            resizer.style.setProperty("--src-sidebar-width", size + "px");1958        } else {1959            updateLocalStorage("desktop-sidebar-width", size.toString());1960            sidebar.style.setProperty("--desktop-sidebar-width", size + "px");1961            resizer.style.setProperty("--desktop-sidebar-width", size + "px");1962        }1963    };19641965    // Check if the sidebar is hidden. Since src pages and doc pages have1966    // different settings, this function has to check that.1967    const isSidebarHidden = function() {1968        return isSrcPage ?1969            !hasClass(document.documentElement, "src-sidebar-expanded") :1970            hasClass(document.documentElement, "hide-sidebar");1971    };19721973    /**1974     * Respond to the resize handle event.1975     * This function enforces size constraints, and implements the1976     * shrink-to-nothing gesture based on thresholds defined above.1977     *1978     * @param {PointerEvent} e1979     */1980    const resize = function(e) {1981        if (currentPointerId === null || currentPointerId !== e.pointerId) {1982            return;1983        }1984        e.preventDefault();1985        const pos = e.clientX - 3;1986        if (pos < SIDEBAR_VANISH_THRESHOLD) {1987            hideSidebar();1988        } else if (pos >= SIDEBAR_MIN) {1989            if (isSidebarHidden()) {1990                showSidebar();1991            }1992            // don't let the sidebar get wider than SIDEBAR_MAX, or the body narrower1993            // than BODY_MIN1994            const constrainedPos = Math.min(pos, window.innerWidth - BODY_MIN, SIDEBAR_MAX);1995            changeSidebarSize(constrainedPos);1996            desiredSidebarSize = constrainedPos;1997            if (pendingSidebarResizingFrame !== false) {1998                clearTimeout(pendingSidebarResizingFrame);1999            }2000            pendingSidebarResizingFrame = setTimeout(() => {

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.