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