]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/html/static/main.js
Improve searching in rustdoc and add tests
[rust.git] / src / librustdoc / html / static / main.js
1 // From rust:
2 /* global ALIASES, currentCrate, rootPath */
3
4 // Local js definitions:
5 /* global addClass, getCurrentValue, hasClass */
6 /* global isHidden, onEach, removeClass, updateLocalStorage */
7
8 if (!String.prototype.startsWith) {
9     String.prototype.startsWith = function(searchString, position) {
10         position = position || 0;
11         return this.indexOf(searchString, position) === position;
12     };
13 }
14 if (!String.prototype.endsWith) {
15     String.prototype.endsWith = function(suffix, length) {
16         var l = length || this.length;
17         return this.indexOf(suffix, l - suffix.length) !== -1;
18     };
19 }
20
21 if (!DOMTokenList.prototype.add) {
22     DOMTokenList.prototype.add = function(className) {
23         if (className && !hasClass(this, className)) {
24             if (this.className && this.className.length > 0) {
25                 this.className += " " + className;
26             } else {
27                 this.className = className;
28             }
29         }
30     };
31 }
32
33 if (!DOMTokenList.prototype.remove) {
34     DOMTokenList.prototype.remove = function(className) {
35         if (className && this.className) {
36             this.className = (" " + this.className + " ").replace(" " + className + " ", " ")
37                                                          .trim();
38         }
39     };
40 }
41
42 (function() {
43     "use strict";
44
45     // This mapping table should match the discriminants of
46     // `rustdoc::html::item_type::ItemType` type in Rust.
47     var itemTypes = ["mod",
48                      "externcrate",
49                      "import",
50                      "struct",
51                      "enum",
52                      "fn",
53                      "type",
54                      "static",
55                      "trait",
56                      "impl",
57                      "tymethod",
58                      "method",
59                      "structfield",
60                      "variant",
61                      "macro",
62                      "primitive",
63                      "associatedtype",
64                      "constant",
65                      "associatedconstant",
66                      "union",
67                      "foreigntype",
68                      "keyword",
69                      "existential",
70                      "attr",
71                      "derive",
72                      "traitalias"];
73
74     var search_input = document.getElementsByClassName("search-input")[0];
75
76     // On the search screen, so you remain on the last tab you opened.
77     //
78     // 0 for "In Names"
79     // 1 for "In Parameters"
80     // 2 for "In Return Types"
81     var currentTab = 0;
82
83     var titleBeforeSearch = document.title;
84
85     function getPageId() {
86         var id = document.location.href.split("#")[1];
87         if (id) {
88             return id.split("?")[0].split("&")[0];
89         }
90         return null;
91     }
92
93     function showSidebar() {
94         var elems = document.getElementsByClassName("sidebar-elems")[0];
95         if (elems) {
96             addClass(elems, "show-it");
97         }
98         var sidebar = document.getElementsByClassName("sidebar")[0];
99         if (sidebar) {
100             addClass(sidebar, "mobile");
101             var filler = document.getElementById("sidebar-filler");
102             if (!filler) {
103                 var div = document.createElement("div");
104                 div.id = "sidebar-filler";
105                 sidebar.appendChild(div);
106             }
107         }
108         var themePickers = document.getElementsByClassName("theme-picker");
109         if (themePickers && themePickers.length > 0) {
110             themePickers[0].style.display = "none";
111         }
112     }
113
114     function hideSidebar() {
115         var elems = document.getElementsByClassName("sidebar-elems")[0];
116         if (elems) {
117             removeClass(elems, "show-it");
118         }
119         var sidebar = document.getElementsByClassName("sidebar")[0];
120         removeClass(sidebar, "mobile");
121         var filler = document.getElementById("sidebar-filler");
122         if (filler) {
123             filler.remove();
124         }
125         document.getElementsByTagName("body")[0].style.marginTop = "";
126         var themePickers = document.getElementsByClassName("theme-picker");
127         if (themePickers && themePickers.length > 0) {
128             themePickers[0].style.display = null;
129         }
130     }
131
132     // used for special search precedence
133     var TY_PRIMITIVE = itemTypes.indexOf("primitive");
134     var TY_KEYWORD = itemTypes.indexOf("keyword");
135
136     onEachLazy(document.getElementsByClassName("js-only"), function(e) {
137         removeClass(e, "js-only");
138     });
139
140     function getQueryStringParams() {
141         var params = {};
142         window.location.search.substring(1).split("&").
143             map(function(s) {
144                 var pair = s.split("=");
145                 params[decodeURIComponent(pair[0])] =
146                     typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]);
147             });
148         return params;
149     }
150
151     function browserSupportsHistoryApi() {
152         return window.history && typeof window.history.pushState === "function";
153     }
154
155     var main = document.getElementById("main");
156
157     function highlightSourceLines(ev) {
158         // If we're in mobile mode, we should add the sidebar in any case.
159         hideSidebar();
160         var elem;
161         var search = document.getElementById("search");
162         var i, from, to, match = window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/);
163         if (match) {
164             from = parseInt(match[1], 10);
165             to = from;
166             if (typeof match[2] !== "undefined") {
167                 to = parseInt(match[2], 10);
168             }
169             if (to < from) {
170                 var tmp = to;
171                 to = from;
172                 from = tmp;
173             }
174             elem = document.getElementById(from);
175             if (!elem) {
176                 return;
177             }
178             if (ev === null) {
179                 var x = document.getElementById(from);
180                 if (x) {
181                     x.scrollIntoView();
182                 }
183             }
184             onEachLazy(document.getElementsByClassName("line-numbers"), function(e) {
185                 onEachLazy(e.getElementsByTagName("span"), function(i_e) {
186                     removeClass(i_e, "line-highlighted");
187                 });
188             });
189             for (i = from; i <= to; ++i) {
190                 elem = document.getElementById(i);
191                 if (!elem) {
192                     break;
193                 }
194                 addClass(elem, "line-highlighted");
195             }
196         } else if (ev !== null && search && !hasClass(search, "hidden") && ev.newURL) {
197             addClass(search, "hidden");
198             removeClass(main, "hidden");
199             var hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1);
200             if (browserSupportsHistoryApi()) {
201                 history.replaceState(hash, "", "?search=#" + hash);
202             }
203             elem = document.getElementById(hash);
204             if (elem) {
205                 elem.scrollIntoView();
206             }
207         }
208     }
209
210     function expandSection(id) {
211         var elem = document.getElementById(id);
212         if (elem && isHidden(elem)) {
213             var h3 = elem.parentNode.previousElementSibling;
214             if (h3 && h3.tagName !== "H3") {
215                 h3 = h3.previousElementSibling; // skip div.docblock
216             }
217
218             if (h3) {
219                 var collapses = h3.getElementsByClassName("collapse-toggle");
220                 if (collapses.length > 0) {
221                     // The element is not visible, we need to make it appear!
222                     collapseDocs(collapses[0], "show");
223                 }
224             }
225         }
226     }
227
228     highlightSourceLines(null);
229     window.onhashchange = highlightSourceLines;
230
231     // Gets the human-readable string for the virtual-key code of the
232     // given KeyboardEvent, ev.
233     //
234     // This function is meant as a polyfill for KeyboardEvent#key,
235     // since it is not supported in Trident.  We also test for
236     // KeyboardEvent#keyCode because the handleShortcut handler is
237     // also registered for the keydown event, because Blink doesn't fire
238     // keypress on hitting the Escape key.
239     //
240     // So I guess you could say things are getting pretty interoperable.
241     function getVirtualKey(ev) {
242         if ("key" in ev && typeof ev.key != "undefined") {
243             return ev.key;
244         }
245
246         var c = ev.charCode || ev.keyCode;
247         if (c == 27) {
248             return "Escape";
249         }
250         return String.fromCharCode(c);
251     }
252
253     function displayHelp(display, ev, help) {
254         if (display === true) {
255             if (hasClass(help, "hidden")) {
256                 ev.preventDefault();
257                 removeClass(help, "hidden");
258                 addClass(document.body, "blur");
259             }
260         } else if (hasClass(help, "hidden") === false) {
261             ev.preventDefault();
262             addClass(help, "hidden");
263             removeClass(document.body, "blur");
264         }
265     }
266
267     function handleEscape(ev, help) {
268         hideModal();
269         var search = document.getElementById("search");
270         if (hasClass(help, "hidden") === false) {
271             displayHelp(false, ev, help);
272         } else if (hasClass(search, "hidden") === false) {
273             ev.preventDefault();
274             addClass(search, "hidden");
275             removeClass(main, "hidden");
276             document.title = titleBeforeSearch;
277         }
278         defocusSearchBar();
279     }
280
281     function handleShortcut(ev) {
282         // Don't interfere with browser shortcuts
283         if (ev.ctrlKey || ev.altKey || ev.metaKey) {
284             return;
285         }
286
287         var help = document.getElementById("help");
288         if (document.activeElement.tagName === "INPUT") {
289             switch (getVirtualKey(ev)) {
290             case "Escape":
291                 handleEscape(ev, help);
292                 break;
293             }
294         } else {
295             switch (getVirtualKey(ev)) {
296             case "Escape":
297                 handleEscape(ev, help);
298                 break;
299
300             case "s":
301             case "S":
302                 displayHelp(false, ev, help);
303                 hideModal();
304                 ev.preventDefault();
305                 focusSearchBar();
306                 break;
307
308             case "+":
309             case "-":
310                 ev.preventDefault();
311                 toggleAllDocs();
312                 break;
313
314             case "?":
315                 if (ev.shiftKey) {
316                     hideModal();
317                     displayHelp(true, ev, help);
318                 }
319                 break;
320             }
321         }
322     }
323
324     function findParentElement(elem, tagName) {
325         do {
326             if (elem && elem.tagName === tagName) {
327                 return elem;
328             }
329             elem = elem.parentNode;
330         } while (elem);
331         return null;
332     }
333
334     document.onkeypress = handleShortcut;
335     document.onkeydown = handleShortcut;
336     document.onclick = function(ev) {
337         if (hasClass(ev.target, "collapse-toggle")) {
338             collapseDocs(ev.target, "toggle");
339         } else if (hasClass(ev.target.parentNode, "collapse-toggle")) {
340             collapseDocs(ev.target.parentNode, "toggle");
341         } else if (ev.target.tagName === "SPAN" && hasClass(ev.target.parentNode, "line-numbers")) {
342             var prev_id = 0;
343
344             var set_fragment = function(name) {
345                 if (browserSupportsHistoryApi()) {
346                     history.replaceState(null, null, "#" + name);
347                     window.hashchange();
348                 } else {
349                     location.replace("#" + name);
350                 }
351             };
352
353             var cur_id = parseInt(ev.target.id, 10);
354
355             if (ev.shiftKey && prev_id) {
356                 if (prev_id > cur_id) {
357                     var tmp = prev_id;
358                     prev_id = cur_id;
359                     cur_id = tmp;
360                 }
361
362                 set_fragment(prev_id + "-" + cur_id);
363             } else {
364                 prev_id = cur_id;
365
366                 set_fragment(cur_id);
367             }
368         } else if (hasClass(document.getElementById("help"), "hidden") === false) {
369             addClass(document.getElementById("help"), "hidden");
370             removeClass(document.body, "blur");
371         } else {
372             // Making a collapsed element visible on onhashchange seems
373             // too late
374             var a = findParentElement(ev.target, "A");
375             if (a && a.hash) {
376                 expandSection(a.hash.replace(/^#/, ""));
377             }
378         }
379     };
380
381     var x = document.getElementsByClassName("version-selector");
382     if (x.length > 0) {
383         x[0].onchange = function() {
384             var i, match,
385                 url = document.location.href,
386                 stripped = "",
387                 len = rootPath.match(/\.\.\//g).length + 1;
388
389             for (i = 0; i < len; ++i) {
390                 match = url.match(/\/[^\/]*$/);
391                 if (i < len - 1) {
392                     stripped = match[0] + stripped;
393                 }
394                 url = url.substring(0, url.length - match[0].length);
395             }
396
397             url += "/" + document.getElementsByClassName("version-selector")[0].value + stripped;
398
399             document.location.href = url;
400         };
401     }
402
403     /**
404      * A function to compute the Levenshtein distance between two strings
405      * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported
406      * Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode
407      * This code is an unmodified version of the code written by Marco de Wit
408      * and was found at http://stackoverflow.com/a/18514751/745719
409      */
410     var levenshtein_row2 = [];
411     function levenshtein(s1, s2) {
412         if (s1 === s2) {
413             return 0;
414         }
415         var s1_len = s1.length, s2_len = s2.length;
416         if (s1_len && s2_len) {
417             var i1 = 0, i2 = 0, a, b, c, c2, row = levenshtein_row2;
418             while (i1 < s1_len) {
419                 row[i1] = ++i1;
420             }
421             while (i2 < s2_len) {
422                 c2 = s2.charCodeAt(i2);
423                 a = i2;
424                 ++i2;
425                 b = i2;
426                 for (i1 = 0; i1 < s1_len; ++i1) {
427                     c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0);
428                     a = row[i1];
429                     b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
430                     row[i1] = b;
431                 }
432             }
433             return b;
434         }
435         return s1_len + s2_len;
436     }
437
438     function initSearch(rawSearchIndex) {
439         var currentResults, index, searchIndex;
440         var MAX_LEV_DISTANCE = 3;
441         var MAX_RESULTS = 200;
442         var GENERICS_DATA = 1;
443         var NAME = 0;
444         var INPUTS_DATA = 0;
445         var OUTPUT_DATA = 1;
446         var params = getQueryStringParams();
447
448         // Set the crate filter from saved storage, if the current page has the saved crate filter.
449         //
450         // If not, ignore the crate filter -- we want to support filtering for crates on sites like
451         // doc.rust-lang.org where the crates may differ from page to page while on the same domain.
452         var savedCrate = getCurrentValue("rustdoc-saved-filter-crate");
453         if (savedCrate !== null) {
454             onEachLazy(document.getElementById("crate-search").getElementsByTagName("option"),
455                        function(e) {
456                 if (e.value === savedCrate) {
457                     document.getElementById("crate-search").value = e.value;
458                     return true;
459                 }
460             });
461         }
462
463         // Populate search bar with query string search term when provided,
464         // but only if the input bar is empty. This avoid the obnoxious issue
465         // where you start trying to do a search, and the index loads, and
466         // suddenly your search is gone!
467         if (search_input.value === "") {
468             search_input.value = params.search || "";
469         }
470
471         /**
472          * Executes the query and builds an index of results
473          * @param  {[Object]} query      [The user query]
474          * @param  {[type]} searchWords  [The list of search words to query
475          *                                against]
476          * @param  {[type]} filterCrates [Crate to search in if defined]
477          * @return {[type]}              [A search index of results]
478          */
479         function execQuery(query, searchWords, filterCrates) {
480             function itemTypeFromName(typename) {
481                 var length = itemTypes.length;
482                 for (var i = 0; i < length; ++i) {
483                     if (itemTypes[i] === typename) {
484                         return i;
485                     }
486                 }
487                 return -1;
488             }
489
490             var valLower = query.query.toLowerCase(),
491                 val = valLower,
492                 typeFilter = itemTypeFromName(query.type),
493                 results = {}, results_in_args = {}, results_returned = {},
494                 split = valLower.split("::");
495
496             var length = split.length;
497             for (var z = 0; z < length; ++z) {
498                 if (split[z] === "") {
499                     split.splice(z, 1);
500                     z -= 1;
501                 }
502             }
503
504             function transformResults(results, isType) {
505                 var out = [];
506                 var length = results.length;
507                 for (var i = 0; i < length; ++i) {
508                     if (results[i].id > -1) {
509                         var obj = searchIndex[results[i].id];
510                         obj.lev = results[i].lev;
511                         if (isType !== true || obj.type) {
512                             var res = buildHrefAndPath(obj);
513                             obj.displayPath = pathSplitter(res[0]);
514                             obj.fullPath = obj.displayPath + obj.name;
515                             // To be sure than it some items aren't considered as duplicate.
516                             obj.fullPath += "|" + obj.ty;
517                             obj.href = res[1];
518                             out.push(obj);
519                             if (out.length >= MAX_RESULTS) {
520                                 break;
521                             }
522                         }
523                     }
524                 }
525                 return out;
526             }
527
528             function sortResults(results, isType) {
529                 var ar = [];
530                 for (var entry in results) {
531                     if (results.hasOwnProperty(entry)) {
532                         ar.push(results[entry]);
533                     }
534                 }
535                 results = ar;
536                 var i;
537                 var nresults = results.length;
538                 for (i = 0; i < nresults; ++i) {
539                     results[i].word = searchWords[results[i].id];
540                     results[i].item = searchIndex[results[i].id] || {};
541                 }
542                 // if there are no results then return to default and fail
543                 if (results.length === 0) {
544                     return [];
545                 }
546
547                 results.sort(function(aaa, bbb) {
548                     var a, b;
549
550                     // sort by exact match with regard to the last word (mismatch goes later)
551                     a = (aaa.word !== val);
552                     b = (bbb.word !== val);
553                     if (a !== b) { return a - b; }
554
555                     // Sort by non levenshtein results and then levenshtein results by the distance
556                     // (less changes required to match means higher rankings)
557                     a = (aaa.lev);
558                     b = (bbb.lev);
559                     if (a !== b) { return a - b; }
560
561                     // sort by crate (non-current crate goes later)
562                     a = (aaa.item.crate !== window.currentCrate);
563                     b = (bbb.item.crate !== window.currentCrate);
564                     if (a !== b) { return a - b; }
565
566                     // sort by item name length (longer goes later)
567                     a = aaa.word.length;
568                     b = bbb.word.length;
569                     if (a !== b) { return a - b; }
570
571                     // sort by item name (lexicographically larger goes later)
572                     a = aaa.word;
573                     b = bbb.word;
574                     if (a !== b) { return (a > b ? +1 : -1); }
575
576                     // sort by index of keyword in item name (no literal occurrence goes later)
577                     a = (aaa.index < 0);
578                     b = (bbb.index < 0);
579                     if (a !== b) { return a - b; }
580                     // (later literal occurrence, if any, goes later)
581                     a = aaa.index;
582                     b = bbb.index;
583                     if (a !== b) { return a - b; }
584
585                     // special precedence for primitive and keyword pages
586                     if ((aaa.item.ty === TY_PRIMITIVE && bbb.item.ty !== TY_KEYWORD) ||
587                         (aaa.item.ty === TY_KEYWORD && bbb.item.ty !== TY_PRIMITIVE)) {
588                         return -1;
589                     }
590                     if ((bbb.item.ty === TY_PRIMITIVE && aaa.item.ty !== TY_PRIMITIVE) ||
591                         (bbb.item.ty === TY_KEYWORD && aaa.item.ty !== TY_KEYWORD)) {
592                         return 1;
593                     }
594
595                     // sort by description (no description goes later)
596                     a = (aaa.item.desc === "");
597                     b = (bbb.item.desc === "");
598                     if (a !== b) { return a - b; }
599
600                     // sort by type (later occurrence in `itemTypes` goes later)
601                     a = aaa.item.ty;
602                     b = bbb.item.ty;
603                     if (a !== b) { return a - b; }
604
605                     // sort by path (lexicographically larger goes later)
606                     a = aaa.item.path;
607                     b = bbb.item.path;
608                     if (a !== b) { return (a > b ? +1 : -1); }
609
610                     // que sera, sera
611                     return 0;
612                 });
613
614                 var length = results.length;
615                 for (i = 0; i < length; ++i) {
616                     var result = results[i];
617
618                     // this validation does not make sense when searching by types
619                     if (result.dontValidate) {
620                         continue;
621                     }
622                     var name = result.item.name.toLowerCase(),
623                         path = result.item.path.toLowerCase(),
624                         parent = result.item.parent;
625
626                     if (isType !== true &&
627                         validateResult(name, path, split, parent) === false)
628                     {
629                         result.id = -1;
630                     }
631                 }
632                 return transformResults(results);
633             }
634
635             function extractGenerics(val) {
636                 val = val.toLowerCase();
637                 if (val.indexOf("<") !== -1) {
638                     var values = val.substring(val.indexOf("<") + 1, val.lastIndexOf(">"));
639                     return {
640                         name: val.substring(0, val.indexOf("<")),
641                         generics: values.split(/\s*,\s*/),
642                     };
643                 }
644                 return {
645                     name: val,
646                     generics: [],
647                 };
648             }
649
650             function checkGenerics(obj, val) {
651                 // The names match, but we need to be sure that all generics kinda
652                 // match as well.
653                 var lev_distance = MAX_LEV_DISTANCE + 1;
654                 if (val.generics.length > 0) {
655                     if (obj.length > GENERICS_DATA &&
656                           obj[GENERICS_DATA].length >= val.generics.length) {
657                         var elems = obj[GENERICS_DATA].slice(0);
658                         var total = 0;
659                         var done = 0;
660                         // We need to find the type that matches the most to remove it in order
661                         // to move forward.
662                         var vlength = val.generics.length;
663                         for (var y = 0; y < vlength; ++y) {
664                             var lev = { pos: -1, lev: MAX_LEV_DISTANCE + 1};
665                             var elength = elems.length;
666                             for (var x = 0; x < elength; ++x) {
667                                 var tmp_lev = levenshtein(elems[x], val.generics[y]);
668                                 if (tmp_lev < lev.lev) {
669                                     lev.lev = tmp_lev;
670                                     lev.pos = x;
671                                 }
672                             }
673                             if (lev.pos !== -1) {
674                                 elems.splice(lev.pos, 1);
675                                 lev_distance = Math.min(lev.lev, lev_distance);
676                                 total += lev.lev;
677                                 done += 1;
678                             } else {
679                                 return MAX_LEV_DISTANCE + 1;
680                             }
681                         }
682                         return Math.ceil(total / done);
683                     }
684                 }
685                 return MAX_LEV_DISTANCE + 1;
686             }
687
688             // Check for type name and type generics (if any).
689             function checkType(obj, val, literalSearch) {
690                 var lev_distance = MAX_LEV_DISTANCE + 1;
691                 var x;
692                 if (obj[NAME] === val.name) {
693                     if (literalSearch === true) {
694                         if (val.generics && val.generics.length !== 0) {
695                             if (obj.length > GENERICS_DATA &&
696                                   obj[GENERICS_DATA].length >= val.generics.length) {
697                                 var elems = obj[GENERICS_DATA].slice(0);
698                                 var allFound = true;
699
700                                 for (var y = 0; allFound === true && y < val.generics.length; ++y) {
701                                     allFound = false;
702                                     for (x = 0; allFound === false && x < elems.length; ++x) {
703                                         allFound = elems[x] === val.generics[y];
704                                     }
705                                     if (allFound === true) {
706                                         elems.splice(x - 1, 1);
707                                     }
708                                 }
709                                 if (allFound === true) {
710                                     return true;
711                                 }
712                             } else {
713                                 return false;
714                             }
715                         }
716                         return true;
717                     }
718                     // If the type has generics but don't match, then it won't return at this point.
719                     // Otherwise, `checkGenerics` will return 0 and it'll return.
720                     if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length !== 0) {
721                         var tmp_lev = checkGenerics(obj, val);
722                         if (tmp_lev <= MAX_LEV_DISTANCE) {
723                             return tmp_lev;
724                         }
725                     } else {
726                         return 0;
727                     }
728                 }
729                 // Names didn't match so let's check if one of the generic types could.
730                 if (literalSearch === true) {
731                      if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
732                         var length = obj[GENERICS_DATA].length;
733                         for (x = 0; x < length; ++x) {
734                             if (obj[GENERICS_DATA][x] === val.name) {
735                                 return true;
736                             }
737                         }
738                     }
739                     return false;
740                 }
741                 lev_distance = Math.min(levenshtein(obj[NAME], val.name), lev_distance);
742                 if (lev_distance <= MAX_LEV_DISTANCE) {
743                     // The generics didn't match but the name kinda did so we give it
744                     // a levenshtein distance value that isn't *this* good so it goes
745                     // into the search results but not too high.
746                     lev_distance = Math.ceil((checkGenerics(obj, val) + lev_distance) / 2);
747                 } else if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
748                     // We can check if the type we're looking for is inside the generics!
749                     var olength = obj[GENERICS_DATA].length;
750                     for (x = 0; x < olength; ++x) {
751                         lev_distance = Math.min(levenshtein(obj[GENERICS_DATA][x], val.name),
752                                                 lev_distance);
753                     }
754                 }
755                 // Now whatever happens, the returned distance is "less good" so we should mark it
756                 // as such, and so we add 1 to the distance to make it "less good".
757                 return lev_distance + 1;
758             }
759
760             function findArg(obj, val, literalSearch) {
761                 var lev_distance = MAX_LEV_DISTANCE + 1;
762
763                 if (obj && obj.type && obj.type[INPUTS_DATA] &&
764                       obj.type[INPUTS_DATA].length > 0) {
765                     var length = obj.type[INPUTS_DATA].length;
766                     for (var i = 0; i < length; i++) {
767                         var tmp = checkType(obj.type[INPUTS_DATA][i], val, literalSearch);
768                         if (literalSearch === true && tmp === true) {
769                             return true;
770                         }
771                         lev_distance = Math.min(tmp, lev_distance);
772                         if (lev_distance === 0) {
773                             return 0;
774                         }
775                     }
776                 }
777                 return literalSearch === true ? false : lev_distance;
778             }
779
780             function checkReturned(obj, val, literalSearch) {
781                 var lev_distance = MAX_LEV_DISTANCE + 1;
782
783                 if (obj && obj.type && obj.type.length > OUTPUT_DATA) {
784                     var ret = obj.type[OUTPUT_DATA];
785                     if (!obj.type[OUTPUT_DATA].length) {
786                         ret = [ret];
787                     }
788                     for (var x = 0; x < ret.length; ++x) {
789                         var r = ret[x];
790                         if (typeof r === "string") {
791                             r = [r];
792                         }
793                         var tmp = checkType(r, val, literalSearch);
794                         if (literalSearch === true) {
795                             if (tmp === true) {
796                                 return true;
797                             }
798                             continue;
799                         }
800                         lev_distance = Math.min(tmp, lev_distance);
801                         if (lev_distance === 0) {
802                             return 0;
803                         }
804                     }
805                 }
806                 return literalSearch === true ? false : lev_distance;
807             }
808
809             function checkPath(contains, lastElem, ty) {
810                 if (contains.length === 0) {
811                     return 0;
812                 }
813                 var ret_lev = MAX_LEV_DISTANCE + 1;
814                 var path = ty.path.split("::");
815
816                 if (ty.parent && ty.parent.name) {
817                     path.push(ty.parent.name.toLowerCase());
818                 }
819
820                 var length = path.length;
821                 var clength = contains.length;
822                 if (clength > length) {
823                     return MAX_LEV_DISTANCE + 1;
824                 }
825                 for (var i = 0; i < length; ++i) {
826                     if (i + clength > length) {
827                         break;
828                     }
829                     var lev_total = 0;
830                     var aborted = false;
831                     for (var x = 0; x < clength; ++x) {
832                         var lev = levenshtein(path[i + x], contains[x]);
833                         if (lev > MAX_LEV_DISTANCE) {
834                             aborted = true;
835                             break;
836                         }
837                         lev_total += lev;
838                     }
839                     if (aborted === false) {
840                         ret_lev = Math.min(ret_lev, Math.round(lev_total / clength));
841                     }
842                 }
843                 return ret_lev;
844             }
845
846             function typePassesFilter(filter, type) {
847                 // No filter
848                 if (filter < 0) return true;
849
850                 // Exact match
851                 if (filter === type) return true;
852
853                 // Match related items
854                 var name = itemTypes[type];
855                 switch (itemTypes[filter]) {
856                     case "constant":
857                         return (name == "associatedconstant");
858                     case "fn":
859                         return (name == "method" || name == "tymethod");
860                     case "type":
861                         return (name == "primitive" || name == "keyword");
862                 }
863
864                 // No match
865                 return false;
866             }
867
868             function generateId(ty) {
869                 if (ty.parent && ty.parent.name) {
870                     return itemTypes[ty.ty] + ty.path + ty.parent.name + ty.name;
871                 }
872                 return itemTypes[ty.ty] + ty.path + ty.name;
873             }
874
875             // quoted values mean literal search
876             var nSearchWords = searchWords.length;
877             var i;
878             var ty;
879             var fullId;
880             var returned;
881             var in_args;
882             if ((val.charAt(0) === "\"" || val.charAt(0) === "'") &&
883                 val.charAt(val.length - 1) === val.charAt(0))
884             {
885                 val = extractGenerics(val.substr(1, val.length - 2));
886                 for (i = 0; i < nSearchWords; ++i) {
887                     if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) {
888                         continue;
889                     }
890                     in_args = findArg(searchIndex[i], val, true);
891                     returned = checkReturned(searchIndex[i], val, true);
892                     ty = searchIndex[i];
893                     fullId = generateId(ty);
894
895                     if (searchWords[i] === val.name) {
896                         // filter type: ... queries
897                         if (typePassesFilter(typeFilter, searchIndex[i].ty) &&
898                             results[fullId] === undefined)
899                         {
900                             results[fullId] = {id: i, index: -1};
901                         }
902                     } else if ((in_args === true || returned === true) &&
903                                typePassesFilter(typeFilter, searchIndex[i].ty)) {
904                         if (in_args === true || returned === true) {
905                             if (in_args === true) {
906                                 results_in_args[fullId] = {
907                                     id: i,
908                                     index: -1,
909                                     dontValidate: true,
910                                 };
911                             }
912                             if (returned === true) {
913                                 results_returned[fullId] = {
914                                     id: i,
915                                     index: -1,
916                                     dontValidate: true,
917                                 };
918                             }
919                         } else {
920                             results[fullId] = {
921                                 id: i,
922                                 index: -1,
923                                 dontValidate: true,
924                             };
925                         }
926                     }
927                 }
928                 query.inputs = [val];
929                 query.output = val;
930                 query.search = val;
931             // searching by type
932             } else if (val.search("->") > -1) {
933                 var trimmer = function(s) { return s.trim(); };
934                 var parts = val.split("->").map(trimmer);
935                 var input = parts[0];
936                 // sort inputs so that order does not matter
937                 var inputs = input.split(",").map(trimmer).sort();
938                 for (i = 0; i < inputs.length; ++i) {
939                     inputs[i] = extractGenerics(inputs[i]);
940                 }
941                 var output = extractGenerics(parts[1]);
942
943                 for (i = 0; i < nSearchWords; ++i) {
944                     if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) {
945                         continue;
946                     }
947                     var type = searchIndex[i].type;
948                     ty = searchIndex[i];
949                     if (!type) {
950                         continue;
951                     }
952                     fullId = generateId(ty);
953
954                     // allow searching for void (no output) functions as well
955                     var typeOutput = type.length > OUTPUT_DATA ? type[OUTPUT_DATA].name : "";
956                     returned = checkReturned(ty, output, true);
957                     if (output.name === "*" || returned === true) {
958                         in_args = false;
959                         var is_module = false;
960
961                         if (input === "*") {
962                             is_module = true;
963                         } else {
964                             var allFound = true;
965                             for (var it = 0; allFound === true && it < inputs.length; it++) {
966                                 allFound = checkType(type, inputs[it], true);
967                             }
968                             in_args = allFound;
969                         }
970                         if (in_args === true) {
971                             results_in_args[fullId] = {
972                                 id: i,
973                                 index: -1,
974                                 dontValidate: true,
975                             };
976                         }
977                         if (returned === true) {
978                             results_returned[fullId] = {
979                                 id: i,
980                                 index: -1,
981                                 dontValidate: true,
982                             };
983                         }
984                         if (is_module === true) {
985                             results[fullId] = {
986                                 id: i,
987                                 index: -1,
988                                 dontValidate: true,
989                             };
990                         }
991                     }
992                 }
993                 query.inputs = inputs.map(function(input) {
994                     return input.name;
995                 });
996                 query.output = output.name;
997             } else {
998                 query.inputs = [val];
999                 query.output = val;
1000                 query.search = val;
1001                 // gather matching search results up to a certain maximum
1002                 val = val.replace(/\_/g, "");
1003
1004                 var valGenerics = extractGenerics(val);
1005
1006                 var paths = valLower.split("::");
1007                 var j;
1008                 for (j = 0; j < paths.length; ++j) {
1009                     if (paths[j] === "") {
1010                         paths.splice(j, 1);
1011                         j -= 1;
1012                     }
1013                 }
1014                 val = paths[paths.length - 1];
1015                 var contains = paths.slice(0, paths.length > 1 ? paths.length - 1 : 1);
1016
1017                 for (j = 0; j < nSearchWords; ++j) {
1018                     var lev;
1019                     var lev_distance;
1020                     ty = searchIndex[j];
1021                     if (!ty || (filterCrates !== undefined && ty.crate !== filterCrates)) {
1022                         continue;
1023                     }
1024                     var lev_distance;
1025                     var lev_add = 0;
1026                     if (paths.length > 1) {
1027                         lev = checkPath(contains, paths[paths.length - 1], ty);
1028                         if (lev > MAX_LEV_DISTANCE) {
1029                             continue;
1030                         } else if (lev > 0) {
1031                             lev_add = lev / 10;
1032                         }
1033                     }
1034
1035                     returned = MAX_LEV_DISTANCE + 1;
1036                     in_args = MAX_LEV_DISTANCE + 1;
1037                     var index = -1;
1038                     // we want lev results to go lower than others
1039                     lev = MAX_LEV_DISTANCE + 1;
1040                     fullId = generateId(ty);
1041
1042                     if (searchWords[j].indexOf(split[i]) > -1 ||
1043                         searchWords[j].indexOf(val) > -1 ||
1044                         searchWords[j].replace(/_/g, "").indexOf(val) > -1)
1045                     {
1046                         // filter type: ... queries
1047                         if (typePassesFilter(typeFilter, ty.ty) && results[fullId] === undefined) {
1048                             index = searchWords[j].replace(/_/g, "").indexOf(val);
1049                         }
1050                     }
1051                     if ((lev = levenshtein(searchWords[j], val)) <= MAX_LEV_DISTANCE) {
1052                         if (typePassesFilter(typeFilter, ty.ty) === false) {
1053                             lev = MAX_LEV_DISTANCE + 1;
1054                         } else {
1055                             lev += 1;
1056                         }
1057                     }
1058                     if ((in_args = findArg(ty, valGenerics)) <= MAX_LEV_DISTANCE) {
1059                         if (typePassesFilter(typeFilter, ty.ty) === false) {
1060                             in_args = MAX_LEV_DISTANCE + 1;
1061                         }
1062                     }
1063                     if ((returned = checkReturned(ty, valGenerics)) <= MAX_LEV_DISTANCE) {
1064                         if (typePassesFilter(typeFilter, ty.ty) === false) {
1065                             returned = MAX_LEV_DISTANCE + 1;
1066                         }
1067                     }
1068
1069                     lev += lev_add;
1070                     if (lev > 0 && val.length > 3 && searchWords[j].indexOf(val) > -1) {
1071                         if (val.length < 6) {
1072                             lev -= 1;
1073                         } else {
1074                             lev = 0;
1075                         }
1076                     }
1077                     if (in_args <= MAX_LEV_DISTANCE) {
1078                         if (results_in_args[fullId] === undefined) {
1079                             results_in_args[fullId] = {
1080                                 id: j,
1081                                 index: index,
1082                                 lev: in_args,
1083                             };
1084                         }
1085                         results_in_args[fullId].lev =
1086                             Math.min(results_in_args[fullId].lev, in_args);
1087                     }
1088                     if (returned <= MAX_LEV_DISTANCE) {
1089                         if (results_returned[fullId] === undefined) {
1090                             results_returned[fullId] = {
1091                                 id: j,
1092                                 index: index,
1093                                 lev: returned,
1094                             };
1095                         }
1096                         results_returned[fullId].lev =
1097                             Math.min(results_returned[fullId].lev, returned);
1098                     }
1099                     if (index !== -1 || lev <= MAX_LEV_DISTANCE) {
1100                         if (index !== -1 && paths.length < 2) {
1101                             lev = 0;
1102                         }
1103                         if (results[fullId] === undefined) {
1104                             results[fullId] = {
1105                                 id: j,
1106                                 index: index,
1107                                 lev: lev,
1108                             };
1109                         }
1110                         results[fullId].lev = Math.min(results[fullId].lev, lev);
1111                     }
1112                 }
1113             }
1114
1115             var ret = {
1116                 "in_args": sortResults(results_in_args, true),
1117                 "returned": sortResults(results_returned, true),
1118                 "others": sortResults(results),
1119             };
1120             if (ALIASES && ALIASES[window.currentCrate] &&
1121                     ALIASES[window.currentCrate][query.raw]) {
1122                 var aliases = ALIASES[window.currentCrate][query.raw];
1123                 for (i = 0; i < aliases.length; ++i) {
1124                     aliases[i].is_alias = true;
1125                     aliases[i].alias = query.raw;
1126                     aliases[i].path = aliases[i].p;
1127                     var res = buildHrefAndPath(aliases[i]);
1128                     aliases[i].displayPath = pathSplitter(res[0]);
1129                     aliases[i].fullPath = aliases[i].displayPath + aliases[i].name;
1130                     aliases[i].href = res[1];
1131                     ret.others.unshift(aliases[i]);
1132                     if (ret.others.length > MAX_RESULTS) {
1133                         ret.others.pop();
1134                     }
1135                 }
1136             }
1137             return ret;
1138         }
1139
1140         /**
1141          * Validate performs the following boolean logic. For example:
1142          * "File::open" will give IF A PARENT EXISTS => ("file" && "open")
1143          * exists in (name || path || parent) OR => ("file" && "open") exists in
1144          * (name || path )
1145          *
1146          * This could be written functionally, but I wanted to minimise
1147          * functions on stack.
1148          *
1149          * @param  {[string]} name   [The name of the result]
1150          * @param  {[string]} path   [The path of the result]
1151          * @param  {[string]} keys   [The keys to be used (["file", "open"])]
1152          * @param  {[object]} parent [The parent of the result]
1153          * @return {[boolean]}       [Whether the result is valid or not]
1154          */
1155         function validateResult(name, path, keys, parent) {
1156             for (var i = 0; i < keys.length; ++i) {
1157                 // each check is for validation so we negate the conditions and invalidate
1158                 if (!(
1159                     // check for an exact name match
1160                     name.indexOf(keys[i]) > -1 ||
1161                     // then an exact path match
1162                     path.indexOf(keys[i]) > -1 ||
1163                     // next if there is a parent, check for exact parent match
1164                     (parent !== undefined &&
1165                         parent.name.toLowerCase().indexOf(keys[i]) > -1) ||
1166                     // lastly check to see if the name was a levenshtein match
1167                     levenshtein(name, keys[i]) <= MAX_LEV_DISTANCE)) {
1168                     return false;
1169                 }
1170             }
1171             return true;
1172         }
1173
1174         function getQuery(raw) {
1175             var matches, type, query;
1176             query = raw;
1177
1178             matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i);
1179             if (matches) {
1180                 type = matches[1].replace(/^const$/, "constant");
1181                 query = query.substring(matches[0].length);
1182             }
1183
1184             return {
1185                 raw: raw,
1186                 query: query,
1187                 type: type,
1188                 id: query + type
1189             };
1190         }
1191
1192         function initSearchNav() {
1193             var hoverTimeout;
1194
1195             var click_func = function(e) {
1196                 var el = e.target;
1197                 // to retrieve the real "owner" of the event.
1198                 while (el.tagName !== "TR") {
1199                     el = el.parentNode;
1200                 }
1201                 var dst = e.target.getElementsByTagName("a");
1202                 if (dst.length < 1) {
1203                     return;
1204                 }
1205                 dst = dst[0];
1206                 if (window.location.pathname === dst.pathname) {
1207                     addClass(document.getElementById("search"), "hidden");
1208                     removeClass(main, "hidden");
1209                     document.location.href = dst.href;
1210                 }
1211             };
1212             var mouseover_func = function(e) {
1213                 var el = e.target;
1214                 // to retrieve the real "owner" of the event.
1215                 while (el.tagName !== "TR") {
1216                     el = el.parentNode;
1217                 }
1218                 clearTimeout(hoverTimeout);
1219                 hoverTimeout = setTimeout(function() {
1220                     onEachLazy(document.getElementsByClassName("search-results"), function(e) {
1221                         onEachLazy(e.getElementsByClassName("result"), function(i_e) {
1222                             removeClass(i_e, "highlighted");
1223                         });
1224                     });
1225                     addClass(el, "highlighted");
1226                 }, 20);
1227             };
1228             onEachLazy(document.getElementsByClassName("search-results"), function(e) {
1229                 onEachLazy(e.getElementsByClassName("result"), function(i_e) {
1230                     i_e.onclick = click_func;
1231                     i_e.onmouseover = mouseover_func;
1232                 });
1233             });
1234
1235             search_input.onkeydown = function(e) {
1236                 // "actives" references the currently highlighted item in each search tab.
1237                 // Each array in "actives" represents a tab.
1238                 var actives = [[], [], []];
1239                 // "current" is used to know which tab we're looking into.
1240                 var current = 0;
1241                 onEachLazy(document.getElementById("results").childNodes, function(e) {
1242                     onEachLazy(e.getElementsByClassName("highlighted"), function(e) {
1243                         actives[current].push(e);
1244                     });
1245                     current += 1;
1246                 });
1247
1248                 if (e.which === 38) { // up
1249                     if (!actives[currentTab].length ||
1250                         !actives[currentTab][0].previousElementSibling) {
1251                         return;
1252                     }
1253
1254                     addClass(actives[currentTab][0].previousElementSibling, "highlighted");
1255                     removeClass(actives[currentTab][0], "highlighted");
1256                 } else if (e.which === 40) { // down
1257                     if (!actives[currentTab].length) {
1258                         var results = document.getElementById("results").childNodes;
1259                         if (results.length > 0) {
1260                             var res = results[currentTab].getElementsByClassName("result");
1261                             if (res.length > 0) {
1262                                 addClass(res[0], "highlighted");
1263                             }
1264                         }
1265                     } else if (actives[currentTab][0].nextElementSibling) {
1266                         addClass(actives[currentTab][0].nextElementSibling, "highlighted");
1267                         removeClass(actives[currentTab][0], "highlighted");
1268                     }
1269                 } else if (e.which === 13) { // return
1270                     if (actives[currentTab].length) {
1271                         document.location.href =
1272                             actives[currentTab][0].getElementsByTagName("a")[0].href;
1273                     }
1274                 } else if (e.which === 9) { // tab
1275                     if (e.shiftKey) {
1276                         printTab(currentTab > 0 ? currentTab - 1 : 2);
1277                     } else {
1278                         printTab(currentTab > 1 ? 0 : currentTab + 1);
1279                     }
1280                     e.preventDefault();
1281                 } else if (e.which === 16) { // shift
1282                     // Does nothing, it's just to avoid losing "focus" on the highlighted element.
1283                 } else if (e.which === 27) { // escape
1284                     removeClass(actives[currentTab][0], "highlighted");
1285                     search_input.value = "";
1286                     defocusSearchBar();
1287                 } else if (actives[currentTab].length > 0) {
1288                     removeClass(actives[currentTab][0], "highlighted");
1289                 }
1290             };
1291         }
1292
1293         function buildHrefAndPath(item) {
1294             var displayPath;
1295             var href;
1296             var type = itemTypes[item.ty];
1297             var name = item.name;
1298
1299             if (type === "mod") {
1300                 displayPath = item.path + "::";
1301                 href = rootPath + item.path.replace(/::/g, "/") + "/" +
1302                        name + "/index.html";
1303             } else if (type === "primitive" || type === "keyword") {
1304                 displayPath = "";
1305                 href = rootPath + item.path.replace(/::/g, "/") +
1306                        "/" + type + "." + name + ".html";
1307             } else if (type === "externcrate") {
1308                 displayPath = "";
1309                 href = rootPath + name + "/index.html";
1310             } else if (item.parent !== undefined) {
1311                 var myparent = item.parent;
1312                 var anchor = "#" + type + "." + name;
1313                 var parentType = itemTypes[myparent.ty];
1314                 if (parentType === "primitive") {
1315                     displayPath = myparent.name + "::";
1316                 } else {
1317                     displayPath = item.path + "::" + myparent.name + "::";
1318                 }
1319                 href = rootPath + item.path.replace(/::/g, "/") +
1320                        "/" + parentType +
1321                        "." + myparent.name +
1322                        ".html" + anchor;
1323             } else {
1324                 displayPath = item.path + "::";
1325                 href = rootPath + item.path.replace(/::/g, "/") +
1326                        "/" + type + "." + name + ".html";
1327             }
1328             return [displayPath, href];
1329         }
1330
1331         function escape(content) {
1332             var h1 = document.createElement("h1");
1333             h1.textContent = content;
1334             return h1.innerHTML;
1335         }
1336
1337         function pathSplitter(path) {
1338             var tmp = "<span>" + path.replace(/::/g, "::</span><span>");
1339             if (tmp.endsWith("<span>")) {
1340                 return tmp.slice(0, tmp.length - 6);
1341             }
1342             return tmp;
1343         }
1344
1345         function addTab(array, query, display) {
1346             var extraStyle = "";
1347             if (display === false) {
1348                 extraStyle = " style=\"display: none;\"";
1349             }
1350
1351             var output = "";
1352             var duplicates = {};
1353             var length = 0;
1354             if (array.length > 0) {
1355                 output = "<table class=\"search-results\"" + extraStyle + ">";
1356
1357                 array.forEach(function(item) {
1358                     var name, type;
1359
1360                     name = item.name;
1361                     type = itemTypes[item.ty];
1362
1363                     if (item.is_alias !== true) {
1364                         if (duplicates[item.fullPath]) {
1365                             return;
1366                         }
1367                         duplicates[item.fullPath] = true;
1368                     }
1369                     length += 1;
1370
1371                     output += "<tr class=\"" + type + " result\"><td>" +
1372                               "<a href=\"" + item.href + "\">" +
1373                               (item.is_alias === true ?
1374                                ("<span class=\"alias\"><b>" + item.alias + " </b></span><span " +
1375                                   "class=\"grey\"><i>&nbsp;- see&nbsp;</i></span>") : "") +
1376                               item.displayPath + "<span class=\"" + type + "\">" +
1377                               name + "</span></a></td><td>" +
1378                               "<a href=\"" + item.href + "\">" +
1379                               "<span class=\"desc\">" + escape(item.desc) +
1380                               "&nbsp;</span></a></td></tr>";
1381                 });
1382                 output += "</table>";
1383             } else {
1384                 output = "<div class=\"search-failed\"" + extraStyle + ">No results :(<br/>" +
1385                     "Try on <a href=\"https://duckduckgo.com/?q=" +
1386                     encodeURIComponent("rust " + query.query) +
1387                     "\">DuckDuckGo</a>?<br/><br/>" +
1388                     "Or try looking in one of these:<ul><li>The <a " +
1389                     "href=\"https://doc.rust-lang.org/reference/index.html\">Rust Reference</a> " +
1390                     " for technical details about the language.</li><li><a " +
1391                     "href=\"https://doc.rust-lang.org/rust-by-example/index.html\">Rust By " +
1392                     "Example</a> for expository code examples.</a></li><li>The <a " +
1393                     "href=\"https://doc.rust-lang.org/book/index.html\">Rust Book</a> for " +
1394                     "introductions to language features and the language itself.</li><li><a " +
1395                     "href=\"https://docs.rs\">Docs.rs</a> for documentation of crates released on" +
1396                     " <a href=\"https://crates.io/\">crates.io</a>.</li></ul></div>";
1397             }
1398             return [output, length];
1399         }
1400
1401         function makeTabHeader(tabNb, text, nbElems) {
1402             if (currentTab === tabNb) {
1403                 return "<div class=\"selected\">" + text +
1404                        " <div class=\"count\">(" + nbElems + ")</div></div>";
1405             }
1406             return "<div>" + text + " <div class=\"count\">(" + nbElems + ")</div></div>";
1407         }
1408
1409         function showResults(results) {
1410             if (results.others.length === 1 &&
1411                 getCurrentValue("rustdoc-go-to-only-result") === "true") {
1412                 var elem = document.createElement("a");
1413                 elem.href = results.others[0].href;
1414                 elem.style.display = "none";
1415                 // For firefox, we need the element to be in the DOM so it can be clicked.
1416                 document.body.appendChild(elem);
1417                 elem.click();
1418             }
1419             var query = getQuery(search_input.value);
1420
1421             currentResults = query.id;
1422
1423             var ret_others = addTab(results.others, query);
1424             var ret_in_args = addTab(results.in_args, query, false);
1425             var ret_returned = addTab(results.returned, query, false);
1426
1427             var output = "<h1>Results for " + escape(query.query) +
1428                 (query.type ? " (type: " + escape(query.type) + ")" : "") + "</h1>" +
1429                 "<div id=\"titles\">" +
1430                 makeTabHeader(0, "In Names", ret_others[1]) +
1431                 makeTabHeader(1, "In Parameters", ret_in_args[1]) +
1432                 makeTabHeader(2, "In Return Types", ret_returned[1]) +
1433                 "</div><div id=\"results\">" +
1434                 ret_others[0] + ret_in_args[0] + ret_returned[0] + "</div>";
1435
1436             addClass(main, "hidden");
1437             var search = document.getElementById("search");
1438             removeClass(search, "hidden");
1439             search.innerHTML = output;
1440             var tds = search.getElementsByTagName("td");
1441             var td_width = 0;
1442             if (tds.length > 0) {
1443                 td_width = tds[0].offsetWidth;
1444             }
1445             var width = search.offsetWidth - 40 - td_width;
1446             onEachLazy(search.getElementsByClassName("desc"), function(e) {
1447                 e.style.width = width + "px";
1448             });
1449             initSearchNav();
1450             var elems = document.getElementById("titles").childNodes;
1451             elems[0].onclick = function() { printTab(0); };
1452             elems[1].onclick = function() { printTab(1); };
1453             elems[2].onclick = function() { printTab(2); };
1454             printTab(currentTab);
1455         }
1456
1457         function execSearch(query, searchWords, filterCrates) {
1458             function getSmallest(arrays, positions, notDuplicates) {
1459                 var start = null;
1460
1461                 for (var it = 0; it < positions.length; ++it) {
1462                     if (arrays[it].length > positions[it] &&
1463                         (start === null || start > arrays[it][positions[it]].lev) &&
1464                         !notDuplicates[arrays[it][positions[it]].fullPath]) {
1465                         start = arrays[it][positions[it]].lev;
1466                     }
1467                 }
1468                 return start;
1469             }
1470
1471             function mergeArrays(arrays) {
1472                 var ret = [];
1473                 var positions = [];
1474                 var notDuplicates = {};
1475
1476                 for (var x = 0; x < arrays.length; ++x) {
1477                     positions.push(0);
1478                 }
1479                 while (ret.length < MAX_RESULTS) {
1480                     var smallest = getSmallest(arrays, positions, notDuplicates);
1481
1482                     if (smallest === null) {
1483                         break;
1484                     }
1485                     for (x = 0; x < arrays.length && ret.length < MAX_RESULTS; ++x) {
1486                         if (arrays[x].length > positions[x] &&
1487                                 arrays[x][positions[x]].lev === smallest &&
1488                                 !notDuplicates[arrays[x][positions[x]].fullPath]) {
1489                             ret.push(arrays[x][positions[x]]);
1490                             notDuplicates[arrays[x][positions[x]].fullPath] = true;
1491                             positions[x] += 1;
1492                         }
1493                     }
1494                 }
1495                 return ret;
1496             }
1497
1498             var queries = query.raw.split(",");
1499             var results = {
1500                 "in_args": [],
1501                 "returned": [],
1502                 "others": [],
1503             };
1504
1505             for (var i = 0; i < queries.length; ++i) {
1506                 query = queries[i].trim();
1507                 if (query.length !== 0) {
1508                     var tmp = execQuery(getQuery(query), searchWords, filterCrates);
1509
1510                     results.in_args.push(tmp.in_args);
1511                     results.returned.push(tmp.returned);
1512                     results.others.push(tmp.others);
1513                 }
1514             }
1515             if (queries.length > 1) {
1516                 return {
1517                     "in_args": mergeArrays(results.in_args),
1518                     "returned": mergeArrays(results.returned),
1519                     "others": mergeArrays(results.others),
1520                 };
1521             } else {
1522                 return {
1523                     "in_args": results.in_args[0],
1524                     "returned": results.returned[0],
1525                     "others": results.others[0],
1526                 };
1527             }
1528         }
1529
1530         function getFilterCrates() {
1531             var elem = document.getElementById("crate-search");
1532
1533             if (elem && elem.value !== "All crates" && rawSearchIndex.hasOwnProperty(elem.value)) {
1534                 return elem.value;
1535             }
1536             return undefined;
1537         }
1538
1539         function search(e, forced) {
1540             var params = getQueryStringParams();
1541             var query = getQuery(search_input.value.trim());
1542
1543             if (e) {
1544                 e.preventDefault();
1545             }
1546
1547             if (query.query.length === 0) {
1548                 return;
1549             }
1550             if (forced !== true && query.id === currentResults) {
1551                 if (query.query.length > 0) {
1552                     putBackSearch(search_input);
1553                 }
1554                 return;
1555             }
1556
1557             // Update document title to maintain a meaningful browser history
1558             document.title = "Results for " + query.query + " - Rust";
1559
1560             // Because searching is incremental by character, only the most
1561             // recent search query is added to the browser history.
1562             if (browserSupportsHistoryApi()) {
1563                 if (!history.state && !params.search) {
1564                     history.pushState(query, "", "?search=" + encodeURIComponent(query.raw));
1565                 } else {
1566                     history.replaceState(query, "", "?search=" + encodeURIComponent(query.raw));
1567                 }
1568             }
1569
1570             var filterCrates = getFilterCrates();
1571             showResults(execSearch(query, index, filterCrates), filterCrates);
1572         }
1573
1574         function buildIndex(rawSearchIndex) {
1575             searchIndex = [];
1576             var searchWords = [];
1577             var i;
1578
1579             for (var crate in rawSearchIndex) {
1580                 if (!rawSearchIndex.hasOwnProperty(crate)) { continue; }
1581
1582                 searchWords.push(crate);
1583                 searchIndex.push({
1584                     crate: crate,
1585                     ty: 1, // == ExternCrate
1586                     name: crate,
1587                     path: "",
1588                     desc: rawSearchIndex[crate].doc,
1589                     type: null,
1590                 });
1591
1592                 // an array of [(Number) item type,
1593                 //              (String) name,
1594                 //              (String) full path or empty string for previous path,
1595                 //              (String) description,
1596                 //              (Number | null) the parent path index to `paths`]
1597                 //              (Object | null) the type of the function (if any)
1598                 var items = rawSearchIndex[crate].i;
1599                 // an array of [(Number) item type,
1600                 //              (String) name]
1601                 var paths = rawSearchIndex[crate].p;
1602
1603                 // convert `paths` into an object form
1604                 var len = paths.length;
1605                 for (i = 0; i < len; ++i) {
1606                     paths[i] = {ty: paths[i][0], name: paths[i][1]};
1607                 }
1608
1609                 // convert `items` into an object form, and construct word indices.
1610                 //
1611                 // before any analysis is performed lets gather the search terms to
1612                 // search against apart from the rest of the data.  This is a quick
1613                 // operation that is cached for the life of the page state so that
1614                 // all other search operations have access to this cached data for
1615                 // faster analysis operations
1616                 len = items.length;
1617                 var lastPath = "";
1618                 for (i = 0; i < len; ++i) {
1619                     var rawRow = items[i];
1620                     var row = {crate: crate, ty: rawRow[0], name: rawRow[1],
1621                                path: rawRow[2] || lastPath, desc: rawRow[3],
1622                                parent: paths[rawRow[4]], type: rawRow[5]};
1623                     searchIndex.push(row);
1624                     if (typeof row.name === "string") {
1625                         var word = row.name.toLowerCase();
1626                         searchWords.push(word);
1627                     } else {
1628                         searchWords.push("");
1629                     }
1630                     lastPath = row.path;
1631                 }
1632             }
1633             return searchWords;
1634         }
1635
1636         function startSearch() {
1637             var searchTimeout;
1638             var callback = function() {
1639                 clearTimeout(searchTimeout);
1640                 if (search_input.value.length === 0) {
1641                     if (browserSupportsHistoryApi()) {
1642                         history.replaceState("", window.currentCrate + " - Rust", "?search=");
1643                     }
1644                     if (hasClass(main, "content")) {
1645                         removeClass(main, "hidden");
1646                     }
1647                     var search_c = document.getElementById("search");
1648                     if (hasClass(search_c, "content")) {
1649                         addClass(search_c, "hidden");
1650                     }
1651                 } else {
1652                     searchTimeout = setTimeout(search, 500);
1653                 }
1654             };
1655             search_input.onkeyup = callback;
1656             search_input.oninput = callback;
1657             document.getElementsByClassName("search-form")[0].onsubmit = function(e) {
1658                 e.preventDefault();
1659                 clearTimeout(searchTimeout);
1660                 search();
1661             };
1662             search_input.onchange = function(e) {
1663                 // Do NOT e.preventDefault() here. It will prevent pasting.
1664                 clearTimeout(searchTimeout);
1665                 // zero-timeout necessary here because at the time of event handler execution the
1666                 // pasted content is not in the input field yet. Shouldn’t make any difference for
1667                 // change, though.
1668                 setTimeout(search, 0);
1669             };
1670             search_input.onpaste = search_input.onchange;
1671
1672             var selectCrate = document.getElementById("crate-search");
1673             if (selectCrate) {
1674                 selectCrate.onchange = function() {
1675                     updateLocalStorage("rustdoc-saved-filter-crate", selectCrate.value);
1676                     search(undefined, true);
1677                 };
1678             }
1679
1680             // Push and pop states are used to add search results to the browser
1681             // history.
1682             if (browserSupportsHistoryApi()) {
1683                 // Store the previous <title> so we can revert back to it later.
1684                 var previousTitle = document.title;
1685
1686                 window.onpopstate = function(e) {
1687                     var params = getQueryStringParams();
1688                     // When browsing back from search results the main page
1689                     // visibility must be reset.
1690                     if (!params.search) {
1691                         if (hasClass(main, "content")) {
1692                             removeClass(main, "hidden");
1693                         }
1694                         var search_c = document.getElementById("search");
1695                         if (hasClass(search_c, "content")) {
1696                             addClass(search_c, "hidden");
1697                         }
1698                     }
1699                     // Revert to the previous title manually since the History
1700                     // API ignores the title parameter.
1701                     document.title = previousTitle;
1702                     // When browsing forward to search results the previous
1703                     // search will be repeated, so the currentResults are
1704                     // cleared to ensure the search is successful.
1705                     currentResults = null;
1706                     // Synchronize search bar with query string state and
1707                     // perform the search. This will empty the bar if there's
1708                     // nothing there, which lets you really go back to a
1709                     // previous state with nothing in the bar.
1710                     if (params.search) {
1711                         search_input.value = params.search;
1712                     } else {
1713                         search_input.value = "";
1714                     }
1715                     // Some browsers fire "onpopstate" for every page load
1716                     // (Chrome), while others fire the event only when actually
1717                     // popping a state (Firefox), which is why search() is
1718                     // called both here and at the end of the startSearch()
1719                     // function.
1720                     search();
1721                 };
1722             }
1723             search();
1724         }
1725
1726         index = buildIndex(rawSearchIndex);
1727         startSearch();
1728
1729         // Draw a convenient sidebar of known crates if we have a listing
1730         if (rootPath === "../" || rootPath === "./") {
1731             var sidebar = document.getElementsByClassName("sidebar-elems")[0];
1732             if (sidebar) {
1733                 var div = document.createElement("div");
1734                 div.className = "block crate";
1735                 div.innerHTML = "<h3>Crates</h3>";
1736                 var ul = document.createElement("ul");
1737                 div.appendChild(ul);
1738
1739                 var crates = [];
1740                 for (var crate in rawSearchIndex) {
1741                     if (!rawSearchIndex.hasOwnProperty(crate)) {
1742                         continue;
1743                     }
1744                     crates.push(crate);
1745                 }
1746                 crates.sort();
1747                 for (var i = 0; i < crates.length; ++i) {
1748                     var klass = "crate";
1749                     if (rootPath !== "./" && crates[i] === window.currentCrate) {
1750                         klass += " current";
1751                     }
1752                     var link = document.createElement("a");
1753                     link.href = rootPath + crates[i] + "/index.html";
1754                     link.title = rawSearchIndex[crates[i]].doc;
1755                     link.className = klass;
1756                     link.textContent = crates[i];
1757
1758                     var li = document.createElement("li");
1759                     li.appendChild(link);
1760                     ul.appendChild(li);
1761                 }
1762                 sidebar.appendChild(div);
1763             }
1764         }
1765     }
1766
1767     window.initSearch = initSearch;
1768
1769     // delayed sidebar rendering.
1770     function initSidebarItems(items) {
1771         var sidebar = document.getElementsByClassName("sidebar-elems")[0];
1772         var current = window.sidebarCurrent;
1773
1774         function block(shortty, longty) {
1775             var filtered = items[shortty];
1776             if (!filtered) {
1777                 return;
1778             }
1779
1780             var div = document.createElement("div");
1781             div.className = "block " + shortty;
1782             var h3 = document.createElement("h3");
1783             h3.textContent = longty;
1784             div.appendChild(h3);
1785             var ul = document.createElement("ul");
1786
1787             var length = filtered.length;
1788             for (var i = 0; i < length; ++i) {
1789                 var item = filtered[i];
1790                 var name = item[0];
1791                 var desc = item[1]; // can be null
1792
1793                 var klass = shortty;
1794                 if (name === current.name && shortty === current.ty) {
1795                     klass += " current";
1796                 }
1797                 var path;
1798                 if (shortty === "mod") {
1799                     path = name + "/index.html";
1800                 } else {
1801                     path = shortty + "." + name + ".html";
1802                 }
1803                 var link = document.createElement("a");
1804                 link.href = current.relpath + path;
1805                 link.title = desc;
1806                 link.className = klass;
1807                 link.textContent = name;
1808                 var li = document.createElement("li");
1809                 li.appendChild(link);
1810                 ul.appendChild(li);
1811             }
1812             div.appendChild(ul);
1813             if (sidebar) {
1814                 sidebar.appendChild(div);
1815             }
1816         }
1817
1818         block("primitive", "Primitive Types");
1819         block("mod", "Modules");
1820         block("macro", "Macros");
1821         block("struct", "Structs");
1822         block("enum", "Enums");
1823         block("union", "Unions");
1824         block("constant", "Constants");
1825         block("static", "Statics");
1826         block("trait", "Traits");
1827         block("fn", "Functions");
1828         block("type", "Type Definitions");
1829         block("foreigntype", "Foreign Types");
1830         block("keyword", "Keywords");
1831         block("traitalias", "Trait Aliases");
1832     }
1833
1834     window.initSidebarItems = initSidebarItems;
1835
1836     window.register_implementors = function(imp) {
1837         var implementors = document.getElementById("implementors-list");
1838         var synthetic_implementors = document.getElementById("synthetic-implementors-list");
1839
1840         var libs = Object.getOwnPropertyNames(imp);
1841         var llength = libs.length;
1842         for (var i = 0; i < llength; ++i) {
1843             if (libs[i] === currentCrate) { continue; }
1844             var structs = imp[libs[i]];
1845
1846             var slength = structs.length;
1847             struct_loop:
1848             for (var j = 0; j < slength; ++j) {
1849                 var struct = structs[j];
1850
1851                 var list = struct.synthetic ? synthetic_implementors : implementors;
1852
1853                 if (struct.synthetic) {
1854                     var stlength = struct.types.length;
1855                     for (var k = 0; k < stlength; k++) {
1856                         if (window.inlined_types.has(struct.types[k])) {
1857                             continue struct_loop;
1858                         }
1859                         window.inlined_types.add(struct.types[k]);
1860                     }
1861                 }
1862
1863                 var code = document.createElement("code");
1864                 code.innerHTML = struct.text;
1865
1866                 var x = code.getElementsByTagName("a");
1867                 var xlength = x.length;
1868                 for (var it = 0; it < xlength; it++) {
1869                     var href = x[it].getAttribute("href");
1870                     if (href && href.indexOf("http") !== 0) {
1871                         x[it].setAttribute("href", rootPath + href);
1872                     }
1873                 }
1874                 var display = document.createElement("h3");
1875                 addClass(display, "impl");
1876                 display.innerHTML = "<span class=\"in-band\"><table class=\"table-display\">" +
1877                     "<tbody><tr><td><code>" + code.outerHTML + "</code></td><td></td></tr>" +
1878                     "</tbody></table></span>";
1879                 list.appendChild(display);
1880             }
1881         }
1882     };
1883     if (window.pending_implementors) {
1884         window.register_implementors(window.pending_implementors);
1885     }
1886
1887     function labelForToggleButton(sectionIsCollapsed) {
1888         if (sectionIsCollapsed) {
1889             // button will expand the section
1890             return "+";
1891         }
1892         // button will collapse the section
1893         // note that this text is also set in the HTML template in render.rs
1894         return "\u2212"; // "\u2212" is "−" minus sign
1895     }
1896
1897     function onEveryMatchingChild(elem, className, func) {
1898         if (elem && className && func) {
1899             var length = elem.childNodes.length;
1900             var nodes = elem.childNodes;
1901             for (var i = 0; i < length; ++i) {
1902                 if (hasClass(nodes[i], className)) {
1903                     func(nodes[i]);
1904                 } else {
1905                     onEveryMatchingChild(nodes[i], className, func);
1906                 }
1907             }
1908         }
1909     }
1910
1911     function toggleAllDocs(pageId, fromAutoCollapse) {
1912         var innerToggle = document.getElementById("toggle-all-docs");
1913         if (!innerToggle) {
1914             return;
1915         }
1916         if (hasClass(innerToggle, "will-expand")) {
1917             updateLocalStorage("rustdoc-collapse", "false");
1918             removeClass(innerToggle, "will-expand");
1919             onEveryMatchingChild(innerToggle, "inner", function(e) {
1920                 e.innerHTML = labelForToggleButton(false);
1921             });
1922             innerToggle.title = "collapse all docs";
1923             if (fromAutoCollapse !== true) {
1924                 onEachLazy(document.getElementsByClassName("collapse-toggle"), function(e) {
1925                     collapseDocs(e, "show");
1926                 });
1927             }
1928         } else {
1929             updateLocalStorage("rustdoc-collapse", "true");
1930             addClass(innerToggle, "will-expand");
1931             onEveryMatchingChild(innerToggle, "inner", function(e) {
1932                 var parent = e.parentNode;
1933                 var superParent = null;
1934
1935                 if (parent) {
1936                     superParent = parent.parentNode;
1937                 }
1938                 if (!parent || !superParent || superParent.id !== "main" ||
1939                     hasClass(parent, "impl") === false) {
1940                     e.innerHTML = labelForToggleButton(true);
1941                 }
1942             });
1943             innerToggle.title = "expand all docs";
1944             if (fromAutoCollapse !== true) {
1945                 onEachLazy(document.getElementsByClassName("collapse-toggle"), function(e) {
1946                     var parent = e.parentNode;
1947                     var superParent = null;
1948
1949                     if (parent) {
1950                         superParent = parent.parentNode;
1951                     }
1952                     if (!parent || !superParent || superParent.id !== "main" ||
1953                         hasClass(parent, "impl") === false) {
1954                         collapseDocs(e, "hide", pageId);
1955                     }
1956                 });
1957             }
1958         }
1959     }
1960
1961     function collapseDocs(toggle, mode, pageId) {
1962         if (!toggle || !toggle.parentNode) {
1963             return;
1964         }
1965
1966         function adjustToggle(arg) {
1967             return function(e) {
1968                 if (hasClass(e, "toggle-label")) {
1969                     if (arg) {
1970                         e.style.display = "inline-block";
1971                     } else {
1972                         e.style.display = "none";
1973                     }
1974                 }
1975                 if (hasClass(e, "inner")) {
1976                     e.innerHTML = labelForToggleButton(arg);
1977                 }
1978             };
1979         }
1980
1981         function implHider(addOrRemove, fullHide) {
1982             return function(n) {
1983                 var is_method = hasClass(n, "method") || fullHide;
1984                 if (is_method || hasClass(n, "type")) {
1985                     if (is_method === true) {
1986                         if (addOrRemove) {
1987                             addClass(n, "hidden-by-impl-hider");
1988                         } else {
1989                             removeClass(n, "hidden-by-impl-hider");
1990                         }
1991                     }
1992                     var ns = n.nextElementSibling;
1993                     while (true) {
1994                         if (ns && (
1995                                 hasClass(ns, "docblock") ||
1996                                 hasClass(ns, "stability"))) {
1997                             if (addOrRemove) {
1998                                 addClass(ns, "hidden-by-impl-hider");
1999                             } else {
2000                                 removeClass(ns, "hidden-by-impl-hider");
2001                             }
2002                             ns = ns.nextElementSibling;
2003                             continue;
2004                         }
2005                         break;
2006                     }
2007                 }
2008             };
2009         }
2010
2011         var relatedDoc;
2012         var action = mode;
2013         if (hasClass(toggle.parentNode, "impl") === false) {
2014             relatedDoc = toggle.parentNode.nextElementSibling;
2015             if (hasClass(relatedDoc, "stability")) {
2016                 relatedDoc = relatedDoc.nextElementSibling;
2017             }
2018             if (hasClass(relatedDoc, "docblock") || hasClass(relatedDoc, "sub-variant")) {
2019                 if (mode === "toggle") {
2020                     if (hasClass(relatedDoc, "hidden-by-usual-hider")) {
2021                         action = "show";
2022                     } else {
2023                         action = "hide";
2024                     }
2025                 }
2026                 if (action === "hide") {
2027                     addClass(relatedDoc, "hidden-by-usual-hider");
2028                     onEachLazy(toggle.childNodes, adjustToggle(true));
2029                     addClass(toggle.parentNode, "collapsed");
2030                 } else if (action === "show") {
2031                     removeClass(relatedDoc, "hidden-by-usual-hider");
2032                     removeClass(toggle.parentNode, "collapsed");
2033                     onEachLazy(toggle.childNodes, adjustToggle(false));
2034                 }
2035             }
2036         } else {
2037             // we are collapsing the impl block(s).
2038
2039             var parentElem = toggle.parentNode;
2040             relatedDoc = parentElem;
2041             var docblock = relatedDoc.nextElementSibling;
2042
2043             while (hasClass(relatedDoc, "impl-items") === false) {
2044                 relatedDoc = relatedDoc.nextElementSibling;
2045             }
2046
2047             if ((!relatedDoc && hasClass(docblock, "docblock") === false) ||
2048                 (pageId && document.getElementById(pageId))) {
2049                 return;
2050             }
2051
2052             // Hide all functions, but not associated types/consts.
2053
2054             if (mode === "toggle") {
2055                 if (hasClass(relatedDoc, "fns-now-collapsed") ||
2056                     hasClass(docblock, "hidden-by-impl-hider")) {
2057                     action = "show";
2058                 } else {
2059                     action = "hide";
2060                 }
2061             }
2062
2063             var dontApplyBlockRule = toggle.parentNode.parentNode.id !== "main";
2064             if (action === "show") {
2065                 removeClass(relatedDoc, "fns-now-collapsed");
2066                 removeClass(docblock, "hidden-by-usual-hider");
2067                 onEachLazy(toggle.childNodes, adjustToggle(false, dontApplyBlockRule));
2068                 onEachLazy(relatedDoc.childNodes, implHider(false, dontApplyBlockRule));
2069             } else if (action === "hide") {
2070                 addClass(relatedDoc, "fns-now-collapsed");
2071                 addClass(docblock, "hidden-by-usual-hider");
2072                 onEachLazy(toggle.childNodes, adjustToggle(true, dontApplyBlockRule));
2073                 onEachLazy(relatedDoc.childNodes, implHider(true, dontApplyBlockRule));
2074             }
2075         }
2076     }
2077
2078     function collapser(e, collapse) {
2079         // inherent impl ids are like "impl" or impl-<number>'.
2080         // they will never be hidden by default.
2081         var n = e.parentElement;
2082         if (n.id.match(/^impl(?:-\d+)?$/) === null) {
2083             // Automatically minimize all non-inherent impls
2084             if (collapse || hasClass(n, "impl")) {
2085                 collapseDocs(e, "hide", pageId);
2086             }
2087         }
2088     }
2089
2090     function autoCollapse(pageId, collapse) {
2091         if (collapse) {
2092             toggleAllDocs(pageId, true);
2093         } else if (getCurrentValue("rustdoc-trait-implementations") !== "false") {
2094             var impl_list = document.getElementById("implementations-list");
2095
2096             if (impl_list !== null) {
2097                 onEachLazy(impl_list.getElementsByClassName("collapse-toggle"), function(e) {
2098                     collapser(e, collapse);
2099                 });
2100             }
2101
2102             var blanket_list = document.getElementById("blanket-implementations-list");
2103
2104             if (blanket_list !== null) {
2105                 onEachLazy(blanket_list.getElementsByClassName("collapse-toggle"), function(e) {
2106                     collapser(e, collapse);
2107                 });
2108             }
2109         }
2110     }
2111
2112     var toggles = document.getElementById("toggle-all-docs");
2113     if (toggles) {
2114         toggles.onclick = toggleAllDocs;
2115     }
2116
2117     function insertAfter(newNode, referenceNode) {
2118         referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
2119     }
2120
2121     function createSimpleToggle(sectionIsCollapsed) {
2122         var toggle = document.createElement("a");
2123         toggle.href = "javascript:void(0)";
2124         toggle.className = "collapse-toggle";
2125         toggle.innerHTML = "[<span class=\"inner\">" + labelForToggleButton(sectionIsCollapsed) +
2126                            "</span>]";
2127         return toggle;
2128     }
2129
2130     var toggle = createSimpleToggle(false);
2131     var hideMethodDocs = getCurrentValue("rustdoc-method-docs") === "true";
2132     var pageId = getPageId();
2133
2134     var func = function(e) {
2135         var next = e.nextElementSibling;
2136         if (!next) {
2137             return;
2138         }
2139         if (hasClass(next, "docblock") === true ||
2140             (hasClass(next, "stability") === true &&
2141              hasClass(next.nextElementSibling, "docblock") === true)) {
2142             var newToggle = toggle.cloneNode(true);
2143             insertAfter(newToggle, e.childNodes[e.childNodes.length - 1]);
2144             if (hideMethodDocs === true && hasClass(e, "method") === true) {
2145                 collapseDocs(newToggle, "hide", pageId);
2146             }
2147         }
2148     };
2149
2150     var funcImpl = function(e) {
2151         var next = e.nextElementSibling;
2152         if (next && hasClass(next, "docblock")) {
2153             next = next.nextElementSibling;
2154         }
2155         if (!next) {
2156             return;
2157         }
2158         if (next.getElementsByClassName("method").length > 0 && hasClass(e, "impl")) {
2159             insertAfter(toggle.cloneNode(true), e.childNodes[e.childNodes.length - 1]);
2160         }
2161     };
2162
2163     onEachLazy(document.getElementsByClassName("method"), func);
2164     onEachLazy(document.getElementsByClassName("associatedconstant"), func);
2165     onEachLazy(document.getElementsByClassName("impl"), funcImpl);
2166     var impl_call = function() {};
2167     if (hideMethodDocs === true) {
2168         impl_call = function(e, newToggle, pageId) {
2169             if (e.id.match(/^impl(?:-\d+)?$/) === null) {
2170                 // Automatically minimize all non-inherent impls
2171                 if (hasClass(e, "impl") === true) {
2172                     collapseDocs(newToggle, "hide", pageId);
2173                 }
2174             }
2175         };
2176     }
2177     var newToggle = document.createElement("a");
2178     newToggle.href = "javascript:void(0)";
2179     newToggle.className = "collapse-toggle hidden-default collapsed";
2180     newToggle.innerHTML = "[<span class=\"inner\">" + labelForToggleButton(true) +
2181                           "</span>] Show hidden undocumented items";
2182     function toggleClicked() {
2183         if (hasClass(this, "collapsed")) {
2184             removeClass(this, "collapsed");
2185             onEachLazy(this.parentNode.getElementsByClassName("hidden"), function(x) {
2186                 if (hasClass(x, "content") === false) {
2187                     removeClass(x, "hidden");
2188                     addClass(x, "x");
2189                 }
2190             }, true);
2191             this.innerHTML = "[<span class=\"inner\">" + labelForToggleButton(false) +
2192                              "</span>] Hide undocumented items";
2193         } else {
2194             addClass(this, "collapsed");
2195             onEachLazy(this.parentNode.getElementsByClassName("x"), function(x) {
2196                 if (hasClass(x, "content") === false) {
2197                     addClass(x, "hidden");
2198                     removeClass(x, "x");
2199                 }
2200             }, true);
2201             this.innerHTML = "[<span class=\"inner\">" + labelForToggleButton(true) +
2202                              "</span>] Show hidden undocumented items";
2203         }
2204     }
2205     onEachLazy(document.getElementsByClassName("impl-items"), function(e) {
2206         onEachLazy(e.getElementsByClassName("associatedconstant"), func);
2207         var hiddenElems = e.getElementsByClassName("hidden");
2208         var needToggle = false;
2209
2210         var hlength = hiddenElems.length;
2211         for (var i = 0; i < hlength; ++i) {
2212             if (hasClass(hiddenElems[i], "content") === false &&
2213                 hasClass(hiddenElems[i], "docblock") === false) {
2214                 needToggle = true;
2215                 break;
2216             }
2217         }
2218         if (needToggle === true) {
2219             var inner_toggle = newToggle.cloneNode(true);
2220             inner_toggle.onclick = toggleClicked;
2221             e.insertBefore(inner_toggle, e.firstChild);
2222             impl_call(e.previousSibling, inner_toggle, pageId);
2223         }
2224     });
2225
2226     function createToggle(otherMessage, fontSize, extraClass, show) {
2227         var span = document.createElement("span");
2228         span.className = "toggle-label";
2229         if (show) {
2230             span.style.display = "none";
2231         }
2232         if (!otherMessage) {
2233             span.innerHTML = "&nbsp;Expand&nbsp;description";
2234         } else {
2235             span.innerHTML = otherMessage;
2236         }
2237
2238         if (fontSize) {
2239             span.style.fontSize = fontSize;
2240         }
2241
2242         var mainToggle = toggle.cloneNode(true);
2243         mainToggle.appendChild(span);
2244
2245         var wrapper = document.createElement("div");
2246         wrapper.className = "toggle-wrapper";
2247         if (!show) {
2248             addClass(wrapper, "collapsed");
2249             var inner = mainToggle.getElementsByClassName("inner");
2250             if (inner && inner.length > 0) {
2251                 inner[0].innerHTML = "+";
2252             }
2253         }
2254         if (extraClass) {
2255             addClass(wrapper, extraClass);
2256         }
2257         wrapper.appendChild(mainToggle);
2258         return wrapper;
2259     }
2260
2261     var showItemDeclarations = getCurrentValue("rustdoc-item-declarations") === "false";
2262     function buildToggleWrapper(e) {
2263         if (hasClass(e, "autohide")) {
2264             var wrap = e.previousElementSibling;
2265             if (wrap && hasClass(wrap, "toggle-wrapper")) {
2266                 var inner_toggle = wrap.childNodes[0];
2267                 var extra = e.childNodes[0].tagName === "H3";
2268
2269                 e.style.display = "none";
2270                 addClass(wrap, "collapsed");
2271                 onEachLazy(inner_toggle.getElementsByClassName("inner"), function(e) {
2272                     e.innerHTML = labelForToggleButton(true);
2273                 });
2274                 onEachLazy(inner_toggle.getElementsByClassName("toggle-label"), function(e) {
2275                     e.style.display = "inline-block";
2276                     if (extra === true) {
2277                         i_e.innerHTML = " Show " + e.childNodes[0].innerHTML;
2278                     }
2279                 });
2280             }
2281         }
2282         if (e.parentNode.id === "main") {
2283             var otherMessage = "";
2284             var fontSize;
2285             var extraClass;
2286
2287             if (hasClass(e, "type-decl")) {
2288                 fontSize = "20px";
2289                 otherMessage = "&nbsp;Show&nbsp;declaration";
2290                 if (showItemDeclarations === false) {
2291                     extraClass = "collapsed";
2292                 }
2293             } else if (hasClass(e, "sub-variant")) {
2294                 otherMessage = "&nbsp;Show&nbsp;fields";
2295             } else if (hasClass(e, "non-exhaustive")) {
2296                 otherMessage = "&nbsp;This&nbsp;";
2297                 if (hasClass(e, "non-exhaustive-struct")) {
2298                     otherMessage += "struct";
2299                 } else if (hasClass(e, "non-exhaustive-enum")) {
2300                     otherMessage += "enum";
2301                 } else if (hasClass(e, "non-exhaustive-variant")) {
2302                     otherMessage += "enum variant";
2303                 } else if (hasClass(e, "non-exhaustive-type")) {
2304                     otherMessage += "type";
2305                 }
2306                 otherMessage += "&nbsp;is&nbsp;marked&nbsp;as&nbsp;non-exhaustive";
2307             } else if (hasClass(e.childNodes[0], "impl-items")) {
2308                 extraClass = "marg-left";
2309             }
2310
2311             e.parentNode.insertBefore(
2312                 createToggle(otherMessage,
2313                              fontSize,
2314                              extraClass,
2315                              hasClass(e, "type-decl") === false || showItemDeclarations === true),
2316                 e);
2317             if (hasClass(e, "type-decl") === true && showItemDeclarations === true) {
2318                 collapseDocs(e.previousSibling.childNodes[0], "toggle");
2319             }
2320             if (hasClass(e, "non-exhaustive") === true) {
2321                 collapseDocs(e.previousSibling.childNodes[0], "toggle");
2322             }
2323         }
2324     }
2325
2326     onEachLazy(document.getElementsByClassName("docblock"), buildToggleWrapper);
2327     onEachLazy(document.getElementsByClassName("sub-variant"), buildToggleWrapper);
2328
2329     function createToggleWrapper(tog) {
2330         var span = document.createElement("span");
2331         span.className = "toggle-label";
2332         span.style.display = "none";
2333         span.innerHTML = "&nbsp;Expand&nbsp;attributes";
2334         tog.appendChild(span);
2335
2336         var wrapper = document.createElement("div");
2337         wrapper.className = "toggle-wrapper toggle-attributes";
2338         wrapper.appendChild(tog);
2339         return wrapper;
2340     }
2341
2342     // To avoid checking on "rustdoc-item-attributes" value on every loop...
2343     var itemAttributesFunc = function() {};
2344     if (getCurrentValue("rustdoc-item-attributes") !== "false") {
2345         itemAttributesFunc = function(x) {
2346             collapseDocs(x.previousSibling.childNodes[0], "toggle");
2347         };
2348     }
2349     var attributesToggle = createToggleWrapper(createSimpleToggle(false));
2350     onEachLazy(main.getElementsByClassName("attributes"), function(i_e) {
2351         var attr_tog = attributesToggle.cloneNode(true);
2352         if (hasClass(i_e, "top-attr") === true) {
2353             addClass(attr_tog, "top-attr");
2354         }
2355         i_e.parentNode.insertBefore(attr_tog, i_e);
2356         itemAttributesFunc(i_e);
2357     });
2358
2359     // To avoid checking on "rustdoc-line-numbers" value on every loop...
2360     var lineNumbersFunc = function() {};
2361     if (getCurrentValue("rustdoc-line-numbers") === "true") {
2362         lineNumbersFunc = function(x) {
2363             var count = x.textContent.split("\n").length;
2364             var elems = [];
2365             for (var i = 0; i < count; ++i) {
2366                 elems.push(i + 1);
2367             }
2368             var node = document.createElement("pre");
2369             addClass(node, "line-number");
2370             node.innerHTML = elems.join("\n");
2371             x.parentNode.insertBefore(node, x);
2372         };
2373     }
2374     onEachLazy(document.getElementsByClassName("rust-example-rendered"), function(e) {
2375         if (hasClass(e, "compile_fail")) {
2376             e.addEventListener("mouseover", function(event) {
2377                 this.parentElement.previousElementSibling.childNodes[0].style.color = "#f00";
2378             });
2379             e.addEventListener("mouseout", function(event) {
2380                 this.parentElement.previousElementSibling.childNodes[0].style.color = "";
2381             });
2382         } else if (hasClass(e, "ignore")) {
2383             e.addEventListener("mouseover", function(event) {
2384                 this.parentElement.previousElementSibling.childNodes[0].style.color = "#ff9200";
2385             });
2386             e.addEventListener("mouseout", function(event) {
2387                 this.parentElement.previousElementSibling.childNodes[0].style.color = "";
2388             });
2389         }
2390         lineNumbersFunc(e);
2391     });
2392
2393     function showModal(content) {
2394         var modal = document.createElement("div");
2395         modal.id = "important";
2396         addClass(modal, "modal");
2397         modal.innerHTML = "<div class=\"modal-content\"><div class=\"close\" id=\"modal-close\">✕" +
2398                           "</div><div class=\"whiter\"></div><span class=\"docblock\">" + content +
2399                           "</span></div>";
2400         document.getElementsByTagName("body")[0].appendChild(modal);
2401         document.getElementById("modal-close").onclick = hideModal;
2402         modal.onclick = hideModal;
2403     }
2404
2405     function hideModal() {
2406         var modal = document.getElementById("important");
2407         if (modal) {
2408             modal.parentNode.removeChild(modal);
2409         }
2410     }
2411
2412     onEachLazy(document.getElementsByClassName("important-traits"), function(e) {
2413         e.onclick = function() {
2414             showModal(e.lastElementChild.innerHTML);
2415         };
2416     });
2417
2418     // In the search display, allows to switch between tabs.
2419     function printTab(nb) {
2420         if (nb === 0 || nb === 1 || nb === 2) {
2421             currentTab = nb;
2422         }
2423         var nb_copy = nb;
2424         onEachLazy(document.getElementById("titles").childNodes, function(elem) {
2425             if (nb_copy === 0) {
2426                 addClass(elem, "selected");
2427             } else {
2428                 removeClass(elem, "selected");
2429             }
2430             nb_copy -= 1;
2431         });
2432         onEachLazy(document.getElementById("results").childNodes, function(elem) {
2433             if (nb === 0) {
2434                 elem.style.display = "";
2435             } else {
2436                 elem.style.display = "none";
2437             }
2438             nb -= 1;
2439         });
2440     }
2441
2442     function putBackSearch(search_input) {
2443         if (search_input.value !== "") {
2444             addClass(main, "hidden");
2445             removeClass(document.getElementById("search"), "hidden");
2446             if (browserSupportsHistoryApi()) {
2447                 history.replaceState(search_input.value,
2448                                      "",
2449                                      "?search=" + encodeURIComponent(search_input.value));
2450             }
2451         }
2452     }
2453
2454     if (search_input) {
2455         search_input.onfocus = function() {
2456             putBackSearch(this);
2457         };
2458     }
2459
2460     var params = getQueryStringParams();
2461     if (params && params.search) {
2462         addClass(main, "hidden");
2463         var search = document.getElementById("search");
2464         removeClass(search, "hidden");
2465         search.innerHTML = "<h3 style=\"text-align: center;\">Loading search results...</h3>";
2466     }
2467
2468     var sidebar_menu = document.getElementsByClassName("sidebar-menu")[0];
2469     if (sidebar_menu) {
2470         sidebar_menu.onclick = function() {
2471             var sidebar = document.getElementsByClassName("sidebar")[0];
2472             if (hasClass(sidebar, "mobile") === true) {
2473                 hideSidebar();
2474             } else {
2475                 showSidebar();
2476             }
2477         };
2478     }
2479
2480     window.onresize = function() {
2481         hideSidebar();
2482     };
2483
2484     autoCollapse(getPageId(), getCurrentValue("rustdoc-collapse") === "true");
2485
2486     if (window.location.hash && window.location.hash.length > 0) {
2487         expandSection(window.location.hash.replace(/^#/, ""));
2488     }
2489
2490     if (main) {
2491         onEachLazy(main.getElementsByClassName("loading-content"), function(e) {
2492             e.remove();
2493         });
2494         onEachLazy(main.childNodes, function(e) {
2495             // Unhide the actual content once loading is complete. Headers get
2496             // flex treatment for their horizontal layout, divs get block treatment
2497             // for vertical layout (column-oriented flex layout for divs caused
2498             // errors in mobile browsers).
2499             if (e.tagName === "H2" || e.tagName === "H3") {
2500                 var nextTagName = e.nextElementSibling.tagName;
2501                 if (nextTagName == "H2" || nextTagName == "H3") {
2502                     e.nextElementSibling.style.display = "flex";
2503                 } else {
2504                     e.nextElementSibling.style.display = "block";
2505                 }
2506             }
2507         });
2508     }
2509
2510     function addSearchOptions(crates) {
2511         var elem = document.getElementById("crate-search");
2512
2513         if (!elem) {
2514             return;
2515         }
2516         var crates_text = [];
2517         if (Object.keys(crates).length > 1) {
2518             for (var crate in crates) {
2519                 if (crates.hasOwnProperty(crate)) {
2520                     crates_text.push(crate);
2521                 }
2522             }
2523         }
2524         crates_text.sort(function(a, b) {
2525             var lower_a = a.toLowerCase();
2526             var lower_b = b.toLowerCase();
2527
2528             if (lower_a < lower_b) {
2529                 return -1;
2530             } else if (lower_a > lower_b) {
2531                 return 1;
2532             }
2533             return 0;
2534         });
2535         for (var i = 0; i < crates_text.length; ++i) {
2536             var option = document.createElement("option");
2537             option.value = crates_text[i];
2538             option.innerText = crates_text[i];
2539             elem.appendChild(option);
2540         }
2541     }
2542
2543     window.addSearchOptions = addSearchOptions;
2544 }());
2545
2546 // Sets the focus on the search bar at the top of the page
2547 function focusSearchBar() {
2548     document.getElementsByClassName("search-input")[0].focus();
2549 }
2550
2551 // Removes the focus from the search bar
2552 function defocusSearchBar() {
2553     document.getElementsByClassName("search-input")[0].blur();
2554 }