]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/html/static/js/main.js
Rollup merge of #92581 - Meziu:armv6k-3ds-target, r=nagisa
[rust.git] / src / librustdoc / html / static / js / main.js
1 // Local js definitions:
2 /* global addClass, getSettingValue, hasClass, searchState */
3 /* global onEach, onEachLazy, removeClass */
4 /* global switchTheme, useSystemTheme */
5
6 if (!String.prototype.startsWith) {
7     String.prototype.startsWith = function(searchString, position) {
8         position = position || 0;
9         return this.indexOf(searchString, position) === position;
10     };
11 }
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;
16     };
17 }
18
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;
24             } else {
25                 this.className = className;
26             }
27         }
28     };
29 }
30
31 if (!DOMTokenList.prototype.remove) {
32     DOMTokenList.prototype.remove = function(className) {
33         if (className && this.className) {
34             this.className = (" " + this.className + " ").replace(" " + className + " ", " ")
35                                                          .trim();
36         }
37     };
38 }
39
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");
44     if (el) {
45         return el.attributes["data-" + name].value;
46     } else {
47         return null;
48     }
49 }
50
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;
55 }
56
57
58 (function () {
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");
64     if (sidebarVars) {
65         window.sidebarCurrent = {
66             name: sidebarVars.attributes["data-name"].value,
67             ty: sidebarVars.attributes["data-ty"].value,
68             relpath: sidebarVars.attributes["data-relpath"].value,
69         };
70     }
71 }());
72
73 // Gets the human-readable string for the virtual-key code of the
74 // given KeyboardEvent, ev.
75 //
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.
81 //
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") {
85         return ev.key;
86     }
87
88     var c = ev.charCode || ev.keyCode;
89     if (c == 27) {
90         return "Escape";
91     }
92     return String.fromCharCode(c);
93 }
94
95 var THEME_PICKER_ELEMENT_ID = "theme-picker";
96 var THEMES_ELEMENT_ID = "theme-choices";
97 var MAIN_ID = "main-content";
98
99 function getThemesElement() {
100     return document.getElementById(THEMES_ELEMENT_ID);
101 }
102
103 function getThemePickerElement() {
104     return document.getElementById(THEME_PICKER_ELEMENT_ID);
105 }
106
107 // Returns the current URL without any query parameter or hash.
108 function getNakedUrl() {
109     return window.location.href.split("?")[0].split("#")[0];
110 }
111
112 function showThemeButtonState() {
113     var themePicker = getThemePickerElement();
114     var themeChoices = getThemesElement();
115
116     themeChoices.style.display = "block";
117     themePicker.style.borderBottomRightRadius = "0";
118     themePicker.style.borderBottomLeftRadius = "0";
119 }
120
121 function hideThemeButtonState() {
122     var themePicker = getThemePickerElement();
123     var themeChoices = getThemesElement();
124
125     themeChoices.style.display = "none";
126     themePicker.style.borderBottomRightRadius = "3px";
127     themePicker.style.borderBottomLeftRadius = "3px";
128 }
129
130 // Set up the theme picker list.
131 (function () {
132     var themeChoices = getThemesElement();
133     var themePicker = getThemePickerElement();
134     var availableThemes = getVar("themes").split(",");
135
136     function switchThemeButtonState() {
137         if (themeChoices.style.display === "block") {
138             hideThemeButtonState();
139         } else {
140             showThemeButtonState();
141         }
142     }
143
144     function handleThemeButtonsBlur(e) {
145         var active = document.activeElement;
146         var related = e.relatedTarget;
147
148         if (active.id !== THEME_PICKER_ELEMENT_ID &&
149             (!active.parentNode || active.parentNode.id !== THEMES_ELEMENT_ID) &&
150             (!related ||
151              (related.id !== THEME_PICKER_ELEMENT_ID &&
152               (!related.parentNode || related.parentNode.id !== THEMES_ELEMENT_ID)))) {
153             hideThemeButtonState();
154         }
155     }
156
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);
165         };
166         but.onblur = handleThemeButtonsBlur;
167         themeChoices.appendChild(but);
168     });
169 }());
170
171 (function() {
172     "use strict";
173
174     window.searchState = {
175         loadingText: "Loading search results...",
176         input: document.getElementsByClassName("search-input")[0],
177         outputElement: function() {
178             return document.getElementById("search");
179         },
180         title: document.title,
181         titleBeforeSearch: document.title,
182         timeout: null,
183         // On the search screen, so you remain on the last tab you opened.
184         //
185         // 0 for "In Names"
186         // 1 for "In Parameters"
187         // 2 for "In Return Types"
188         currentTab: 0,
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;
195             }
196         },
197         // Sets the focus on the search bar at the top of the page
198         focus: function() {
199             searchState.input.focus();
200         },
201         // Removes the focus from the search bar.
202         defocus: function() {
203             searchState.input.blur();
204         },
205         showResults: function(search) {
206             if (search === null || typeof search === 'undefined') {
207                 search = searchState.outputElement();
208             }
209             addClass(main, "hidden");
210             removeClass(search, "hidden");
211             searchState.mouseMovedAfterSearch = false;
212             document.title = searchState.title;
213         },
214         hideResults: function(search) {
215             if (search === null || typeof search === 'undefined') {
216                 search = searchState.outputElement();
217             }
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);
225             }
226         },
227         getQueryStringParams: function() {
228             var params = {};
229             window.location.search.substring(1).split("&").
230                 map(function(s) {
231                     var pair = s.split("=");
232                     params[decodeURIComponent(pair[0])] =
233                         typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]);
234                 });
235             return params;
236         },
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);
245                 }
246                 document.title = searchState.title;
247             }
248         },
249         browserSupportsHistoryApi: function() {
250             return window.history && typeof window.history.pushState === "function";
251         },
252         setup: function() {
253             var search_input = searchState.input;
254             if (!searchState.input) {
255                 return;
256             }
257             function loadScript(url) {
258                 var script = document.createElement('script');
259                 script.src = url;
260                 document.head.append(script);
261             }
262
263             var searchLoaded = false;
264             function loadSearch() {
265                 if (!searchLoaded) {
266                     searchLoaded = true;
267                     loadScript(window.searchJS);
268                     loadScript(window.searchIndexJS);
269                 }
270             }
271
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.";
276                 loadSearch();
277             });
278             search_input.addEventListener("blur", function() {
279                 search_input.placeholder = searchState.input.origPlaceholder;
280             });
281
282             if (search_input.value != '') {
283                 loadSearch();
284             }
285
286             // `crates{version}.js` should always be loaded before this script, so we can use it
287             // safely.
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);
295                 loadSearch();
296             }
297         },
298         addCrateDropdown: function(crates) {
299             var elem = document.getElementById("crate-search");
300
301             if (!elem) {
302                 return;
303             }
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
311                 // filter.
312                 //
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
315                 // on the
316                 // same domain.
317                 if (crates[i] === savedCrate) {
318                     elem.value = savedCrate;
319                 }
320             }
321         },
322     };
323
324     function getPageId() {
325         if (window.location.hash) {
326             var tmp = window.location.hash.replace(/^#/, "");
327             if (tmp.length > 0) {
328                 return tmp;
329             }
330         }
331         return null;
332     }
333
334     function showSidebar() {
335         var elems = document.getElementsByClassName("sidebar-elems")[0];
336         if (elems) {
337             addClass(elems, "show-it");
338         }
339         var sidebar = document.getElementsByClassName("sidebar")[0];
340         if (sidebar) {
341             addClass(sidebar, "mobile");
342             var filler = document.getElementById("sidebar-filler");
343             if (!filler) {
344                 var div = document.createElement("div");
345                 div.id = "sidebar-filler";
346                 sidebar.appendChild(div);
347             }
348         }
349     }
350
351     function hideSidebar() {
352         var elems = document.getElementsByClassName("sidebar-elems")[0];
353         if (elems) {
354             removeClass(elems, "show-it");
355         }
356         var sidebar = document.getElementsByClassName("sidebar")[0];
357         removeClass(sidebar, "mobile");
358         var filler = document.getElementById("sidebar-filler");
359         if (filler) {
360             filler.remove();
361         }
362         document.getElementsByTagName("body")[0].style.marginTop = "";
363     }
364
365     var toggleAllDocsId = "toggle-all-docs";
366     var main = document.getElementById(MAIN_ID);
367     var savedHash = "";
368
369     function handleHashes(ev) {
370         var elem;
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
374             // in a search.
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);
381             }
382             elem = document.getElementById(hash);
383             if (elem) {
384                 elem.scrollIntoView();
385             }
386         }
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) {
391                 return;
392             }
393             expandSection(savedHash.slice(1)); // we remove the '#'
394         }
395     }
396
397     function onHashChange(ev) {
398         // If we're in mobile mode, we should hide the sidebar in any case.
399         hideSidebar();
400         handleHashes(ev);
401     }
402
403     function openParentDetails(elem) {
404         while (elem) {
405             if (elem.tagName === "DETAILS") {
406                 elem.open = true;
407             }
408             elem = elem.parentNode;
409         }
410     }
411
412     function expandSection(id) {
413         openParentDetails(document.getElementById(id));
414     }
415
416     function getHelpElement(build) {
417         if (build) {
418             buildHelperPopup();
419         }
420         return document.getElementById("help");
421     }
422
423     /**
424      * Show the help popup.
425      *
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
429      */
430     function displayHelp(display, ev, help) {
431         if (display) {
432             help = help ? help : getHelpElement(true);
433             if (hasClass(help, "hidden")) {
434                 ev.preventDefault();
435                 removeClass(help, "hidden");
436                 addClass(document.body, "blur");
437             }
438         } else {
439             // No need to build the help popup if we want to hide it in case it hasn't been
440             // built yet...
441             help = help ? help : getHelpElement(false);
442             if (help && !hasClass(help, "hidden")) {
443                 ev.preventDefault();
444                 addClass(help, "hidden");
445                 removeClass(document.body, "blur");
446             }
447         }
448     }
449
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();
457             ev.preventDefault();
458             searchState.hideResults(search);
459         }
460         searchState.defocus();
461         hideThemeButtonState();
462     }
463
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) {
468             return;
469         }
470
471         if (document.activeElement.tagName === "INPUT") {
472             switch (getVirtualKey(ev)) {
473             case "Escape":
474                 handleEscape(ev);
475                 break;
476             }
477         } else {
478             switch (getVirtualKey(ev)) {
479             case "Escape":
480                 handleEscape(ev);
481                 break;
482
483             case "s":
484             case "S":
485                 displayHelp(false, ev);
486                 ev.preventDefault();
487                 searchState.focus();
488                 break;
489
490             case "+":
491             case "-":
492                 ev.preventDefault();
493                 toggleAllDocs();
494                 break;
495
496             case "?":
497                 displayHelp(true, ev);
498                 break;
499
500             case "t":
501             case "T":
502                 displayHelp(false, ev);
503                 ev.preventDefault();
504                 var themePicker = getThemePickerElement();
505                 themePicker.click();
506                 themePicker.focus();
507                 break;
508
509             default:
510                 if (getThemePickerElement().parentNode.contains(ev.target)) {
511                     handleThemeKeyDown(ev);
512                 }
513             }
514         }
515     }
516
517     function handleThemeKeyDown(ev) {
518         var active = document.activeElement;
519         var themes = getThemesElement();
520         switch (getVirtualKey(ev)) {
521         case "ArrowUp":
522             ev.preventDefault();
523             if (active.previousElementSibling && ev.target.id !== THEME_PICKER_ELEMENT_ID) {
524                 active.previousElementSibling.focus();
525             } else {
526                 showThemeButtonState();
527                 themes.lastElementChild.focus();
528             }
529             break;
530         case "ArrowDown":
531             ev.preventDefault();
532             if (active.nextElementSibling && ev.target.id !== THEME_PICKER_ELEMENT_ID) {
533                 active.nextElementSibling.focus();
534             } else {
535                 showThemeButtonState();
536                 themes.firstElementChild.focus();
537             }
538             break;
539         case "Enter":
540         case "Return":
541         case "Space":
542             if (ev.target.id === THEME_PICKER_ELEMENT_ID && themes.style.display === "none") {
543                 ev.preventDefault();
544                 showThemeButtonState();
545                 themes.firstElementChild.focus();
546             }
547             break;
548         case "Home":
549             ev.preventDefault();
550             themes.firstElementChild.focus();
551             break;
552         case "End":
553             ev.preventDefault();
554             themes.lastElementChild.focus();
555             break;
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
558         }
559     }
560
561     document.addEventListener("keypress", handleShortcut);
562     document.addEventListener("keydown", handleShortcut);
563
564     (function() {
565         var x = document.getElementsByClassName("version-selector");
566         if (x.length > 0) {
567             x[0].onchange = function() {
568                 var i, match,
569                     url = document.location.href,
570                     stripped = "",
571                     len = window.rootPath.match(/\.\.\//g).length + 1;
572
573                 for (i = 0; i < len; ++i) {
574                     match = url.match(/\/[^/]*$/);
575                     if (i < len - 1) {
576                         stripped = match[0] + stripped;
577                     }
578                     url = url.substring(0, url.length - match[0].length);
579                 }
580
581                 var selectedVersion = document.getElementsByClassName("version-selector")[0].value;
582                 url += "/" + selectedVersion + stripped;
583
584                 document.location.href = url;
585             };
586         }
587     }());
588
589     // delayed sidebar rendering.
590     window.initSidebarItems = function(items) {
591         var sidebar = document.getElementsByClassName("sidebar-elems")[0];
592         var others;
593         var current = window.sidebarCurrent;
594
595         function addSidebarCrates(crates) {
596             if (!hasClass(document.body, "crate")) {
597                 // We only want to list crates on the crate page.
598                 return;
599             }
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");
605             div.appendChild(ul);
606
607             for (var i = 0; i < crates.length; ++i) {
608                 var klass = "crate";
609                 if (window.rootPath !== "./" && crates[i] === window.currentCrate) {
610                     klass += " current";
611                 }
612                 var link = document.createElement("a");
613                 link.href = window.rootPath + crates[i] + "/index.html";
614                 link.className = klass;
615                 link.textContent = crates[i];
616
617                 var li = document.createElement("li");
618                 li.appendChild(link);
619                 ul.appendChild(li);
620             }
621             others.appendChild(div);
622         }
623
624         function block(shortty, longty) {
625             var filtered = items[shortty];
626             if (!filtered) {
627                 return;
628             }
629
630             var div = document.createElement("div");
631             div.className = "block " + shortty;
632             var h3 = document.createElement("h3");
633             h3.textContent = longty;
634             div.appendChild(h3);
635             var ul = document.createElement("ul");
636
637             for (var i = 0, len = filtered.length; i < len; ++i) {
638                 var item = filtered[i];
639                 var name = item[0];
640                 var desc = item[1]; // can be null
641
642                 var klass = shortty;
643                 if (name === current.name && shortty === current.ty) {
644                     klass += " current";
645                 }
646                 var path;
647                 if (shortty === "mod") {
648                     path = name + "/index.html";
649                 } else {
650                     path = shortty + "." + name + ".html";
651                 }
652                 var link = document.createElement("a");
653                 link.href = current.relpath + path;
654                 link.title = desc;
655                 link.className = klass;
656                 link.textContent = name;
657                 var li = document.createElement("li");
658                 li.appendChild(link);
659                 ul.appendChild(li);
660             }
661             div.appendChild(ul);
662             others.appendChild(div);
663         }
664
665         if (sidebar) {
666             others = document.createElement("div");
667             others.className = "others";
668             sidebar.appendChild(others);
669
670             var isModule = hasClass(document.body, "mod");
671             if (!isModule) {
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");
686             }
687
688             // `crates{version}.js` should always be loaded before this script, so we can use
689             // it safely.
690             addSidebarCrates(window.ALL_CRATES);
691         }
692     };
693
694     window.register_implementors = function(imp) {
695         var implementors = document.getElementById("implementors-list");
696         var synthetic_implementors = document.getElementById("synthetic-implementors-list");
697
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.
701             //
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");
707                 if (!aliases) {
708                     return;
709                 }
710                 aliases.split(",").forEach(function(alias) {
711                     inlined_types.add(alias);
712                 });
713             });
714         }
715
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]];
723
724             struct_loop:
725             for (var j = 0, slength = structs.length; j < slength; ++j) {
726                 var struct = structs[j];
727
728                 var list = struct.synthetic ? synthetic_implementors : implementors;
729
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;
734                         }
735                         inlined_types.add(struct.types[k]);
736                     }
737                 }
738
739                 var code = document.createElement("h3");
740                 code.innerHTML = struct.text;
741                 addClass(code, "code-header");
742                 addClass(code, "in-band");
743
744                 onEachLazy(code.getElementsByTagName("a"), function(elem) {
745                     var href = elem.getAttribute("href");
746
747                     if (href && href.indexOf("http") !== 0) {
748                         elem.setAttribute("href", window.rootPath + href);
749                     }
750                 });
751
752                 var currentId = baseIdName + currentNbImpls;
753                 var anchor = document.createElement("a");
754                 anchor.href = "#" + currentId;
755                 addClass(anchor, "anchor");
756
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);
763                 currentNbImpls += 1;
764             }
765         }
766     };
767     if (window.pending_implementors) {
768         window.register_implementors(window.pending_implementors);
769     }
770
771     function labelForToggleButton(sectionIsCollapsed) {
772         if (sectionIsCollapsed) {
773             // button will expand the section
774             return "+";
775         }
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
779     }
780
781     function toggleAllDocs() {
782         var innerToggle = document.getElementById(toggleAllDocsId);
783         if (!innerToggle) {
784             return;
785         }
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")) {
791                     e.open = true;
792                 }
793             });
794             innerToggle.title = "collapse all docs";
795         } else {
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")))
801                 {
802                     e.open = false;
803                 }
804             });
805             sectionIsCollapsed = true;
806             innerToggle.title = "expand all docs";
807         }
808         innerToggle.children[0].innerText = labelForToggleButton(sectionIsCollapsed);
809     }
810
811     function insertAfter(newNode, referenceNode) {
812         referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
813     }
814
815     (function() {
816         var toggles = document.getElementById(toggleAllDocsId);
817         if (toggles) {
818             toggles.onclick = toggleAllDocs;
819         }
820
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";
824
825         function setImplementorsTogglesOpen(id, open) {
826             var list = document.getElementById(id);
827             if (list !== null) {
828                 onEachLazy(list.getElementsByClassName("implementors-toggle"), function(e) {
829                     e.open = open;
830                 });
831             }
832         }
833
834         if (hideImplementations) {
835             setImplementorsTogglesOpen("trait-implementations-list", false);
836             setImplementorsTogglesOpen("blanket-implementations-list", false);
837         }
838
839         onEachLazy(document.getElementsByClassName("rustdoc-toggle"), function (e) {
840             if (!hideLargeItemContents && hasClass(e, "type-contents-toggle")) {
841                 e.open = true;
842             }
843             if (hideMethodDocs && hasClass(e, "method-toggle")) {
844                 e.open = false;
845             }
846
847         });
848
849         var pageId = getPageId();
850         if (pageId !== null) {
851             expandSection(pageId);
852         }
853     }());
854
855     (function() {
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;
861                 var elems = [];
862                 for (var i = 0; i < count; ++i) {
863                     elems.push(i + 1);
864                 }
865                 var node = document.createElement("pre");
866                 addClass(node, "line-number");
867                 node.innerHTML = elems.join("\n");
868                 x.parentNode.insertBefore(node, x);
869             };
870         }
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";
875                 });
876                 e.addEventListener("mouseout", function() {
877                     this.parentElement.previousElementSibling.childNodes[0].style.color = "";
878                 });
879             } else if (hasClass(e, "ignore")) {
880                 e.addEventListener("mouseover", function() {
881                     this.parentElement.previousElementSibling.childNodes[0].style.color = "#ff9200";
882                 });
883                 e.addEventListener("mouseout", function() {
884                     this.parentElement.previousElementSibling.childNodes[0].style.color = "";
885                 });
886             }
887             lineNumbersFunc(e);
888         });
889     }());
890
891     function handleClick(id, f) {
892         var elem = document.getElementById(id);
893         if (elem) {
894             elem.addEventListener("click", f);
895         }
896     }
897     handleClick("help-button", function(ev) {
898         displayHelp(true, ev);
899     });
900
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.
905         if (el.hash) {
906             el.addEventListener("click", function() {
907                 expandSection(el.hash.slice(1));
908             });
909         }
910     });
911
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") {
915                 e.preventDefault();
916             }
917         });
918     });
919
920     onEachLazy(document.getElementsByClassName("notable-traits"), function(e) {
921         e.onclick = function() {
922             this.getElementsByClassName('notable-traits-tooltiptext')[0]
923                 .classList.toggle("force-tooltip");
924         };
925     });
926
927     var sidebar_menu = document.getElementsByClassName("sidebar-menu")[0];
928     if (sidebar_menu) {
929         sidebar_menu.onclick = function() {
930             var sidebar = document.getElementsByClassName("sidebar")[0];
931             if (hasClass(sidebar, "mobile")) {
932                 hideSidebar();
933             } else {
934                 showSidebar();
935             }
936         };
937     }
938
939     var buildHelperPopup = function() {
940         var popup = document.createElement("aside");
941         addClass(popup, "hidden");
942         popup.id = "help";
943
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);
948             }
949         });
950
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>.";
955
956         var container = document.createElement("div");
957         var shortcuts = [
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             ["&#9166;", "Go to active search result"],
965             ["+", "Expand all sections"],
966             ["-", "Collapse all sections"],
967         ].map(function(x) {
968             return "<dt>" +
969                 x[0].split(" ")
970                     .map(function(y, index) {
971                         return (index & 1) === 0 ? "<kbd>" + y + "</kbd>" : " " + y + " ";
972                     })
973                     .join("") + "</dt><dd>" + x[1] + "</dd>";
974         }).join("");
975         var div_shortcuts = document.createElement("div");
976         addClass(div_shortcuts, "shortcuts");
977         div_shortcuts.innerHTML = "<h2>Keyboard Shortcuts</h2><dl>" + shortcuts + "</dl></div>";
978
979         var infos = [
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 -&gt; usize</code> or \
986              <code>* -&gt; 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>",
992         ].map(function(x) {
993             return "<p>" + x + "</p>";
994         }).join("");
995         var div_infos = document.createElement("div");
996         addClass(div_infos, "infos");
997         div_infos.innerHTML = "<h2>Search Tricks</h2>" + infos;
998
999         container.appendChild(book_info);
1000         container.appendChild(div_shortcuts);
1001         container.appendChild(div_infos);
1002
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);
1008
1009         container.appendChild(rustdoc_version);
1010
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() {};
1015     };
1016
1017     onHashChange(null);
1018     window.addEventListener("hashchange", onHashChange);
1019     searchState.setup();
1020 }());
1021
1022 (function () {
1023     var reset_button_timeout = null;
1024
1025     window.copy_path = function(but) {
1026         var parent = but.parentElement;
1027         var path = [];
1028
1029         onEach(parent.childNodes, function(child) {
1030             if (child.tagName === 'A') {
1031                 path.push(child.textContent);
1032             }
1033         });
1034
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';
1041
1042         document.body.appendChild(el);
1043         el.select();
1044         document.execCommand('copy');
1045         document.body.removeChild(el);
1046
1047         // There is always one children, but multiple childNodes.
1048         but.children[0].style.display = 'none';
1049
1050         var tmp;
1051         if (but.childNodes.length < 2) {
1052             tmp = document.createTextNode('✓');
1053             but.appendChild(tmp);
1054         } else {
1055             onEachLazy(but.childNodes, function(e) {
1056                 if (e.nodeType === Node.TEXT_NODE) {
1057                     tmp = e;
1058                     return true;
1059                 }
1060             });
1061             tmp.textContent = '✓';
1062         }
1063
1064         if (reset_button_timeout !== null) {
1065             window.clearTimeout(reset_button_timeout);
1066         }
1067
1068         function reset_button() {
1069             tmp.textContent = '';
1070             reset_button_timeout = null;
1071             but.children[0].style.display = "";
1072         }
1073
1074         reset_button_timeout = window.setTimeout(reset_button, 1000);
1075     };
1076 }());