1 // Local js definitions:
2 /* global addClass, getSettingValue, hasClass, searchState */
3 /* global onEach, onEachLazy, removeClass */
4 /* global switchTheme, useSystemTheme */
6 if (!String.prototype.startsWith) {
7 String.prototype.startsWith = function(searchString, position) {
8 position = position || 0;
9 return this.indexOf(searchString, position) === position;
12 if (!String.prototype.endsWith) {
13 String.prototype.endsWith = function(suffix, length) {
14 var l = length || this.length;
15 return this.indexOf(suffix, l - suffix.length) !== -1;
19 if (!DOMTokenList.prototype.add) {
20 DOMTokenList.prototype.add = function(className) {
21 if (className && !hasClass(this, className)) {
22 if (this.className && this.className.length > 0) {
23 this.className += " " + className;
25 this.className = className;
31 if (!DOMTokenList.prototype.remove) {
32 DOMTokenList.prototype.remove = function(className) {
33 if (className && this.className) {
34 this.className = (" " + this.className + " ").replace(" " + className + " ", " ")
40 // Get a value from the rustdoc-vars div, which is used to convey data from
41 // Rust to the JS. If there is no such element, return null.
42 function getVar(name) {
43 var el = document.getElementById("rustdoc-vars");
45 return el.attributes["data-" + name].value;
51 // Given a basename (e.g. "storage") and an extension (e.g. ".js"), return a URL
52 // for a resource under the root-path, with the resource-suffix.
53 function resourcePath(basename, extension) {
54 return getVar("root-path") + basename + getVar("resource-suffix") + extension;
59 window.rootPath = getVar("root-path");
60 window.currentCrate = getVar("current-crate");
61 window.searchJS = resourcePath("search", ".js");
62 window.searchIndexJS = resourcePath("search-index", ".js");
63 var sidebarVars = document.getElementById("sidebar-vars");
65 window.sidebarCurrent = {
66 name: sidebarVars.attributes["data-name"].value,
67 ty: sidebarVars.attributes["data-ty"].value,
68 relpath: sidebarVars.attributes["data-relpath"].value,
73 // Gets the human-readable string for the virtual-key code of the
74 // given KeyboardEvent, ev.
76 // This function is meant as a polyfill for KeyboardEvent#key,
77 // since it is not supported in IE 11 or Chrome for Android. We also test for
78 // KeyboardEvent#keyCode because the handleShortcut handler is
79 // also registered for the keydown event, because Blink doesn't fire
80 // keypress on hitting the Escape key.
82 // So I guess you could say things are getting pretty interoperable.
83 function getVirtualKey(ev) {
84 if ("key" in ev && typeof ev.key != "undefined") {
88 var c = ev.charCode || ev.keyCode;
92 return String.fromCharCode(c);
95 var THEME_PICKER_ELEMENT_ID = "theme-picker";
96 var THEMES_ELEMENT_ID = "theme-choices";
97 var MAIN_ID = "main-content";
99 function getThemesElement() {
100 return document.getElementById(THEMES_ELEMENT_ID);
103 function getThemePickerElement() {
104 return document.getElementById(THEME_PICKER_ELEMENT_ID);
107 // Returns the current URL without any query parameter or hash.
108 function getNakedUrl() {
109 return window.location.href.split("?")[0].split("#")[0];
112 function showThemeButtonState() {
113 var themePicker = getThemePickerElement();
114 var themeChoices = getThemesElement();
116 themeChoices.style.display = "block";
117 themePicker.style.borderBottomRightRadius = "0";
118 themePicker.style.borderBottomLeftRadius = "0";
121 function hideThemeButtonState() {
122 var themePicker = getThemePickerElement();
123 var themeChoices = getThemesElement();
125 themeChoices.style.display = "none";
126 themePicker.style.borderBottomRightRadius = "3px";
127 themePicker.style.borderBottomLeftRadius = "3px";
130 // Set up the theme picker list.
132 var themeChoices = getThemesElement();
133 var themePicker = getThemePickerElement();
134 var availableThemes = getVar("themes").split(",");
136 function switchThemeButtonState() {
137 if (themeChoices.style.display === "block") {
138 hideThemeButtonState();
140 showThemeButtonState();
144 function handleThemeButtonsBlur(e) {
145 var active = document.activeElement;
146 var related = e.relatedTarget;
148 if (active.id !== THEME_PICKER_ELEMENT_ID &&
149 (!active.parentNode || active.parentNode.id !== THEMES_ELEMENT_ID) &&
151 (related.id !== THEME_PICKER_ELEMENT_ID &&
152 (!related.parentNode || related.parentNode.id !== THEMES_ELEMENT_ID)))) {
153 hideThemeButtonState();
157 themePicker.onclick = switchThemeButtonState;
158 themePicker.onblur = handleThemeButtonsBlur;
159 availableThemes.forEach(function(item) {
160 var but = document.createElement("button");
161 but.textContent = item;
162 but.onclick = function() {
163 switchTheme(window.currentTheme, window.mainTheme, item, true);
164 useSystemTheme(false);
166 but.onblur = handleThemeButtonsBlur;
167 themeChoices.appendChild(but);
174 window.searchState = {
175 loadingText: "Loading search results...",
176 input: document.getElementsByClassName("search-input")[0],
177 outputElement: function() {
178 return document.getElementById("search");
180 title: document.title,
181 titleBeforeSearch: document.title,
183 // On the search screen, so you remain on the last tab you opened.
186 // 1 for "In Parameters"
187 // 2 for "In Return Types"
189 // tab and back preserves the element that was focused.
190 focusedByTab: [null, null, null],
191 clearInputTimeout: function() {
192 if (searchState.timeout !== null) {
193 clearTimeout(searchState.timeout);
194 searchState.timeout = null;
197 // Sets the focus on the search bar at the top of the page
199 searchState.input.focus();
201 // Removes the focus from the search bar.
202 defocus: function() {
203 searchState.input.blur();
205 showResults: function(search) {
206 if (search === null || typeof search === 'undefined') {
207 search = searchState.outputElement();
209 addClass(main, "hidden");
210 removeClass(search, "hidden");
211 searchState.mouseMovedAfterSearch = false;
212 document.title = searchState.title;
214 hideResults: function(search) {
215 if (search === null || typeof search === 'undefined') {
216 search = searchState.outputElement();
218 addClass(search, "hidden");
219 removeClass(main, "hidden");
220 document.title = searchState.titleBeforeSearch;
221 // We also remove the query parameter from the URL.
222 if (searchState.browserSupportsHistoryApi()) {
223 history.replaceState("", window.currentCrate + " - Rust",
224 getNakedUrl() + window.location.hash);
227 getQueryStringParams: function() {
229 window.location.search.substring(1).split("&").
231 var pair = s.split("=");
232 params[decodeURIComponent(pair[0])] =
233 typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]);
237 putBackSearch: function(search_input) {
238 var search = searchState.outputElement();
239 if (search_input.value !== "" && hasClass(search, "hidden")) {
240 searchState.showResults(search);
241 if (searchState.browserSupportsHistoryApi()) {
242 var extra = "?search=" + encodeURIComponent(search_input.value);
243 history.replaceState(search_input.value, "",
244 getNakedUrl() + extra + window.location.hash);
246 document.title = searchState.title;
249 browserSupportsHistoryApi: function() {
250 return window.history && typeof window.history.pushState === "function";
253 var search_input = searchState.input;
254 if (!searchState.input) {
257 function loadScript(url) {
258 var script = document.createElement('script');
260 document.head.append(script);
263 var searchLoaded = false;
264 function loadSearch() {
267 loadScript(window.searchJS);
268 loadScript(window.searchIndexJS);
272 search_input.addEventListener("focus", function() {
273 searchState.putBackSearch(this);
274 search_input.origPlaceholder = searchState.input.placeholder;
275 search_input.placeholder = "Type your search here.";
278 search_input.addEventListener("blur", function() {
279 search_input.placeholder = searchState.input.origPlaceholder;
282 if (search_input.value != '') {
286 // `crates{version}.js` should always be loaded before this script, so we can use it
288 searchState.addCrateDropdown(window.ALL_CRATES);
289 var params = searchState.getQueryStringParams();
290 if (params.search !== undefined) {
291 var search = searchState.outputElement();
292 search.innerHTML = "<h3 class=\"search-loading\">" +
293 searchState.loadingText + "</h3>";
294 searchState.showResults(search);
298 addCrateDropdown: function(crates) {
299 var elem = document.getElementById("crate-search");
304 var savedCrate = getSettingValue("saved-filter-crate");
305 for (var i = 0, len = crates.length; i < len; ++i) {
306 var option = document.createElement("option");
307 option.value = crates[i];
308 option.innerText = crates[i];
309 elem.appendChild(option);
310 // Set the crate filter from saved storage, if the current page has the saved crate
313 // If not, ignore the crate filter -- we want to support filtering for crates on
314 // sites like doc.rust-lang.org where the crates may differ from page to page while
317 if (crates[i] === savedCrate) {
318 elem.value = savedCrate;
324 function getPageId() {
325 if (window.location.hash) {
326 var tmp = window.location.hash.replace(/^#/, "");
327 if (tmp.length > 0) {
334 function showSidebar() {
335 var elems = document.getElementsByClassName("sidebar-elems")[0];
337 addClass(elems, "show-it");
339 var sidebar = document.getElementsByClassName("sidebar")[0];
341 addClass(sidebar, "mobile");
342 var filler = document.getElementById("sidebar-filler");
344 var div = document.createElement("div");
345 div.id = "sidebar-filler";
346 sidebar.appendChild(div);
351 function hideSidebar() {
352 var elems = document.getElementsByClassName("sidebar-elems")[0];
354 removeClass(elems, "show-it");
356 var sidebar = document.getElementsByClassName("sidebar")[0];
357 removeClass(sidebar, "mobile");
358 var filler = document.getElementById("sidebar-filler");
362 document.getElementsByTagName("body")[0].style.marginTop = "";
365 var toggleAllDocsId = "toggle-all-docs";
366 var main = document.getElementById(MAIN_ID);
369 function handleHashes(ev) {
371 var search = searchState.outputElement();
372 if (ev !== null && search && !hasClass(search, "hidden") && ev.newURL) {
373 // This block occurs when clicking on an element in the navbar while
375 searchState.hideResults(search);
376 var hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1);
377 if (searchState.browserSupportsHistoryApi()) {
378 // `window.location.search`` contains all the query parameters, not just `search`.
379 history.replaceState(hash, "",
380 getNakedUrl() + window.location.search + "#" + hash);
382 elem = document.getElementById(hash);
384 elem.scrollIntoView();
387 // This part is used in case an element is not visible.
388 if (savedHash !== window.location.hash) {
389 savedHash = window.location.hash;
390 if (savedHash.length === 0) {
393 expandSection(savedHash.slice(1)); // we remove the '#'
397 function onHashChange(ev) {
398 // If we're in mobile mode, we should hide the sidebar in any case.
403 function openParentDetails(elem) {
405 if (elem.tagName === "DETAILS") {
408 elem = elem.parentNode;
412 function expandSection(id) {
413 openParentDetails(document.getElementById(id));
416 function getHelpElement(build) {
420 return document.getElementById("help");
424 * Show the help popup.
426 * @param {boolean} display - Whether to show or hide the popup
427 * @param {Event} ev - The event that triggered this call
428 * @param {Element} [help] - The help element if it already exists
430 function displayHelp(display, ev, help) {
432 help = help ? help : getHelpElement(true);
433 if (hasClass(help, "hidden")) {
435 removeClass(help, "hidden");
436 addClass(document.body, "blur");
439 // No need to build the help popup if we want to hide it in case it hasn't been
441 help = help ? help : getHelpElement(false);
442 if (help && !hasClass(help, "hidden")) {
444 addClass(help, "hidden");
445 removeClass(document.body, "blur");
450 function handleEscape(ev) {
451 var help = getHelpElement(false);
452 var search = searchState.outputElement();
453 if (help && !hasClass(help, "hidden")) {
454 displayHelp(false, ev, help);
455 } else if (search && !hasClass(search, "hidden")) {
456 searchState.clearInputTimeout();
458 searchState.hideResults(search);
460 searchState.defocus();
461 hideThemeButtonState();
464 var disableShortcuts = getSettingValue("disable-shortcuts") === "true";
465 function handleShortcut(ev) {
466 // Don't interfere with browser shortcuts
467 if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts) {
471 if (document.activeElement.tagName === "INPUT") {
472 switch (getVirtualKey(ev)) {
478 switch (getVirtualKey(ev)) {
485 displayHelp(false, ev);
497 displayHelp(true, ev);
502 displayHelp(false, ev);
504 var themePicker = getThemePickerElement();
510 if (getThemePickerElement().parentNode.contains(ev.target)) {
511 handleThemeKeyDown(ev);
517 function handleThemeKeyDown(ev) {
518 var active = document.activeElement;
519 var themes = getThemesElement();
520 switch (getVirtualKey(ev)) {
523 if (active.previousElementSibling && ev.target.id !== THEME_PICKER_ELEMENT_ID) {
524 active.previousElementSibling.focus();
526 showThemeButtonState();
527 themes.lastElementChild.focus();
532 if (active.nextElementSibling && ev.target.id !== THEME_PICKER_ELEMENT_ID) {
533 active.nextElementSibling.focus();
535 showThemeButtonState();
536 themes.firstElementChild.focus();
542 if (ev.target.id === THEME_PICKER_ELEMENT_ID && themes.style.display === "none") {
544 showThemeButtonState();
545 themes.firstElementChild.focus();
550 themes.firstElementChild.focus();
554 themes.lastElementChild.focus();
556 // The escape key is handled in handleEscape, not here,
557 // so that pressing escape will close the menu even if it isn't focused
561 document.addEventListener("keypress", handleShortcut);
562 document.addEventListener("keydown", handleShortcut);
565 var x = document.getElementsByClassName("version-selector");
567 x[0].onchange = function() {
569 url = document.location.href,
571 len = window.rootPath.match(/\.\.\//g).length + 1;
573 for (i = 0; i < len; ++i) {
574 match = url.match(/\/[^/]*$/);
576 stripped = match[0] + stripped;
578 url = url.substring(0, url.length - match[0].length);
581 var selectedVersion = document.getElementsByClassName("version-selector")[0].value;
582 url += "/" + selectedVersion + stripped;
584 document.location.href = url;
589 // delayed sidebar rendering.
590 window.initSidebarItems = function(items) {
591 var sidebar = document.getElementsByClassName("sidebar-elems")[0];
593 var current = window.sidebarCurrent;
595 function addSidebarCrates(crates) {
596 if (!hasClass(document.body, "crate")) {
597 // We only want to list crates on the crate page.
600 // Draw a convenient sidebar of known crates if we have a listing
601 var div = document.createElement("div");
602 div.className = "block crate";
603 div.innerHTML = "<h3>Crates</h3>";
604 var ul = document.createElement("ul");
607 for (var i = 0; i < crates.length; ++i) {
609 if (window.rootPath !== "./" && crates[i] === window.currentCrate) {
612 var link = document.createElement("a");
613 link.href = window.rootPath + crates[i] + "/index.html";
614 link.className = klass;
615 link.textContent = crates[i];
617 var li = document.createElement("li");
618 li.appendChild(link);
621 others.appendChild(div);
624 function block(shortty, longty) {
625 var filtered = items[shortty];
630 var div = document.createElement("div");
631 div.className = "block " + shortty;
632 var h3 = document.createElement("h3");
633 h3.textContent = longty;
635 var ul = document.createElement("ul");
637 for (var i = 0, len = filtered.length; i < len; ++i) {
638 var item = filtered[i];
640 var desc = item[1]; // can be null
643 if (name === current.name && shortty === current.ty) {
647 if (shortty === "mod") {
648 path = name + "/index.html";
650 path = shortty + "." + name + ".html";
652 var link = document.createElement("a");
653 link.href = current.relpath + path;
655 link.className = klass;
656 link.textContent = name;
657 var li = document.createElement("li");
658 li.appendChild(link);
662 others.appendChild(div);
666 others = document.createElement("div");
667 others.className = "others";
668 sidebar.appendChild(others);
670 var isModule = hasClass(document.body, "mod");
672 block("primitive", "Primitive Types");
673 block("mod", "Modules");
674 block("macro", "Macros");
675 block("struct", "Structs");
676 block("enum", "Enums");
677 block("union", "Unions");
678 block("constant", "Constants");
679 block("static", "Statics");
680 block("trait", "Traits");
681 block("fn", "Functions");
682 block("type", "Type Definitions");
683 block("foreigntype", "Foreign Types");
684 block("keyword", "Keywords");
685 block("traitalias", "Trait Aliases");
688 // `crates{version}.js` should always be loaded before this script, so we can use
690 addSidebarCrates(window.ALL_CRATES);
694 window.register_implementors = function(imp) {
695 var implementors = document.getElementById("implementors-list");
696 var synthetic_implementors = document.getElementById("synthetic-implementors-list");
698 if (synthetic_implementors) {
699 // This `inlined_types` variable is used to avoid having the same implementation
700 // showing up twice. For example "String" in the "Sync" doc page.
702 // By the way, this is only used by and useful for traits implemented automatically
703 // (like "Send" and "Sync").
704 var inlined_types = new Set();
705 onEachLazy(synthetic_implementors.getElementsByClassName("impl"), function(el) {
706 var aliases = el.getAttribute("data-aliases");
710 aliases.split(",").forEach(function(alias) {
711 inlined_types.add(alias);
716 var currentNbImpls = implementors.getElementsByClassName("impl").length;
717 var traitName = document.querySelector("h1.fqn > .in-band > .trait").textContent;
718 var baseIdName = "impl-" + traitName + "-";
719 var libs = Object.getOwnPropertyNames(imp);
720 for (var i = 0, llength = libs.length; i < llength; ++i) {
721 if (libs[i] === window.currentCrate) { continue; }
722 var structs = imp[libs[i]];
725 for (var j = 0, slength = structs.length; j < slength; ++j) {
726 var struct = structs[j];
728 var list = struct.synthetic ? synthetic_implementors : implementors;
730 if (struct.synthetic) {
731 for (var k = 0, stlength = struct.types.length; k < stlength; k++) {
732 if (inlined_types.has(struct.types[k])) {
733 continue struct_loop;
735 inlined_types.add(struct.types[k]);
739 var code = document.createElement("h3");
740 code.innerHTML = struct.text;
741 addClass(code, "code-header");
742 addClass(code, "in-band");
744 onEachLazy(code.getElementsByTagName("a"), function(elem) {
745 var href = elem.getAttribute("href");
747 if (href && href.indexOf("http") !== 0) {
748 elem.setAttribute("href", window.rootPath + href);
752 var currentId = baseIdName + currentNbImpls;
753 var anchor = document.createElement("a");
754 anchor.href = "#" + currentId;
755 addClass(anchor, "anchor");
757 var display = document.createElement("div");
758 display.id = currentId;
759 addClass(display, "impl");
760 display.appendChild(anchor);
761 display.appendChild(code);
762 list.appendChild(display);
767 if (window.pending_implementors) {
768 window.register_implementors(window.pending_implementors);
771 function labelForToggleButton(sectionIsCollapsed) {
772 if (sectionIsCollapsed) {
773 // button will expand the section
776 // button will collapse the section
777 // note that this text is also set in the HTML template in ../render/mod.rs
778 return "\u2212"; // "\u2212" is "−" minus sign
781 function toggleAllDocs() {
782 var innerToggle = document.getElementById(toggleAllDocsId);
786 var sectionIsCollapsed = false;
787 if (hasClass(innerToggle, "will-expand")) {
788 removeClass(innerToggle, "will-expand");
789 onEachLazy(document.getElementsByClassName("rustdoc-toggle"), function(e) {
790 if (!hasClass(e, "type-contents-toggle")) {
794 innerToggle.title = "collapse all docs";
796 addClass(innerToggle, "will-expand");
797 onEachLazy(document.getElementsByClassName("rustdoc-toggle"), function(e) {
798 if (e.parentNode.id !== MAIN_ID ||
799 (!hasClass(e, "implementors-toggle") &&
800 !hasClass(e, "type-contents-toggle")))
805 sectionIsCollapsed = true;
806 innerToggle.title = "expand all docs";
808 innerToggle.children[0].innerText = labelForToggleButton(sectionIsCollapsed);
811 function insertAfter(newNode, referenceNode) {
812 referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
816 var toggles = document.getElementById(toggleAllDocsId);
818 toggles.onclick = toggleAllDocs;
821 var hideMethodDocs = getSettingValue("auto-hide-method-docs") === "true";
822 var hideImplementations = getSettingValue("auto-hide-trait-implementations") === "true";
823 var hideLargeItemContents = getSettingValue("auto-hide-large-items") !== "false";
825 function setImplementorsTogglesOpen(id, open) {
826 var list = document.getElementById(id);
828 onEachLazy(list.getElementsByClassName("implementors-toggle"), function(e) {
834 if (hideImplementations) {
835 setImplementorsTogglesOpen("trait-implementations-list", false);
836 setImplementorsTogglesOpen("blanket-implementations-list", false);
839 onEachLazy(document.getElementsByClassName("rustdoc-toggle"), function (e) {
840 if (!hideLargeItemContents && hasClass(e, "type-contents-toggle")) {
843 if (hideMethodDocs && hasClass(e, "method-toggle")) {
849 var pageId = getPageId();
850 if (pageId !== null) {
851 expandSection(pageId);
856 // To avoid checking on "rustdoc-line-numbers" value on every loop...
857 var lineNumbersFunc = function() {};
858 if (getSettingValue("line-numbers") === "true") {
859 lineNumbersFunc = function(x) {
860 var count = x.textContent.split("\n").length;
862 for (var i = 0; i < count; ++i) {
865 var node = document.createElement("pre");
866 addClass(node, "line-number");
867 node.innerHTML = elems.join("\n");
868 x.parentNode.insertBefore(node, x);
871 onEachLazy(document.getElementsByClassName("rust-example-rendered"), function(e) {
872 if (hasClass(e, "compile_fail")) {
873 e.addEventListener("mouseover", function() {
874 this.parentElement.previousElementSibling.childNodes[0].style.color = "#f00";
876 e.addEventListener("mouseout", function() {
877 this.parentElement.previousElementSibling.childNodes[0].style.color = "";
879 } else if (hasClass(e, "ignore")) {
880 e.addEventListener("mouseover", function() {
881 this.parentElement.previousElementSibling.childNodes[0].style.color = "#ff9200";
883 e.addEventListener("mouseout", function() {
884 this.parentElement.previousElementSibling.childNodes[0].style.color = "";
891 function handleClick(id, f) {
892 var elem = document.getElementById(id);
894 elem.addEventListener("click", f);
897 handleClick("help-button", function(ev) {
898 displayHelp(true, ev);
901 onEachLazy(document.getElementsByTagName("a"), function(el) {
902 // For clicks on internal links (<A> tags with a hash property), we expand the section we're
903 // jumping to *before* jumping there. We can't do this in onHashChange, because it changes
904 // the height of the document so we wind up scrolled to the wrong place.
906 el.addEventListener("click", function() {
907 expandSection(el.hash.slice(1));
912 onEachLazy(document.querySelectorAll(".rustdoc-toggle > summary:not(.hideme)"), function(el) {
913 el.addEventListener("click", function(e) {
914 if (e.target.tagName != "SUMMARY" && e.target.tagName != "A") {
920 onEachLazy(document.getElementsByClassName("notable-traits"), function(e) {
921 e.onclick = function() {
922 this.getElementsByClassName('notable-traits-tooltiptext')[0]
923 .classList.toggle("force-tooltip");
927 var sidebar_menu = document.getElementsByClassName("sidebar-menu")[0];
929 sidebar_menu.onclick = function() {
930 var sidebar = document.getElementsByClassName("sidebar")[0];
931 if (hasClass(sidebar, "mobile")) {
939 var buildHelperPopup = function() {
940 var popup = document.createElement("aside");
941 addClass(popup, "hidden");
944 popup.addEventListener("click", function(ev) {
945 if (ev.target === popup) {
946 // Clicked the blurred zone outside the help popup; dismiss help.
947 displayHelp(false, ev);
951 var book_info = document.createElement("span");
952 book_info.className = "top";
953 book_info.innerHTML = "You can find more information in \
954 <a href=\"https://doc.rust-lang.org/rustdoc/\">the rustdoc book</a>.";
956 var container = document.createElement("div");
958 ["?", "Show this help dialog"],
959 ["S", "Focus the search field"],
960 ["T", "Focus the theme picker menu"],
961 ["↑", "Move up in search results"],
962 ["↓", "Move down in search results"],
963 ["← / →", "Switch result tab (when results focused)"],
964 ["⏎", "Go to active search result"],
965 ["+", "Expand all sections"],
966 ["-", "Collapse all sections"],
970 .map(function(y, index) {
971 return (index & 1) === 0 ? "<kbd>" + y + "</kbd>" : " " + y + " ";
973 .join("") + "</dt><dd>" + x[1] + "</dd>";
975 var div_shortcuts = document.createElement("div");
976 addClass(div_shortcuts, "shortcuts");
977 div_shortcuts.innerHTML = "<h2>Keyboard Shortcuts</h2><dl>" + shortcuts + "</dl></div>";
980 "Prefix searches with a type followed by a colon (e.g., <code>fn:</code>) to \
981 restrict the search to a given item kind.",
982 "Accepted kinds are: <code>fn</code>, <code>mod</code>, <code>struct</code>, \
983 <code>enum</code>, <code>trait</code>, <code>type</code>, <code>macro</code>, \
984 and <code>const</code>.",
985 "Search functions by type signature (e.g., <code>vec -> usize</code> or \
986 <code>* -> vec</code>)",
987 "Search multiple things at once by splitting your query with comma (e.g., \
988 <code>str,u8</code> or <code>String,struct:Vec,test</code>)",
989 "You can look for items with an exact name by putting double quotes around \
990 your request: <code>\"string\"</code>",
991 "Look for items inside another one by searching for a path: <code>vec::Vec</code>",
993 return "<p>" + x + "</p>";
995 var div_infos = document.createElement("div");
996 addClass(div_infos, "infos");
997 div_infos.innerHTML = "<h2>Search Tricks</h2>" + infos;
999 container.appendChild(book_info);
1000 container.appendChild(div_shortcuts);
1001 container.appendChild(div_infos);
1003 var rustdoc_version = document.createElement("span");
1004 rustdoc_version.className = "bottom";
1005 var rustdoc_version_code = document.createElement("code");
1006 rustdoc_version_code.innerText = "rustdoc " + getVar("rustdoc-version");
1007 rustdoc_version.appendChild(rustdoc_version_code);
1009 container.appendChild(rustdoc_version);
1011 popup.appendChild(container);
1012 insertAfter(popup, document.querySelector("main"));
1013 // So that it's only built once and then it'll do nothing when called!
1014 buildHelperPopup = function() {};
1018 window.addEventListener("hashchange", onHashChange);
1019 searchState.setup();
1023 var reset_button_timeout = null;
1025 window.copy_path = function(but) {
1026 var parent = but.parentElement;
1029 onEach(parent.childNodes, function(child) {
1030 if (child.tagName === 'A') {
1031 path.push(child.textContent);
1035 var el = document.createElement('textarea');
1036 el.value = path.join('::');
1037 el.setAttribute('readonly', '');
1038 // To not make it appear on the screen.
1039 el.style.position = 'absolute';
1040 el.style.left = '-9999px';
1042 document.body.appendChild(el);
1044 document.execCommand('copy');
1045 document.body.removeChild(el);
1047 // There is always one children, but multiple childNodes.
1048 but.children[0].style.display = 'none';
1051 if (but.childNodes.length < 2) {
1052 tmp = document.createTextNode('✓');
1053 but.appendChild(tmp);
1055 onEachLazy(but.childNodes, function(e) {
1056 if (e.nodeType === Node.TEXT_NODE) {
1061 tmp.textContent = '✓';
1064 if (reset_button_timeout !== null) {
1065 window.clearTimeout(reset_button_timeout);
1068 function reset_button() {
1069 tmp.textContent = '';
1070 reset_button_timeout = null;
1071 but.children[0].style.display = "";
1074 reset_button_timeout = window.setTimeout(reset_button, 1000);