]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/html/static/main.js
6c6c067f95189ab4ce608a8f8454708f4e7222d3
[rust.git] / src / librustdoc / html / static / main.js
1 /*!
2  * Copyright 2014 The Rust Project Developers. See the COPYRIGHT
3  * file at the top-level directory of this distribution and at
4  * http://rust-lang.org/COPYRIGHT.
5  *
6  * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
7  * http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
8  * <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
9  * option. This file may not be copied, modified, or distributed
10  * except according to those terms.
11  */
12
13 /*jslint browser: true, es5: true */
14 /*globals $: true, rootPath: true */
15
16 (function() {
17     "use strict";
18
19     // This mapping table should match the discriminants of
20     // `rustdoc::html::item_type::ItemType` type in Rust.
21     var itemTypes = ["mod",
22                      "externcrate",
23                      "import",
24                      "struct",
25                      "enum",
26                      "fn",
27                      "type",
28                      "static",
29                      "trait",
30                      "impl",
31                      "tymethod",
32                      "method",
33                      "structfield",
34                      "variant",
35                      "macro",
36                      "primitive",
37                      "associatedtype",
38                      "constant",
39                      "associatedconstant",
40                      "union",
41                      "foreigntype"];
42
43     // On the search screen, so you remain on the last tab you opened.
44     //
45     // 0 for "In Names"
46     // 1 for "In Parameters"
47     // 2 for "In Return Types"
48     var currentTab = 0;
49
50     var themesWidth = null;
51
52     if (!String.prototype.startsWith) {
53         String.prototype.startsWith = function(searchString, position) {
54             position = position || 0;
55             return this.indexOf(searchString, position) === position;
56         };
57     }
58
59     function hasClass(elem, className) {
60         if (elem && className && elem.className) {
61             var elemClass = elem.className;
62             var start = elemClass.indexOf(className);
63             if (start === -1) {
64                 return false;
65             } else if (elemClass.length === className.length) {
66                 return true;
67             } else {
68                 if (start > 0 && elemClass[start - 1] !== ' ') {
69                     return false;
70                 }
71                 var end = start + className.length;
72                 if (end < elemClass.length && elemClass[end] !== ' ') {
73                     return false;
74                 }
75                 return true;
76             }
77             if (start > 0 && elemClass[start - 1] !== ' ') {
78                 return false;
79             }
80             var end = start + className.length;
81             if (end < elemClass.length && elemClass[end] !== ' ') {
82                 return false;
83             }
84             return true;
85         }
86         return false;
87     }
88
89     function addClass(elem, className) {
90         if (elem && className && !hasClass(elem, className)) {
91             if (elem.className && elem.className.length > 0) {
92                 elem.className += ' ' + className;
93             } else {
94                 elem.className = className;
95             }
96         }
97     }
98
99     function removeClass(elem, className) {
100         if (elem && className && elem.className) {
101             elem.className = (" " + elem.className + " ").replace(" " + className + " ", " ")
102                                                          .trim();
103         }
104     }
105
106     function isHidden(elem) {
107         return (elem.offsetParent === null)
108     }
109
110     function showSidebar() {
111         var elems = document.getElementsByClassName("sidebar-elems")[0];
112         if (elems) {
113             addClass(elems, "show-it");
114         }
115         var sidebar = document.getElementsByClassName('sidebar')[0];
116         if (sidebar) {
117             addClass(sidebar, 'mobile');
118             var filler = document.getElementById("sidebar-filler");
119             if (!filler) {
120                 var div = document.createElement("div");
121                 div.id = "sidebar-filler";
122                 sidebar.appendChild(div);
123             }
124         }
125         var themePicker = document.getElementsByClassName("theme-picker");
126         if (themePicker && themePicker.length > 0) {
127             themePicker[0].style.display = "none";
128         }
129     }
130
131     function hideSidebar() {
132         var elems = document.getElementsByClassName("sidebar-elems")[0];
133         if (elems) {
134             removeClass(elems, "show-it");
135         }
136         var sidebar = document.getElementsByClassName('sidebar')[0];
137         removeClass(sidebar, 'mobile');
138         var filler = document.getElementById("sidebar-filler");
139         if (filler) {
140             filler.remove();
141         }
142         document.getElementsByTagName("body")[0].style.marginTop = '';
143         var themePicker = document.getElementsByClassName("theme-picker");
144         if (themePicker && themePicker.length > 0) {
145             themePicker[0].style.display = null;
146         }
147     }
148
149     // used for special search precedence
150     var TY_PRIMITIVE = itemTypes.indexOf("primitive");
151
152     onEach(document.getElementsByClassName('js-only'), function(e) {
153         removeClass(e, 'js-only');
154     });
155
156     function getQueryStringParams() {
157         var params = {};
158         window.location.search.substring(1).split("&").
159             map(function(s) {
160                 var pair = s.split("=");
161                 params[decodeURIComponent(pair[0])] =
162                     typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]);
163             });
164         return params;
165     }
166
167     function browserSupportsHistoryApi() {
168         return document.location.protocol != "file:" &&
169           window.history && typeof window.history.pushState === "function";
170     }
171
172     function highlightSourceLines(ev) {
173         // If we're in mobile mode, we should add the sidebar in any case.
174         hideSidebar();
175         var search = document.getElementById("search");
176         var i, from, to, match = window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/);
177         if (match) {
178             from = parseInt(match[1], 10);
179             to = Math.min(50000, parseInt(match[2] || match[1], 10));
180             from = Math.min(from, to);
181             var elem = document.getElementById(from);
182             if (!elem) {
183                 return;
184             }
185             if (ev === null) {
186                 var x = document.getElementById(from);
187                 if (x) {
188                     x.scrollIntoView();
189                 }
190             }
191             onEach(document.getElementsByClassName('line-numbers'), function(e) {
192                 onEach(e.getElementsByTagName('span'), function(i_e) {
193                     removeClass(i_e, 'line-highlighted');
194                 });
195             })
196             for (i = from; i <= to; ++i) {
197                 addClass(document.getElementById(i), 'line-highlighted');
198             }
199         } else if (ev !== null && search && !hasClass(search, "hidden") && ev.newURL) {
200             addClass(search, "hidden");
201             removeClass(document.getElementById("main"), "hidden");
202             var hash = ev.newURL.slice(ev.newURL.indexOf('#') + 1);
203             if (browserSupportsHistoryApi()) {
204                 history.replaceState(hash, "", "?search=#" + hash);
205             }
206             var elem = document.getElementById(hash);
207             if (elem) {
208                 elem.scrollIntoView();
209             }
210         }
211     }
212     highlightSourceLines(null);
213     window.onhashchange = highlightSourceLines;
214
215     // Gets the human-readable string for the virtual-key code of the
216     // given KeyboardEvent, ev.
217     //
218     // This function is meant as a polyfill for KeyboardEvent#key,
219     // since it is not supported in Trident.  We also test for
220     // KeyboardEvent#keyCode because the handleShortcut handler is
221     // also registered for the keydown event, because Blink doesn't fire
222     // keypress on hitting the Escape key.
223     //
224     // So I guess you could say things are getting pretty interoperable.
225     function getVirtualKey(ev) {
226         if ("key" in ev && typeof ev.key != "undefined")
227             return ev.key;
228
229         var c = ev.charCode || ev.keyCode;
230         if (c == 27)
231             return "Escape";
232         return String.fromCharCode(c);
233     }
234
235     function displayHelp(display, ev) {
236         if (display === true) {
237             if (hasClass(help, "hidden")) {
238                 ev.preventDefault();
239                 removeClass(help, "hidden");
240                 addClass(document.body, "blur");
241             }
242         } else if (!hasClass(help, "hidden")) {
243             ev.preventDefault();
244             addClass(help, "hidden");
245             removeClass(document.body, "blur");
246         }
247     }
248
249     function handleEscape(ev, help) {
250         hideModal();
251         var search = document.getElementById("search");
252         if (!hasClass(help, "hidden")) {
253             displayHelp(false, ev);
254         } else if (!hasClass(search, "hidden")) {
255             ev.preventDefault();
256             addClass(search, "hidden");
257             removeClass(document.getElementById("main"), "hidden");
258         }
259         defocusSearchBar();
260     }
261
262     function handleShortcut(ev) {
263         // Don't interfere with browser shortcuts
264         if (ev.ctrlKey || ev.altKey || ev.metaKey) {
265             return;
266         }
267
268         var help = document.getElementById("help");
269         if (document.activeElement.tagName === "INPUT") {
270             switch (getVirtualKey(ev)) {
271             case "Escape":
272                 handleEscape(ev, help);
273                 break;
274             }
275         } else {
276             switch (getVirtualKey(ev)) {
277             case "Escape":
278                 handleEscape(ev, help);
279                 break;
280
281             case "s":
282             case "S":
283                 displayHelp(false, ev);
284                 hideModal();
285                 ev.preventDefault();
286                 focusSearchBar();
287                 break;
288
289             case "+":
290             case "-":
291                 ev.preventDefault();
292                 toggleAllDocs();
293                 break;
294
295             case "?":
296                 if (ev.shiftKey) {
297                     hideModal();
298                     displayHelp(true, ev);
299                 }
300                 break;
301             }
302         }
303     }
304
305     document.onkeypress = handleShortcut;
306     document.onkeydown = handleShortcut;
307     document.onclick = function(ev) {
308         if (hasClass(ev.target, 'collapse-toggle')) {
309             collapseDocs(ev.target, "toggle");
310         } else if (hasClass(ev.target.parentNode, 'collapse-toggle')) {
311             collapseDocs(ev.target.parentNode, "toggle");
312         } else if (ev.target.tagName === 'SPAN' && hasClass(ev.target.parentNode, 'line-numbers')) {
313             var prev_id = 0;
314
315             var set_fragment = function (name) {
316                 if (browserSupportsHistoryApi()) {
317                     history.replaceState(null, null, '#' + name);
318                     window.hashchange();
319                 } else {
320                     location.replace('#' + name);
321                 }
322             };
323
324             var cur_id = parseInt(ev.target.id, 10);
325
326             if (ev.shiftKey && prev_id) {
327                 if (prev_id > cur_id) {
328                     var tmp = prev_id;
329                     prev_id = cur_id;
330                     cur_id = tmp;
331                 }
332
333                 set_fragment(prev_id + '-' + cur_id);
334             } else {
335                 prev_id = cur_id;
336
337                 set_fragment(cur_id);
338             }
339         } else if (!hasClass(document.getElementById("help"), "hidden")) {
340             addClass(document.getElementById("help"), "hidden");
341             removeClass(document.body, "blur");
342         }
343     };
344
345     var x = document.getElementsByClassName('version-selector');
346     if (x.length > 0) {
347         x[0].onchange = function() {
348             var i, match,
349                 url = document.location.href,
350                 stripped = '',
351                 len = rootPath.match(/\.\.\//g).length + 1;
352
353             for (i = 0; i < len; ++i) {
354                 match = url.match(/\/[^\/]*$/);
355                 if (i < len - 1) {
356                     stripped = match[0] + stripped;
357                 }
358                 url = url.substring(0, url.length - match[0].length);
359             }
360
361             url += '/' + document.getElementsByClassName('version-selector')[0].value + stripped;
362
363             document.location.href = url;
364         };
365     }
366
367     /**
368      * A function to compute the Levenshtein distance between two strings
369      * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported
370      * Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode
371      * This code is an unmodified version of the code written by Marco de Wit
372      * and was found at http://stackoverflow.com/a/18514751/745719
373      */
374     var levenshtein_row2 = [];
375     function levenshtein(s1, s2) {
376         if (s1 === s2) {
377             return 0;
378         }
379         var s1_len = s1.length, s2_len = s2.length;
380         if (s1_len && s2_len) {
381             var i1 = 0, i2 = 0, a, b, c, c2, row = levenshtein_row2;
382             while (i1 < s1_len) {
383                 row[i1] = ++i1;
384             }
385             while (i2 < s2_len) {
386                 c2 = s2.charCodeAt(i2);
387                 a = i2;
388                 ++i2;
389                 b = i2;
390                 for (i1 = 0; i1 < s1_len; ++i1) {
391                     c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0);
392                     a = row[i1];
393                     b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
394                     row[i1] = b;
395                 }
396             }
397             return b;
398         }
399         return s1_len + s2_len;
400     }
401
402     function initSearch(rawSearchIndex) {
403         var currentResults, index, searchIndex;
404         var MAX_LEV_DISTANCE = 3;
405         var MAX_RESULTS = 200;
406         var params = getQueryStringParams();
407
408         // Populate search bar with query string search term when provided,
409         // but only if the input bar is empty. This avoid the obnoxious issue
410         // where you start trying to do a search, and the index loads, and
411         // suddenly your search is gone!
412         if (document.getElementsByClassName("search-input")[0].value === "") {
413             document.getElementsByClassName("search-input")[0].value = params.search || '';
414         }
415
416         /**
417          * Executes the query and builds an index of results
418          * @param  {[Object]} query     [The user query]
419          * @param  {[type]} searchWords [The list of search words to query
420          *                               against]
421          * @return {[type]}             [A search index of results]
422          */
423         function execQuery(query, searchWords) {
424             function itemTypeFromName(typename) {
425                 for (var i = 0; i < itemTypes.length; ++i) {
426                     if (itemTypes[i] === typename) {
427                         return i;
428                     }
429                 }
430                 return -1;
431             }
432
433             var valLower = query.query.toLowerCase(),
434                 val = valLower,
435                 typeFilter = itemTypeFromName(query.type),
436                 results = {}, results_in_args = {}, results_returned = {},
437                 split = valLower.split("::");
438
439             for (var z = 0; z < split.length; ++z) {
440                 if (split[z] === "") {
441                     split.splice(z, 1);
442                     z -= 1;
443                 }
444             }
445
446             function transformResults(results, isType) {
447                 var out = [];
448                 for (i = 0; i < results.length; ++i) {
449                     if (results[i].id > -1) {
450                         var obj = searchIndex[results[i].id];
451                         obj.lev = results[i].lev;
452                         if (isType !== true || obj.type) {
453                             out.push(obj);
454                         }
455                     }
456                     if (out.length >= MAX_RESULTS) {
457                         break;
458                     }
459                 }
460                 return out;
461             }
462
463             function sortResults(results, isType) {
464                 var ar = [];
465                 for (var entry in results) {
466                     if (results.hasOwnProperty(entry)) {
467                         ar.push(results[entry]);
468                     }
469                 }
470                 results = ar;
471                 var nresults = results.length;
472                 for (var i = 0; i < nresults; ++i) {
473                     results[i].word = searchWords[results[i].id];
474                     results[i].item = searchIndex[results[i].id] || {};
475                 }
476                 // if there are no results then return to default and fail
477                 if (results.length === 0) {
478                     return [];
479                 }
480
481                 results.sort(function(aaa, bbb) {
482                     var a, b;
483
484                     // Sort by non levenshtein results and then levenshtein results by the distance
485                     // (less changes required to match means higher rankings)
486                     a = (aaa.lev);
487                     b = (bbb.lev);
488                     if (a !== b) { return a - b; }
489
490                     // sort by crate (non-current crate goes later)
491                     a = (aaa.item.crate !== window.currentCrate);
492                     b = (bbb.item.crate !== window.currentCrate);
493                     if (a !== b) { return a - b; }
494
495                     // sort by exact match (mismatch goes later)
496                     a = (aaa.word !== valLower);
497                     b = (bbb.word !== valLower);
498                     if (a !== b) { return a - b; }
499
500                     // sort by item name length (longer goes later)
501                     a = aaa.word.length;
502                     b = bbb.word.length;
503                     if (a !== b) { return a - b; }
504
505                     // sort by item name (lexicographically larger goes later)
506                     a = aaa.word;
507                     b = bbb.word;
508                     if (a !== b) { return (a > b ? +1 : -1); }
509
510                     // sort by index of keyword in item name (no literal occurrence goes later)
511                     a = (aaa.index < 0);
512                     b = (bbb.index < 0);
513                     if (a !== b) { return a - b; }
514                     // (later literal occurrence, if any, goes later)
515                     a = aaa.index;
516                     b = bbb.index;
517                     if (a !== b) { return a - b; }
518
519                     // special precedence for primitive pages
520                     if ((aaa.item.ty === TY_PRIMITIVE) && (bbb.item.ty !== TY_PRIMITIVE)) {
521                         return -1;
522                     }
523                     if ((bbb.item.ty === TY_PRIMITIVE) && (aaa.item.ty !== TY_PRIMITIVE)) {
524                         return 1;
525                     }
526
527                     // sort by description (no description goes later)
528                     a = (aaa.item.desc === '');
529                     b = (bbb.item.desc === '');
530                     if (a !== b) { return a - b; }
531
532                     // sort by type (later occurrence in `itemTypes` goes later)
533                     a = aaa.item.ty;
534                     b = bbb.item.ty;
535                     if (a !== b) { return a - b; }
536
537                     // sort by path (lexicographically larger goes later)
538                     a = aaa.item.path;
539                     b = bbb.item.path;
540                     if (a !== b) { return (a > b ? +1 : -1); }
541
542                     // que sera, sera
543                     return 0;
544                 });
545
546                 for (var i = 0; i < results.length; ++i) {
547                     var result = results[i];
548
549                     // this validation does not make sense when searching by types
550                     if (result.dontValidate) {
551                         continue;
552                     }
553                     var name = result.item.name.toLowerCase(),
554                         path = result.item.path.toLowerCase(),
555                         parent = result.item.parent;
556
557                     if (isType !== true &&
558                         validateResult(name, path, split, parent) === false)
559                     {
560                         result.id = -1;
561                     }
562                 }
563                 return transformResults(results);
564             }
565
566             function extractGenerics(val) {
567                 val = val.toLowerCase();
568                 if (val.indexOf('<') !== -1) {
569                     var values = val.substring(val.indexOf('<') + 1, val.lastIndexOf('>'));
570                     return {
571                         name: val.substring(0, val.indexOf('<')),
572                         generics: values.split(/\s*,\s*/),
573                     };
574                 }
575                 return {
576                     name: val,
577                     generics: [],
578                 };
579             }
580
581             function checkGenerics(obj, val) {
582                 // The names match, but we need to be sure that all generics kinda
583                 // match as well.
584                 var lev_distance = MAX_LEV_DISTANCE + 1;
585                 if (val.generics.length > 0) {
586                     if (obj.generics && obj.generics.length >= val.generics.length) {
587                         var elems = obj.generics.slice(0);
588                         var total = 0;
589                         var done = 0;
590                         // We need to find the type that matches the most to remove it in order
591                         // to move forward.
592                         for (var y = 0; y < val.generics.length; ++y) {
593                             var lev = { pos: -1, lev: MAX_LEV_DISTANCE + 1};
594                             for (var x = 0; x < elems.length; ++x) {
595                                 var tmp_lev = levenshtein(elems[x], val.generics[y]);
596                                 if (tmp_lev < lev.lev) {
597                                     lev.lev = tmp_lev;
598                                     lev.pos = x;
599                                 }
600                             }
601                             if (lev.pos !== -1) {
602                                 elems.splice(lev.pos, 1);
603                                 lev_distance = Math.min(lev.lev, lev_distance);
604                                 total += lev.lev;
605                                 done += 1;
606                             } else {
607                                 return MAX_LEV_DISTANCE + 1;
608                             }
609                         }
610                         return lev_distance;//Math.ceil(total / done);
611                     }
612                 }
613                 return MAX_LEV_DISTANCE + 1;
614             }
615
616             // Check for type name and type generics (if any).
617             function checkType(obj, val, literalSearch) {
618                 var lev_distance = MAX_LEV_DISTANCE + 1;
619                 if (obj.name === val.name) {
620                     if (literalSearch === true) {
621                         if (val.generics && val.generics.length !== 0) {
622                             if (obj.generics && obj.length >= val.generics.length) {
623                                 var elems = obj.generics.slice(0);
624                                 var allFound = true;
625                                 var x;
626
627                                 for (var y = 0; allFound === true && y < val.generics.length; ++y) {
628                                     allFound = false;
629                                     for (x = 0; allFound === false && x < elems.length; ++x) {
630                                         allFound = elems[x] === val.generics[y];
631                                     }
632                                     if (allFound === true) {
633                                         elems.splice(x - 1, 1);
634                                     }
635                                 }
636                                 if (allFound === true) {
637                                     return true;
638                                 }
639                             } else {
640                                 return false;
641                             }
642                         }
643                         return true;
644                     }
645                     // If the type has generics but don't match, then it won't return at this point.
646                     // Otherwise, `checkGenerics` will return 0 and it'll return.
647                     if (obj.generics && obj.generics.length !== 0) {
648                         var tmp_lev = checkGenerics(obj, val);
649                         if (tmp_lev <= MAX_LEV_DISTANCE) {
650                             return tmp_lev;
651                         }
652                     } else {
653                         return 0;
654                     }
655                 }
656                 // Names didn't match so let's check if one of the generic types could.
657                 if (literalSearch === true) {
658                      if (obj.generics && obj.generics.length > 0) {
659                         for (var x = 0; x < obj.generics.length; ++x) {
660                             if (obj.generics[x] === val.name) {
661                                 return true;
662                             }
663                         }
664                     }
665                     return false;
666                 }
667                 var lev_distance = Math.min(levenshtein(obj.name, val.name), lev_distance);
668                 if (lev_distance <= MAX_LEV_DISTANCE) {
669                     lev_distance = Math.min(checkGenerics(obj, val), lev_distance);
670                 } else if (obj.generics && obj.generics.length > 0) {
671                     // We can check if the type we're looking for is inside the generics!
672                     for (var x = 0; x < obj.generics.length; ++x) {
673                         lev_distance = Math.min(levenshtein(obj.generics[x], val.name),
674                                                 lev_distance);
675                     }
676                 }
677                 // Now whatever happens, the returned distance is "less good" so we should mark it
678                 // as such, and so we add 1 to the distance to make it "less good".
679                 return lev_distance + 1;
680             }
681
682             function findArg(obj, val, literalSearch) {
683                 var lev_distance = MAX_LEV_DISTANCE + 1;
684
685                 if (obj && obj.type && obj.type.inputs.length > 0) {
686                     for (var i = 0; i < obj.type.inputs.length; i++) {
687                         var tmp = checkType(obj.type.inputs[i], val, literalSearch);
688                         if (literalSearch === true && tmp === true) {
689                             return true;
690                         }
691                         lev_distance = Math.min(tmp, lev_distance);
692                         if (lev_distance === 0) {
693                             return 0;
694                         }
695                     }
696                 }
697                 return literalSearch === true ? false : lev_distance;
698             }
699
700             function checkReturned(obj, val, literalSearch) {
701                 var lev_distance = MAX_LEV_DISTANCE + 1;
702
703                 if (obj && obj.type && obj.type.output) {
704                     var tmp = checkType(obj.type.output, val, literalSearch);
705                     if (literalSearch === true && tmp === true) {
706                         return true;
707                     }
708                     lev_distance = Math.min(tmp, lev_distance);
709                     if (lev_distance === 0) {
710                         return 0;
711                     }
712                 }
713                 return literalSearch === true ? false : lev_distance;
714             }
715
716             function checkPath(startsWith, lastElem, ty) {
717                 if (startsWith.length === 0) {
718                     return 0;
719                 }
720                 var ret_lev = MAX_LEV_DISTANCE + 1;
721                 var path = ty.path.split("::");
722
723                 if (ty.parent && ty.parent.name) {
724                     path.push(ty.parent.name.toLowerCase());
725                 }
726
727                 if (startsWith.length > path.length) {
728                     return MAX_LEV_DISTANCE + 1;
729                 }
730                 for (var i = 0; i < path.length; ++i) {
731                     if (i + startsWith.length > path.length) {
732                         break;
733                     }
734                     var lev_total = 0;
735                     var aborted = false;
736                     for (var x = 0; x < startsWith.length; ++x) {
737                         var lev = levenshtein(path[i + x], startsWith[x]);
738                         if (lev > MAX_LEV_DISTANCE) {
739                             aborted = true;
740                             break;
741                         }
742                         lev_total += lev;
743                     }
744                     if (aborted === false) {
745                         ret_lev = Math.min(ret_lev, Math.round(lev_total / startsWith.length));
746                     }
747                 }
748                 return ret_lev;
749             }
750
751             function typePassesFilter(filter, type) {
752                 // No filter
753                 if (filter < 0) return true;
754
755                 // Exact match
756                 if (filter === type) return true;
757
758                 // Match related items
759                 var name = itemTypes[type];
760                 switch (itemTypes[filter]) {
761                     case "constant":
762                         return (name == "associatedconstant");
763                     case "fn":
764                         return (name == "method" || name == "tymethod");
765                     case "type":
766                         return (name == "primitive");
767                 }
768
769                 // No match
770                 return false;
771             }
772
773             function generateId(ty) {
774                 if (ty.parent && ty.parent.name) {
775                     return itemTypes[ty.ty] + ty.path + ty.parent.name + ty.name;
776                 }
777                 return itemTypes[ty.ty] + ty.path + ty.name;
778             }
779
780             // quoted values mean literal search
781             var nSearchWords = searchWords.length;
782             if ((val.charAt(0) === "\"" || val.charAt(0) === "'") &&
783                 val.charAt(val.length - 1) === val.charAt(0))
784             {
785                 val = extractGenerics(val.substr(1, val.length - 2));
786                 for (var i = 0; i < nSearchWords; ++i) {
787                     var in_args = findArg(searchIndex[i], val, true);
788                     var returned = checkReturned(searchIndex[i], val, true);
789                     var ty = searchIndex[i];
790                     var fullId = generateId(ty);
791
792                     if (searchWords[i] === val.name) {
793                         // filter type: ... queries
794                         if (typePassesFilter(typeFilter, searchIndex[i].ty) &&
795                             results[fullId] === undefined)
796                         {
797                             results[fullId] = {id: i, index: -1};
798                         }
799                     } else if ((in_args === true || returned === true) &&
800                                typePassesFilter(typeFilter, searchIndex[i].ty)) {
801                         if (in_args === true || returned === true) {
802                             if (in_args === true) {
803                                 results_in_args[fullId] = {
804                                     id: i,
805                                     index: -1,
806                                     dontValidate: true,
807                                 };
808                             }
809                             if (returned === true) {
810                                 results_returned[fullId] = {
811                                     id: i,
812                                     index: -1,
813                                     dontValidate: true,
814                                 };
815                             }
816                         } else {
817                             results[fullId] = {
818                                 id: i,
819                                 index: -1,
820                                 dontValidate: true,
821                             };
822                         }
823                     }
824                 }
825                 query.inputs = [val];
826                 query.output = val;
827                 query.search = val;
828             // searching by type
829             } else if (val.search("->") > -1) {
830                 var trimmer = function (s) { return s.trim(); };
831                 var parts = val.split("->").map(trimmer);
832                 var input = parts[0];
833                 // sort inputs so that order does not matter
834                 var inputs = input.split(",").map(trimmer).sort();
835                 for (var i = 0; i < inputs.length; ++i) {
836                     inputs[i] = extractGenerics(inputs[i]);
837                 }
838                 var output = extractGenerics(parts[1]);
839
840                 for (var i = 0; i < nSearchWords; ++i) {
841                     var type = searchIndex[i].type;
842                     var ty = searchIndex[i];
843                     if (!type) {
844                         continue;
845                     }
846                     var fullId = generateId(ty);
847
848                     // allow searching for void (no output) functions as well
849                     var typeOutput = type.output ? type.output.name : "";
850                     var returned = checkReturned(ty, output, true);
851                     if (output.name === "*" || returned === true) {
852                         var in_args = false;
853                         var module = false;
854
855                         if (input === "*") {
856                             module = true;
857                         } else {
858                             var allFound = true;
859                             for (var it = 0; allFound === true && it < inputs.length; it++) {
860                                 allFound = checkType(type, inputs[it], true);
861                             }
862                             in_args = allFound;
863                         }
864                         if (in_args === true) {
865                             results_in_args[fullId] = {
866                                 id: i,
867                                 index: -1,
868                                 dontValidate: true,
869                             };
870                         }
871                         if (returned === true) {
872                             results_returned[fullId] = {
873                                 id: i,
874                                 index: -1,
875                                 dontValidate: true,
876                             };
877                         }
878                         if (module === true) {
879                             results[fullId] = {
880                                 id: i,
881                                 index: -1,
882                                 dontValidate: true,
883                             };
884                         }
885                     }
886                 }
887                 query.inputs = inputs.map(function(input) {
888                     return input.name;
889                 });
890                 query.output = output.name;
891             } else {
892                 query.inputs = [val];
893                 query.output = val;
894                 query.search = val;
895                 // gather matching search results up to a certain maximum
896                 val = val.replace(/\_/g, "");
897
898                 var valGenerics = extractGenerics(val);
899
900                 var paths = valLower.split("::");
901                 var j;
902                 for (j = 0; j < paths.length; ++j) {
903                     if (paths[j] === "") {
904                         paths.splice(j, 1);
905                         j -= 1;
906                     }
907                 }
908                 val = paths[paths.length - 1];
909                 var startsWith = paths.slice(0, paths.length > 1 ? paths.length - 1 : 1);
910
911                 for (j = 0; j < nSearchWords; ++j) {
912                     var lev_distance;
913                     var ty = searchIndex[j];
914                     if (!ty) {
915                         continue;
916                     }
917                     var lev_add = 0;
918                     if (paths.length > 1) {
919                         var lev = checkPath(startsWith, paths[paths.length - 1], ty);
920                         if (lev > MAX_LEV_DISTANCE) {
921                             continue;
922                         } else if (lev > 0) {
923                             lev_add = 1;
924                         }
925                     }
926
927                     var returned = MAX_LEV_DISTANCE + 1;
928                     var in_args = MAX_LEV_DISTANCE + 1;
929                     var index = -1;
930                     // we want lev results to go lower than others
931                     var lev = MAX_LEV_DISTANCE + 1;
932                     var fullId = generateId(ty);
933
934                     if (searchWords[j].indexOf(split[i]) > -1 ||
935                         searchWords[j].indexOf(val) > -1 ||
936                         searchWords[j].replace(/_/g, "").indexOf(val) > -1)
937                     {
938                         // filter type: ... queries
939                         if (typePassesFilter(typeFilter, ty.ty) && results[fullId] === undefined) {
940                             index = searchWords[j].replace(/_/g, "").indexOf(val);
941                         }
942                     }
943                     if ((lev = levenshtein(searchWords[j], val)) <= MAX_LEV_DISTANCE) {
944                         if (typePassesFilter(typeFilter, ty.ty) === false) {
945                             lev = MAX_LEV_DISTANCE + 1;
946                         } else {
947                             lev += 1;
948                         }
949                     }
950                     if ((in_args = findArg(ty, valGenerics)) <= MAX_LEV_DISTANCE) {
951                         if (typePassesFilter(typeFilter, ty.ty) === false) {
952                             in_args = MAX_LEV_DISTANCE + 1;
953                         }
954                     }
955                     if ((returned = checkReturned(ty, valGenerics)) <= MAX_LEV_DISTANCE) {
956                         if (typePassesFilter(typeFilter, ty.ty) === false) {
957                             returned = MAX_LEV_DISTANCE + 1;
958                         }
959                     }
960
961                     lev += lev_add;
962                     if (lev > 0 && val.length > 3 && searchWords[j].startsWith(val)) {
963                         if (val.length < 6) {
964                             lev -= 1;
965                         } else {
966                             lev = 0;
967                         }
968                     }
969                     if (in_args <= MAX_LEV_DISTANCE) {
970                         if (results_in_args[fullId] === undefined) {
971                             results_in_args[fullId] = {
972                                 id: j,
973                                 index: index,
974                                 lev: in_args,
975                             };
976                         }
977                         results_in_args[fullId].lev =
978                             Math.min(results_in_args[fullId].lev, in_args);
979                     }
980                     if (returned <= MAX_LEV_DISTANCE) {
981                         if (results_returned[fullId] === undefined) {
982                             results_returned[fullId] = {
983                                 id: j,
984                                 index: index,
985                                 lev: returned,
986                             };
987                         }
988                         results_returned[fullId].lev =
989                             Math.min(results_returned[fullId].lev, returned);
990                     }
991                     if (index !== -1 || lev <= MAX_LEV_DISTANCE) {
992                         if (index !== -1) {
993                             lev = 0;
994                         }
995                         if (results[fullId] === undefined) {
996                             results[fullId] = {
997                                 id: j,
998                                 index: index,
999                                 lev: lev,
1000                             };
1001                         }
1002                         results[fullId].lev = Math.min(results[fullId].lev, lev);
1003                     }
1004                 }
1005             }
1006
1007             return {
1008                 'in_args': sortResults(results_in_args, true),
1009                 'returned': sortResults(results_returned, true),
1010                 'others': sortResults(results),
1011             };
1012         }
1013
1014         /**
1015          * Validate performs the following boolean logic. For example:
1016          * "File::open" will give IF A PARENT EXISTS => ("file" && "open")
1017          * exists in (name || path || parent) OR => ("file" && "open") exists in
1018          * (name || path )
1019          *
1020          * This could be written functionally, but I wanted to minimise
1021          * functions on stack.
1022          *
1023          * @param  {[string]} name   [The name of the result]
1024          * @param  {[string]} path   [The path of the result]
1025          * @param  {[string]} keys   [The keys to be used (["file", "open"])]
1026          * @param  {[object]} parent [The parent of the result]
1027          * @return {[boolean]}       [Whether the result is valid or not]
1028          */
1029         function validateResult(name, path, keys, parent) {
1030             for (var i = 0; i < keys.length; ++i) {
1031                 // each check is for validation so we negate the conditions and invalidate
1032                 if (!(
1033                     // check for an exact name match
1034                     name.indexOf(keys[i]) > -1 ||
1035                     // then an exact path match
1036                     path.indexOf(keys[i]) > -1 ||
1037                     // next if there is a parent, check for exact parent match
1038                     (parent !== undefined &&
1039                         parent.name.toLowerCase().indexOf(keys[i]) > -1) ||
1040                     // lastly check to see if the name was a levenshtein match
1041                     levenshtein(name, keys[i]) <= MAX_LEV_DISTANCE)) {
1042                     return false;
1043                 }
1044             }
1045             return true;
1046         }
1047
1048         function getQuery(raw) {
1049             var matches, type, query;
1050             query = raw;
1051
1052             matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i);
1053             if (matches) {
1054                 type = matches[1].replace(/^const$/, 'constant');
1055                 query = query.substring(matches[0].length);
1056             }
1057
1058             return {
1059                 raw: raw,
1060                 query: query,
1061                 type: type,
1062                 id: query + type
1063             };
1064         }
1065
1066         function initSearchNav() {
1067             var hoverTimeout;
1068
1069             var click_func = function(e) {
1070                 var el = e.target;
1071                 // to retrieve the real "owner" of the event.
1072                 while (el.tagName !== 'TR') {
1073                     el = el.parentNode;
1074                 }
1075                 var dst = e.target.getElementsByTagName('a');
1076                 if (dst.length < 1) {
1077                     return;
1078                 }
1079                 dst = dst[0];
1080                 if (window.location.pathname === dst.pathname) {
1081                     addClass(document.getElementById('search'), 'hidden');
1082                     removeClass(document.getElementById('main'), 'hidden');
1083                     document.location.href = dst.href;
1084                 }
1085             };
1086             var mouseover_func = function(e) {
1087                 var el = e.target;
1088                 // to retrieve the real "owner" of the event.
1089                 while (el.tagName !== 'TR') {
1090                     el = el.parentNode;
1091                 }
1092                 clearTimeout(hoverTimeout);
1093                 hoverTimeout = setTimeout(function() {
1094                     onEach(document.getElementsByClassName('search-results'), function(e) {
1095                         onEach(e.getElementsByClassName('result'), function(i_e) {
1096                             removeClass(i_e, 'highlighted');
1097                         });
1098                     });
1099                     addClass(el, 'highlighted');
1100                 }, 20);
1101             };
1102             onEach(document.getElementsByClassName('search-results'), function(e) {
1103                 onEach(e.getElementsByClassName('result'), function(i_e) {
1104                     i_e.onclick = click_func;
1105                     i_e.onmouseover = mouseover_func;
1106                 });
1107             });
1108
1109             var search_input = document.getElementsByClassName('search-input')[0];
1110             search_input.onkeydown = function(e) {
1111                 // "actives" references the currently highlighted item in each search tab.
1112                 // Each array in "actives" represents a tab.
1113                 var actives = [[], [], []];
1114                 // "current" is used to know which tab we're looking into.
1115                 var current = 0;
1116                 onEach(document.getElementsByClassName('search-results'), function(e) {
1117                     onEach(e.getElementsByClassName('highlighted'), function(e) {
1118                         actives[current].push(e);
1119                     });
1120                     current += 1;
1121                 });
1122
1123                 if (e.which === 38) { // up
1124                     if (!actives[currentTab].length ||
1125                         !actives[currentTab][0].previousElementSibling) {
1126                         return;
1127                     }
1128
1129                     addClass(actives[currentTab][0].previousElementSibling, 'highlighted');
1130                     removeClass(actives[currentTab][0], 'highlighted');
1131                 } else if (e.which === 40) { // down
1132                     if (!actives[currentTab].length) {
1133                         var results = document.getElementsByClassName('search-results');
1134                         if (results.length > 0) {
1135                             var res = results[currentTab].getElementsByClassName('result');
1136                             if (res.length > 0) {
1137                                 addClass(res[0], 'highlighted');
1138                             }
1139                         }
1140                     } else if (actives[currentTab][0].nextElementSibling) {
1141                         addClass(actives[currentTab][0].nextElementSibling, 'highlighted');
1142                         removeClass(actives[currentTab][0], 'highlighted');
1143                     }
1144                 } else if (e.which === 13) { // return
1145                     if (actives[currentTab].length) {
1146                         document.location.href =
1147                             actives[currentTab][0].getElementsByTagName('a')[0].href;
1148                     }
1149                 } else if (e.which === 9) { // tab
1150                     if (e.shiftKey) {
1151                         printTab(currentTab > 0 ? currentTab - 1 : 2);
1152                     } else {
1153                         printTab(currentTab > 1 ? 0 : currentTab + 1);
1154                     }
1155                     e.preventDefault();
1156                 } else if (e.which === 16) { // shift
1157                     // Does nothing, it's just to avoid losing "focus" on the highlighted element.
1158                 } else if (e.which === 27) { // escape
1159                     removeClass(actives[currentTab][0], 'highlighted');
1160                     document.getElementsByClassName('search-input')[0].value = '';
1161                     defocusSearchBar();
1162                 } else if (actives[currentTab].length > 0) {
1163                     removeClass(actives[currentTab][0], 'highlighted');
1164                 }
1165             };
1166         }
1167
1168         function escape(content) {
1169             var h1 = document.createElement('h1');
1170             h1.textContent = content;
1171             return h1.innerHTML;
1172         }
1173
1174         function addTab(array, query, display) {
1175             var extraStyle = '';
1176             if (display === false) {
1177                 extraStyle = ' style="display: none;"';
1178             }
1179
1180             var output = '';
1181             if (array.length > 0) {
1182                 output = '<table class="search-results"' + extraStyle + '>';
1183                 var shown = [];
1184
1185                 array.forEach(function(item) {
1186                     var name, type, href, displayPath;
1187
1188                     if (shown.indexOf(item) !== -1) {
1189                         return;
1190                     }
1191
1192                     shown.push(item);
1193                     name = item.name;
1194                     type = itemTypes[item.ty];
1195
1196                     if (type === 'mod') {
1197                         displayPath = item.path + '::';
1198                         href = rootPath + item.path.replace(/::/g, '/') + '/' +
1199                                name + '/index.html';
1200                     } else if (type === "primitive") {
1201                         displayPath = "";
1202                         href = rootPath + item.path.replace(/::/g, '/') +
1203                                '/' + type + '.' + name + '.html';
1204                     } else if (type === "externcrate") {
1205                         displayPath = "";
1206                         href = rootPath + name + '/index.html';
1207                     } else if (item.parent !== undefined) {
1208                         var myparent = item.parent;
1209                         var anchor = '#' + type + '.' + name;
1210                         var parentType = itemTypes[myparent.ty];
1211                         if (parentType === "primitive") {
1212                             displayPath = myparent.name + '::';
1213                         } else {
1214                             displayPath = item.path + '::' + myparent.name + '::';
1215                         }
1216                         href = rootPath + item.path.replace(/::/g, '/') +
1217                                '/' + parentType +
1218                                '.' + myparent.name +
1219                                '.html' + anchor;
1220                     } else {
1221                         displayPath = item.path + '::';
1222                         href = rootPath + item.path.replace(/::/g, '/') +
1223                                '/' + type + '.' + name + '.html';
1224                     }
1225
1226                     output += '<tr class="' + type + ' result"><td>' +
1227                               '<a href="' + href + '">' +
1228                               displayPath + '<span class="' + type + '">' +
1229                               name + '</span></a></td><td>' +
1230                               '<a href="' + href + '">' +
1231                               '<span class="desc">' + escape(item.desc) +
1232                               '&nbsp;</span></a></td></tr>';
1233                 });
1234                 output += '</table>';
1235             } else {
1236                 output = '<div class="search-failed"' + extraStyle + '>No results :(<br/>' +
1237                     'Try on <a href="https://duckduckgo.com/?q=' +
1238                     encodeURIComponent('rust ' + query.query) +
1239                     '">DuckDuckGo</a>?</div>';
1240             }
1241             return output;
1242         }
1243
1244         function makeTabHeader(tabNb, text, nbElems) {
1245             if (currentTab === tabNb) {
1246                 return '<div class="selected">' + text +
1247                        ' <div class="count">(' + nbElems + ')</div></div>';
1248             }
1249             return '<div>' + text + ' <div class="count">(' + nbElems + ')</div></div>';
1250         }
1251
1252         function showResults(results) {
1253             var output, query = getQuery(document.getElementsByClassName('search-input')[0].value);
1254
1255             currentResults = query.id;
1256             output = '<h1>Results for ' + escape(query.query) +
1257                 (query.type ? ' (type: ' + escape(query.type) + ')' : '') + '</h1>' +
1258                 '<div id="titles">' +
1259                 makeTabHeader(0, "In Names", results['others'].length) +
1260                 makeTabHeader(1, "In Parameters", results['in_args'].length) +
1261                 makeTabHeader(2, "In Return Types", results['returned'].length) +
1262                 '</div><div id="results">';
1263
1264             output += addTab(results['others'], query);
1265             output += addTab(results['in_args'], query, false);
1266             output += addTab(results['returned'], query, false);
1267             output += '</div>';
1268
1269             addClass(document.getElementById('main'), 'hidden');
1270             var search = document.getElementById('search');
1271             removeClass(search, 'hidden');
1272             search.innerHTML = output;
1273             var tds = search.getElementsByTagName('td');
1274             var td_width = 0;
1275             if (tds.length > 0) {
1276                 td_width = tds[0].offsetWidth;
1277             }
1278             var width = search.offsetWidth - 40 - td_width;
1279             onEach(search.getElementsByClassName('desc'), function(e) {
1280                 e.style.width = width + 'px';
1281             });
1282             initSearchNav();
1283             var elems = document.getElementById('titles').childNodes;
1284             elems[0].onclick = function() { printTab(0); };
1285             elems[1].onclick = function() { printTab(1); };
1286             elems[2].onclick = function() { printTab(2); };
1287             printTab(currentTab);
1288         }
1289
1290         function search(e) {
1291             var query,
1292                 obj, i, len,
1293                 results = {"in_args": [], "returned": [], "others": []},
1294                 resultIndex;
1295             var params = getQueryStringParams();
1296
1297             query = getQuery(document.getElementsByClassName('search-input')[0].value);
1298             if (e) {
1299                 e.preventDefault();
1300             }
1301
1302             if (!query.query || query.id === currentResults) {
1303                 return;
1304             }
1305
1306             // Update document title to maintain a meaningful browser history
1307             document.title = "Results for " + query.query + " - Rust";
1308
1309             // Because searching is incremental by character, only the most
1310             // recent search query is added to the browser history.
1311             if (browserSupportsHistoryApi()) {
1312                 if (!history.state && !params.search) {
1313                     history.pushState(query, "", "?search=" + encodeURIComponent(query.raw));
1314                 } else {
1315                     history.replaceState(query, "", "?search=" + encodeURIComponent(query.raw));
1316                 }
1317             }
1318
1319             results = execQuery(query, index);
1320             showResults(results);
1321         }
1322
1323         function buildIndex(rawSearchIndex) {
1324             searchIndex = [];
1325             var searchWords = [];
1326             for (var crate in rawSearchIndex) {
1327                 if (!rawSearchIndex.hasOwnProperty(crate)) { continue; }
1328
1329                 searchWords.push(crate);
1330                 searchIndex.push({
1331                     crate: crate,
1332                     ty: 1, // == ExternCrate
1333                     name: crate,
1334                     path: "",
1335                     desc: rawSearchIndex[crate].doc,
1336                     type: null,
1337                 });
1338
1339                 // an array of [(Number) item type,
1340                 //              (String) name,
1341                 //              (String) full path or empty string for previous path,
1342                 //              (String) description,
1343                 //              (Number | null) the parent path index to `paths`]
1344                 //              (Object | null) the type of the function (if any)
1345                 var items = rawSearchIndex[crate].items;
1346                 // an array of [(Number) item type,
1347                 //              (String) name]
1348                 var paths = rawSearchIndex[crate].paths;
1349
1350                 // convert `paths` into an object form
1351                 var len = paths.length;
1352                 for (var i = 0; i < len; ++i) {
1353                     paths[i] = {ty: paths[i][0], name: paths[i][1]};
1354                 }
1355
1356                 // convert `items` into an object form, and construct word indices.
1357                 //
1358                 // before any analysis is performed lets gather the search terms to
1359                 // search against apart from the rest of the data.  This is a quick
1360                 // operation that is cached for the life of the page state so that
1361                 // all other search operations have access to this cached data for
1362                 // faster analysis operations
1363                 var len = items.length;
1364                 var lastPath = "";
1365                 for (var i = 0; i < len; ++i) {
1366                     var rawRow = items[i];
1367                     var row = {crate: crate, ty: rawRow[0], name: rawRow[1],
1368                                path: rawRow[2] || lastPath, desc: rawRow[3],
1369                                parent: paths[rawRow[4]], type: rawRow[5]};
1370                     searchIndex.push(row);
1371                     if (typeof row.name === "string") {
1372                         var word = row.name.toLowerCase();
1373                         searchWords.push(word);
1374                     } else {
1375                         searchWords.push("");
1376                     }
1377                     lastPath = row.path;
1378                 }
1379             }
1380             return searchWords;
1381         }
1382
1383         function startSearch() {
1384             var searchTimeout;
1385             var callback = function() {
1386                 var search_input = document.getElementsByClassName('search-input');
1387                 if (search_input.length < 1) { return; }
1388                 search_input = search_input[0];
1389                 clearTimeout(searchTimeout);
1390                 if (search_input.value.length === 0) {
1391                     if (browserSupportsHistoryApi()) {
1392                         history.replaceState("", "std - Rust", "?search=");
1393                     }
1394                     var main = document.getElementById('main');
1395                     if (hasClass(main, 'content')) {
1396                         removeClass(main, 'hidden');
1397                     }
1398                     var search_c = document.getElementById('search');
1399                     if (hasClass(search_c, 'content')) {
1400                         addClass(search_c, 'hidden');
1401                     }
1402                 } else {
1403                     searchTimeout = setTimeout(search, 500);
1404                 }
1405             };
1406             var search_input = document.getElementsByClassName("search-input")[0];
1407             search_input.onkeyup = callback;
1408             search_input.oninput = callback;
1409             document.getElementsByClassName("search-form")[0].onsubmit = function(e) {
1410                 e.preventDefault();
1411                 clearTimeout(searchTimeout);
1412                 search();
1413             };
1414             search_input.onchange = function(e) {
1415                 // Do NOT e.preventDefault() here. It will prevent pasting.
1416                 clearTimeout(searchTimeout);
1417                 // zero-timeout necessary here because at the time of event handler execution the
1418                 // pasted content is not in the input field yet. Shouldn’t make any difference for
1419                 // change, though.
1420                 setTimeout(search, 0);
1421             };
1422             search_input.onpaste = search_input.onchange;
1423
1424             // Push and pop states are used to add search results to the browser
1425             // history.
1426             if (browserSupportsHistoryApi()) {
1427                 // Store the previous <title> so we can revert back to it later.
1428                 var previousTitle = document.title;
1429
1430                 window.onpopstate = function(e) {
1431                     var params = getQueryStringParams();
1432                     // When browsing back from search results the main page
1433                     // visibility must be reset.
1434                     if (!params.search) {
1435                         var main = document.getElementById('main');
1436                         if (hasClass(main, 'content')) {
1437                             removeClass(main, 'hidden');
1438                         }
1439                         var search_c = document.getElementById('search');
1440                         if (hasClass(search_c, 'content')) {
1441                             addClass(search_c, 'hidden');
1442                         }
1443                     }
1444                     // Revert to the previous title manually since the History
1445                     // API ignores the title parameter.
1446                     document.title = previousTitle;
1447                     // When browsing forward to search results the previous
1448                     // search will be repeated, so the currentResults are
1449                     // cleared to ensure the search is successful.
1450                     currentResults = null;
1451                     // Synchronize search bar with query string state and
1452                     // perform the search. This will empty the bar if there's
1453                     // nothing there, which lets you really go back to a
1454                     // previous state with nothing in the bar.
1455                     if (params.search) {
1456                         document.getElementsByClassName('search-input')[0].value = params.search;
1457                     } else {
1458                         document.getElementsByClassName('search-input')[0].value = '';
1459                     }
1460                     // Some browsers fire 'onpopstate' for every page load
1461                     // (Chrome), while others fire the event only when actually
1462                     // popping a state (Firefox), which is why search() is
1463                     // called both here and at the end of the startSearch()
1464                     // function.
1465                     search();
1466                 };
1467             }
1468             search();
1469         }
1470
1471         index = buildIndex(rawSearchIndex);
1472         startSearch();
1473
1474         // Draw a convenient sidebar of known crates if we have a listing
1475         if (rootPath === '../') {
1476             var sidebar = document.getElementsByClassName('sidebar-elems')[0];
1477             if (sidebar) {
1478                 var div = document.createElement('div');
1479                 div.className = 'block crate';
1480                 div.innerHTML = '<h3>Crates</h3>';
1481                 var ul = document.createElement('ul');
1482                 div.appendChild(ul);
1483
1484                 var crates = [];
1485                 for (var crate in rawSearchIndex) {
1486                     if (!rawSearchIndex.hasOwnProperty(crate)) {
1487                         continue;
1488                     }
1489                     crates.push(crate);
1490                 }
1491                 crates.sort();
1492                 for (var i = 0; i < crates.length; ++i) {
1493                     var klass = 'crate';
1494                     if (crates[i] === window.currentCrate) {
1495                         klass += ' current';
1496                     }
1497                     var link = document.createElement('a');
1498                     link.href = '../' + crates[i] + '/index.html';
1499                     link.title = rawSearchIndex[crates[i]].doc;
1500                     link.className = klass;
1501                     link.textContent = crates[i];
1502
1503                     var li = document.createElement('li');
1504                     li.appendChild(link);
1505                     ul.appendChild(li);
1506                 }
1507                 sidebar.appendChild(div);
1508             }
1509         }
1510     }
1511
1512     window.initSearch = initSearch;
1513
1514     // delayed sidebar rendering.
1515     function initSidebarItems(items) {
1516         var sidebar = document.getElementsByClassName('sidebar-elems')[0];
1517         var current = window.sidebarCurrent;
1518
1519         function block(shortty, longty) {
1520             var filtered = items[shortty];
1521             if (!filtered) { return; }
1522
1523             var div = document.createElement('div');
1524             div.className = 'block ' + shortty;
1525             var h3 = document.createElement('h3');
1526             h3.textContent = longty;
1527             div.appendChild(h3);
1528             var ul = document.createElement('ul');
1529
1530             for (var i = 0; i < filtered.length; ++i) {
1531                 var item = filtered[i];
1532                 var name = item[0];
1533                 var desc = item[1]; // can be null
1534
1535                 var klass = shortty;
1536                 if (name === current.name && shortty === current.ty) {
1537                     klass += ' current';
1538                 }
1539                 var path;
1540                 if (shortty === 'mod') {
1541                     path = name + '/index.html';
1542                 } else {
1543                     path = shortty + '.' + name + '.html';
1544                 }
1545                 var link = document.createElement('a');
1546                 link.href = current.relpath + path;
1547                 link.title = desc;
1548                 link.className = klass;
1549                 link.textContent = name;
1550                 var li = document.createElement('li');
1551                 li.appendChild(link);
1552                 ul.appendChild(li);
1553             }
1554             div.appendChild(ul);
1555             if (sidebar) {
1556                 sidebar.appendChild(div);
1557             }
1558         }
1559
1560         block("primitive", "Primitive Types");
1561         block("mod", "Modules");
1562         block("macro", "Macros");
1563         block("struct", "Structs");
1564         block("enum", "Enums");
1565         block("union", "Unions");
1566         block("constant", "Constants");
1567         block("static", "Statics");
1568         block("trait", "Traits");
1569         block("fn", "Functions");
1570         block("type", "Type Definitions");
1571         block("foreigntype", "Foreign Types");
1572     }
1573
1574     window.initSidebarItems = initSidebarItems;
1575
1576     window.register_implementors = function(imp) {
1577         var implementors = document.getElementById('implementors-list');
1578         var synthetic_implementors = document.getElementById('synthetic-implementors-list');
1579
1580         var libs = Object.getOwnPropertyNames(imp);
1581         for (var i = 0; i < libs.length; ++i) {
1582             if (libs[i] === currentCrate) { continue; }
1583             var structs = imp[libs[i]];
1584
1585             struct_loop:
1586             for (var j = 0; j < structs.length; ++j) {
1587                 var struct = structs[j];
1588
1589                 var list = struct.synthetic ? synthetic_implementors : implementors;
1590
1591                 if (struct.synthetic) {
1592                     for (var k = 0; k < struct.types.length; k++) {
1593                         if (window.inlined_types.has(struct.types[k])) {
1594                             continue struct_loop;
1595                         }
1596                         window.inlined_types.add(struct.types[k]);
1597                     }
1598                 }
1599
1600                 var code = document.createElement('code');
1601                 code.innerHTML = struct.text;
1602
1603                 var x = code.getElementsByTagName('a');
1604                 for (var k = 0; k < x.length; k++) {
1605                     var href = x[k].getAttribute('href');
1606                     if (href && href.indexOf('http') !== 0) {
1607                         x[k].setAttribute('href', rootPath + href);
1608                     }
1609                 }
1610                 var li = document.createElement('li');
1611                 li.appendChild(code);
1612                 list.appendChild(li);
1613             }
1614         }
1615     };
1616     if (window.pending_implementors) {
1617         window.register_implementors(window.pending_implementors);
1618     }
1619
1620     function labelForToggleButton(sectionIsCollapsed) {
1621         if (sectionIsCollapsed) {
1622             // button will expand the section
1623             return "+";
1624         }
1625         // button will collapse the section
1626         // note that this text is also set in the HTML template in render.rs
1627         return "\u2212"; // "\u2212" is '−' minus sign
1628     }
1629
1630     function onEveryMatchingChild(elem, className, func) {
1631         if (elem && className && func) {
1632             for (var i = 0; i < elem.childNodes.length; i++) {
1633                 if (hasClass(elem.childNodes[i], className)) {
1634                     func(elem.childNodes[i]);
1635                 } else {
1636                     onEveryMatchingChild(elem.childNodes[i], className, func);
1637                 }
1638             }
1639         }
1640     }
1641
1642     function toggleAllDocs() {
1643         var toggle = document.getElementById("toggle-all-docs");
1644         if (hasClass(toggle, "will-expand")) {
1645             updateLocalStorage("rustdoc-collapse", "false");
1646             removeClass(toggle, "will-expand");
1647             onEveryMatchingChild(toggle, "inner", function(e) {
1648                 e.innerHTML = labelForToggleButton(false);
1649             });
1650             toggle.title = "collapse all docs";
1651             onEach(document.getElementsByClassName("collapse-toggle"), function(e) {
1652                 collapseDocs(e, "show");
1653             });
1654         } else {
1655             updateLocalStorage("rustdoc-collapse", "true");
1656             addClass(toggle, "will-expand");
1657             onEveryMatchingChild(toggle, "inner", function(e) {
1658                 e.innerHTML = labelForToggleButton(true);
1659             });
1660             toggle.title = "expand all docs";
1661
1662             onEach(document.getElementsByClassName("collapse-toggle"), function(e) {
1663                 collapseDocs(e, "hide");
1664             });
1665         }
1666     }
1667
1668     function collapseDocs(toggle, mode) {
1669         if (!toggle || !toggle.parentNode) {
1670             return;
1671         }
1672
1673         function adjustToggle(arg) {
1674             return function(e) {
1675                 if (hasClass(e, 'toggle-label')) {
1676                     if (arg) {
1677                         e.style.display = 'inline-block';
1678                     } else {
1679                         e.style.display = 'none';
1680                     }
1681                 }
1682                 if (hasClass(e, 'inner')) {
1683                     e.innerHTML = labelForToggleButton(arg);
1684                 }
1685             };
1686         };
1687
1688         if (!hasClass(toggle.parentNode, "impl")) {
1689             var relatedDoc = toggle.parentNode.nextElementSibling;
1690             if (hasClass(relatedDoc, "stability")) {
1691                 relatedDoc = relatedDoc.nextElementSibling;
1692             }
1693             if (hasClass(relatedDoc, "docblock")) {
1694                 var action = mode;
1695                 if (action === "toggle") {
1696                     if (hasClass(relatedDoc, "hidden-by-usual-hider")) {
1697                         action = "show";
1698                     } else {
1699                         action = "hide";
1700                     }
1701                 }
1702                 if (action === "hide") {
1703                     addClass(relatedDoc, "hidden-by-usual-hider");
1704                     onEach(toggle.childNodes, adjustToggle(true));
1705                     addClass(toggle.parentNode, 'collapsed');
1706                 } else if (action === "show") {
1707                     removeClass(relatedDoc, "hidden-by-usual-hider");
1708                     removeClass(toggle.parentNode, 'collapsed');
1709                     onEach(toggle.childNodes, adjustToggle(false));
1710                 }
1711             }
1712         } else {
1713             // we are collapsing the impl block
1714             function implHider(addOrRemove) {
1715                 return function(n) {
1716                     if (hasClass(n, "method")) {
1717                         if (addOrRemove) {
1718                             addClass(n, "hidden-by-impl-hider");
1719                         } else {
1720                             removeClass(n, "hidden-by-impl-hider");
1721                         }
1722                         var ns = n.nextElementSibling;
1723                         while (true) {
1724                             if (ns && (
1725                                     hasClass(ns, "docblock") ||
1726                                     hasClass(ns, "stability") ||
1727                                     false
1728                                     )) {
1729                                 if (addOrRemove) {
1730                                     addClass(ns, "hidden-by-impl-hider");
1731                                 } else {
1732                                     removeClass(ns, "hidden-by-impl-hider");
1733                                 }
1734                                 ns = ns.nextElementSibling;
1735                                 continue;
1736                             }
1737                             break;
1738                         }
1739                     }
1740                 }
1741             }
1742
1743             var relatedDoc = toggle.parentNode;
1744
1745             while (!hasClass(relatedDoc, "impl-items")) {
1746                 relatedDoc = relatedDoc.nextElementSibling;
1747             }
1748
1749             if (!relatedDoc) {
1750                 return;
1751             }
1752
1753             // Hide all functions, but not associated types/consts
1754
1755             var action = mode;
1756             if (action === "toggle") {
1757                 if (hasClass(relatedDoc, "fns-now-collapsed")) {
1758                     action = "show";
1759                 } else {
1760                     action = "hide";
1761                 }
1762             }
1763
1764             if (action === "show") {
1765                 removeClass(relatedDoc, "fns-now-collapsed");
1766                 onEach(toggle.childNodes, adjustToggle(false));
1767                 onEach(relatedDoc.childNodes, implHider(false));
1768             } else if (action === "hide") {
1769                 addClass(relatedDoc, "fns-now-collapsed");
1770                 onEach(toggle.childNodes, adjustToggle(true));
1771                 onEach(relatedDoc.childNodes, implHider(true));
1772             }
1773         }
1774     }
1775
1776     function autoCollapseAllImpls() {
1777         // Automatically minimize all non-inherent impls
1778         onEach(document.getElementsByClassName('impl'), function(n) {
1779             // inherent impl ids are like 'impl' or impl-<number>'
1780             var inherent = (n.id.match(/^impl(?:-\d+)?$/) !== null);
1781             if (!inherent) {
1782                 onEach(n.childNodes, function(m) {
1783                     if (hasClass(m, "collapse-toggle")) {
1784                         collapseDocs(m, "hide");
1785                     }
1786                 });
1787             }
1788         });
1789     }
1790
1791     var x = document.getElementById('toggle-all-docs');
1792     if (x) {
1793         x.onclick = toggleAllDocs;
1794     }
1795
1796     function insertAfter(newNode, referenceNode) {
1797         referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
1798     }
1799
1800     function checkIfThereAreMethods(elems) {
1801         var areThereMethods = false;
1802
1803         onEach(elems, function(e) {
1804             if (hasClass(e, "method")) {
1805                 areThereMethods = true;
1806                 return true;
1807             }
1808         });
1809         return areThereMethods;
1810     }
1811
1812     var toggle = document.createElement('a');
1813     toggle.href = 'javascript:void(0)';
1814     toggle.className = 'collapse-toggle';
1815     toggle.innerHTML = "[<span class='inner'>" + labelForToggleButton(false) + "</span>]";
1816
1817     var func = function(e) {
1818         var next = e.nextElementSibling;
1819         if (!next) {
1820             return;
1821         }
1822         if ((checkIfThereAreMethods(next.childNodes) || hasClass(e, 'method')) &&
1823             (hasClass(next, 'docblock') ||
1824              hasClass(e, 'impl') ||
1825              (hasClass(next, 'stability') &&
1826               hasClass(next.nextElementSibling, 'docblock')))) {
1827             insertAfter(toggle.cloneNode(true), e.childNodes[e.childNodes.length - 1]);
1828         }
1829     }
1830     onEach(document.getElementsByClassName('method'), func);
1831     onEach(document.getElementsByClassName('impl'), func);
1832     onEach(document.getElementsByClassName('impl-items'), function(e) {
1833         onEach(e.getElementsByClassName('associatedconstant'), func);
1834     });
1835
1836     function createToggle() {
1837         var span = document.createElement('span');
1838         span.className = 'toggle-label';
1839         span.style.display = 'none';
1840         span.innerHTML = '&nbsp;Expand&nbsp;description';
1841
1842         var mainToggle = toggle.cloneNode(true);
1843         mainToggle.appendChild(span);
1844
1845         var wrapper = document.createElement('div');
1846         wrapper.className = 'toggle-wrapper';
1847         wrapper.appendChild(mainToggle);
1848         return wrapper;
1849     }
1850
1851     onEach(document.getElementById('main').getElementsByClassName('docblock'), function(e) {
1852         if (e.parentNode.id === "main") {
1853             e.parentNode.insertBefore(createToggle(), e);
1854         }
1855     });
1856
1857     onEach(document.getElementsByClassName('docblock'), function(e) {
1858         if (hasClass(e, 'autohide')) {
1859             var wrap = e.previousElementSibling;
1860             if (wrap && hasClass(wrap, 'toggle-wrapper')) {
1861                 var toggle = wrap.childNodes[0];
1862                 if (e.childNodes[0].tagName === 'H3') {
1863                     onEach(toggle.getElementsByClassName('toggle-label'), function(i_e) {
1864                         i_e.innerHTML = " Show " + e.childNodes[0].innerHTML;
1865                     });
1866                 }
1867                 e.style.display = 'none';
1868                 addClass(wrap, 'collapsed');
1869                 onEach(toggle.getElementsByClassName('inner'), function(e) {
1870                     e.innerHTML = labelForToggleButton(true);
1871                 });
1872                 onEach(toggle.getElementsByClassName('toggle-label'), function(e) {
1873                     e.style.display = 'inline-block';
1874                 });
1875             }
1876         }
1877     })
1878
1879     autoCollapseAllImpls();
1880
1881     function createToggleWrapper() {
1882         var span = document.createElement('span');
1883         span.className = 'toggle-label';
1884         span.style.display = 'none';
1885         span.innerHTML = '&nbsp;Expand&nbsp;attributes';
1886         toggle.appendChild(span);
1887
1888         var wrapper = document.createElement('div');
1889         wrapper.className = 'toggle-wrapper toggle-attributes';
1890         wrapper.appendChild(toggle);
1891         return wrapper;
1892     }
1893
1894     // In the search display, allows to switch between tabs.
1895     function printTab(nb) {
1896         if (nb === 0 || nb === 1 || nb === 2) {
1897             currentTab = nb;
1898         }
1899         var nb_copy = nb;
1900         onEach(document.getElementById('titles').childNodes, function(elem) {
1901             if (nb_copy === 0) {
1902                 addClass(elem, 'selected');
1903             } else {
1904                 removeClass(elem, 'selected');
1905             }
1906             nb_copy -= 1;
1907         });
1908         onEach(document.getElementById('results').childNodes, function(elem) {
1909             if (nb === 0) {
1910                 elem.style.display = '';
1911             } else {
1912                 elem.style.display = 'none';
1913             }
1914             nb -= 1;
1915         });
1916     }
1917
1918     onEach(document.getElementById('main').getElementsByTagName('pre'), function(e) {
1919         onEach(e.getElementsByClassName('attributes'), function(i_e) {
1920             i_e.parentNode.insertBefore(createToggleWrapper(), i_e);
1921             collapseDocs(i_e.previousSibling.childNodes[0], "toggle");
1922         });
1923     });
1924
1925     onEach(document.getElementsByClassName('rust-example-rendered'), function(e) {
1926         if (hasClass(e, 'compile_fail')) {
1927             e.addEventListener("mouseover", function(event) {
1928                 e.previousElementSibling.childNodes[0].style.color = '#f00';
1929             });
1930             e.addEventListener("mouseout", function(event) {
1931                 e.previousElementSibling.childNodes[0].style.color = '';
1932             });
1933         } else if (hasClass(e, 'ignore')) {
1934             e.addEventListener("mouseover", function(event) {
1935                 e.previousElementSibling.childNodes[0].style.color = '#ff9200';
1936             });
1937             e.addEventListener("mouseout", function(event) {
1938                 e.previousElementSibling.childNodes[0].style.color = '';
1939             });
1940         }
1941     });
1942
1943     function showModal(content) {
1944         var modal = document.createElement('div');
1945         modal.id = "important";
1946         addClass(modal, 'modal');
1947         modal.innerHTML = '<div class="modal-content"><div class="close" id="modal-close">✕</div>' +
1948                           '<div class="whiter"></div><span class="docblock">' + content +
1949                           '</span></div>';
1950         document.getElementsByTagName('body')[0].appendChild(modal);
1951         document.getElementById('modal-close').onclick = hideModal;
1952         modal.onclick = hideModal;
1953     }
1954
1955     function hideModal() {
1956         var modal = document.getElementById("important");
1957         if (modal) {
1958             modal.parentNode.removeChild(modal);
1959         }
1960     }
1961
1962     onEach(document.getElementsByClassName('important-traits'), function(e) {
1963         e.onclick = function() {
1964             showModal(e.lastElementChild.innerHTML);
1965         };
1966     });
1967
1968     var search_input = document.getElementsByClassName("search-input")[0];
1969
1970     if (search_input) {
1971         search_input.onfocus = function() {
1972             if (search_input.value !== "") {
1973                 addClass(document.getElementById("main"), "hidden");
1974                 removeClass(document.getElementById("search"), "hidden");
1975                 if (browserSupportsHistoryApi()) {
1976                     history.replaceState(search_input.value,
1977                                          "",
1978                                          "?search=" + encodeURIComponent(search_input.value));
1979                 }
1980             }
1981         };
1982     }
1983
1984     var params = getQueryStringParams();
1985     if (params && params.search) {
1986         addClass(document.getElementById("main"), "hidden");
1987         var search = document.getElementById("search");
1988         removeClass(search, "hidden");
1989         search.innerHTML = '<h3 style="text-align: center;">Loading search results...</h3>';
1990     }
1991
1992     var sidebar_menu = document.getElementsByClassName("sidebar-menu")[0];
1993     if (sidebar_menu) {
1994         sidebar_menu.onclick = function() {
1995             var sidebar = document.getElementsByClassName('sidebar')[0];
1996             if (hasClass(sidebar, "mobile") === true) {
1997                 hideSidebar();
1998             } else {
1999                 showSidebar();
2000             }
2001         };
2002     }
2003
2004     window.onresize = function() {
2005         hideSidebar();
2006     };
2007
2008     if (getCurrentValue("rustdoc-collapse") === "true") {
2009         toggleAllDocs();
2010     }
2011 }());
2012
2013 // Sets the focus on the search bar at the top of the page
2014 function focusSearchBar() {
2015     document.getElementsByClassName('search-input')[0].focus();
2016 }
2017
2018 // Removes the focus from the search bar
2019 function defocusSearchBar() {
2020     document.getElementsByClassName('search-input')[0].blur();
2021 }