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.
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.
13 /*jslint browser: true, es5: true */
14 /*globals $: true, rootPath: true */
19 // This mapping table should match the discriminants of
20 // `rustdoc::html::item_type::ItemType` type in Rust.
21 var itemTypes = ["mod",
47 var search_input = document.getElementsByClassName('search-input')[0];
49 // On the search screen, so you remain on the last tab you opened.
52 // 1 for "In Parameters"
53 // 2 for "In Return Types"
56 var themesWidth = null;
58 var titleBeforeSearch = document.title;
60 if (!String.prototype.startsWith) {
61 String.prototype.startsWith = function(searchString, position) {
62 position = position || 0;
63 return this.indexOf(searchString, position) === position;
66 if (!String.prototype.endsWith) {
67 String.prototype.endsWith = function(suffix, length) {
68 var l = length || this.length;
69 return this.indexOf(suffix, l - suffix.length) !== -1;
73 function getPageId() {
74 var id = document.location.href.split('#')[1];
76 return id.split('?')[0].split('&')[0];
81 function hasClass(elem, className) {
82 if (elem && className && elem.className) {
83 var elemClass = elem.className;
84 var start = elemClass.indexOf(className);
87 } else if (elemClass.length === className.length) {
90 if (start > 0 && elemClass[start - 1] !== ' ') {
93 var end = start + className.length;
94 return !(end < elemClass.length && elemClass[end] !== ' ');
96 if (start > 0 && elemClass[start - 1] !== ' ') {
99 var end = start + className.length;
100 return !(end < elemClass.length && elemClass[end] !== ' ');
105 function addClass(elem, className) {
106 if (elem && className && !hasClass(elem, className)) {
107 if (elem.className && elem.className.length > 0) {
108 elem.className += ' ' + className;
110 elem.className = className;
115 function removeClass(elem, className) {
116 if (elem && className && elem.className) {
117 elem.className = (" " + elem.className + " ").replace(" " + className + " ", " ")
122 function isHidden(elem) {
123 return (elem.offsetParent === null)
126 function showSidebar() {
127 var elems = document.getElementsByClassName("sidebar-elems")[0];
129 addClass(elems, "show-it");
131 var sidebar = document.getElementsByClassName('sidebar')[0];
133 addClass(sidebar, 'mobile');
134 var filler = document.getElementById("sidebar-filler");
136 var div = document.createElement("div");
137 div.id = "sidebar-filler";
138 sidebar.appendChild(div);
141 var themePicker = document.getElementsByClassName("theme-picker");
142 if (themePicker && themePicker.length > 0) {
143 themePicker[0].style.display = "none";
147 function hideSidebar() {
148 var elems = document.getElementsByClassName("sidebar-elems")[0];
150 removeClass(elems, "show-it");
152 var sidebar = document.getElementsByClassName('sidebar')[0];
153 removeClass(sidebar, 'mobile');
154 var filler = document.getElementById("sidebar-filler");
158 document.getElementsByTagName("body")[0].style.marginTop = '';
159 var themePicker = document.getElementsByClassName("theme-picker");
160 if (themePicker && themePicker.length > 0) {
161 themePicker[0].style.display = null;
165 // used for special search precedence
166 var TY_PRIMITIVE = itemTypes.indexOf("primitive");
167 var TY_KEYWORD = itemTypes.indexOf("keyword");
169 onEach(document.getElementsByClassName('js-only'), function(e) {
170 removeClass(e, 'js-only');
173 function getQueryStringParams() {
175 window.location.search.substring(1).split("&").
177 var pair = s.split("=");
178 params[decodeURIComponent(pair[0])] =
179 typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]);
184 function browserSupportsHistoryApi() {
185 return document.location.protocol != "file:" &&
186 window.history && typeof window.history.pushState === "function";
189 function highlightSourceLines(ev) {
190 // If we're in mobile mode, we should add the sidebar in any case.
192 var search = document.getElementById("search");
193 var i, from, to, match = window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/);
195 from = parseInt(match[1], 10);
196 to = Math.min(50000, parseInt(match[2] || match[1], 10));
197 from = Math.min(from, to);
198 var elem = document.getElementById(from);
203 var x = document.getElementById(from);
208 onEach(document.getElementsByClassName('line-numbers'), function(e) {
209 onEach(e.getElementsByTagName('span'), function(i_e) {
210 removeClass(i_e, 'line-highlighted');
213 for (i = from; i <= to; ++i) {
214 addClass(document.getElementById(i), 'line-highlighted');
216 } else if (ev !== null && search && !hasClass(search, "hidden") && ev.newURL) {
217 addClass(search, "hidden");
218 removeClass(document.getElementById("main"), "hidden");
219 var hash = ev.newURL.slice(ev.newURL.indexOf('#') + 1);
220 if (browserSupportsHistoryApi()) {
221 history.replaceState(hash, "", "?search=#" + hash);
223 var elem = document.getElementById(hash);
225 elem.scrollIntoView();
230 function expandSection(id) {
231 var elem = document.getElementById(id);
232 if (elem && isHidden(elem)) {
233 var h3 = elem.parentNode.previousSibling;
234 if (h3 && h3.tagName !== 'H3') {
235 h3 = h3.previousSibling; // skip div.docblock
239 var collapses = h3.getElementsByClassName("collapse-toggle");
240 if (collapses.length > 0) {
241 // The element is not visible, we need to make it appear!
242 collapseDocs(collapses[0], "show");
248 highlightSourceLines(null);
249 window.onhashchange = highlightSourceLines;
251 // Gets the human-readable string for the virtual-key code of the
252 // given KeyboardEvent, ev.
254 // This function is meant as a polyfill for KeyboardEvent#key,
255 // since it is not supported in Trident. We also test for
256 // KeyboardEvent#keyCode because the handleShortcut handler is
257 // also registered for the keydown event, because Blink doesn't fire
258 // keypress on hitting the Escape key.
260 // So I guess you could say things are getting pretty interoperable.
261 function getVirtualKey(ev) {
262 if ("key" in ev && typeof ev.key != "undefined")
265 var c = ev.charCode || ev.keyCode;
268 return String.fromCharCode(c);
271 function displayHelp(display, ev) {
272 if (display === true) {
273 if (hasClass(help, "hidden")) {
275 removeClass(help, "hidden");
276 addClass(document.body, "blur");
278 } else if (!hasClass(help, "hidden")) {
280 addClass(help, "hidden");
281 removeClass(document.body, "blur");
285 function handleEscape(ev, help) {
287 var search = document.getElementById("search");
288 if (!hasClass(help, "hidden")) {
289 displayHelp(false, ev);
290 } else if (!hasClass(search, "hidden")) {
292 addClass(search, "hidden");
293 removeClass(document.getElementById("main"), "hidden");
294 document.title = titleBeforeSearch;
299 function handleShortcut(ev) {
300 // Don't interfere with browser shortcuts
301 if (ev.ctrlKey || ev.altKey || ev.metaKey) {
305 var help = document.getElementById("help");
306 if (document.activeElement.tagName === "INPUT") {
307 switch (getVirtualKey(ev)) {
309 handleEscape(ev, help);
313 switch (getVirtualKey(ev)) {
315 handleEscape(ev, help);
320 displayHelp(false, ev);
335 displayHelp(true, ev);
342 function findParentElement(elem, tagName) {
344 if (elem && elem.tagName === tagName) {
347 } while (elem = elem.parentNode);
351 document.onkeypress = handleShortcut;
352 document.onkeydown = handleShortcut;
353 document.onclick = function(ev) {
354 if (hasClass(ev.target, 'collapse-toggle')) {
355 collapseDocs(ev.target, "toggle");
356 } else if (hasClass(ev.target.parentNode, 'collapse-toggle')) {
357 collapseDocs(ev.target.parentNode, "toggle");
358 } else if (ev.target.tagName === 'SPAN' && hasClass(ev.target.parentNode, 'line-numbers')) {
361 var set_fragment = function(name) {
362 if (browserSupportsHistoryApi()) {
363 history.replaceState(null, null, '#' + name);
366 location.replace('#' + name);
370 var cur_id = parseInt(ev.target.id, 10);
372 if (ev.shiftKey && prev_id) {
373 if (prev_id > cur_id) {
379 set_fragment(prev_id + '-' + cur_id);
383 set_fragment(cur_id);
385 } else if (!hasClass(document.getElementById("help"), "hidden")) {
386 addClass(document.getElementById("help"), "hidden");
387 removeClass(document.body, "blur");
389 // Making a collapsed element visible on onhashchange seems
391 var a = findParentElement(ev.target, 'A');
393 expandSection(a.hash.replace(/^#/, ''));
398 var x = document.getElementsByClassName('version-selector');
400 x[0].onchange = function() {
402 url = document.location.href,
404 len = rootPath.match(/\.\.\//g).length + 1;
406 for (i = 0; i < len; ++i) {
407 match = url.match(/\/[^\/]*$/);
409 stripped = match[0] + stripped;
411 url = url.substring(0, url.length - match[0].length);
414 url += '/' + document.getElementsByClassName('version-selector')[0].value + stripped;
416 document.location.href = url;
421 * A function to compute the Levenshtein distance between two strings
422 * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported
423 * Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode
424 * This code is an unmodified version of the code written by Marco de Wit
425 * and was found at http://stackoverflow.com/a/18514751/745719
427 var levenshtein_row2 = [];
428 function levenshtein(s1, s2) {
432 var s1_len = s1.length, s2_len = s2.length;
433 if (s1_len && s2_len) {
434 var i1 = 0, i2 = 0, a, b, c, c2, row = levenshtein_row2;
435 while (i1 < s1_len) {
438 while (i2 < s2_len) {
439 c2 = s2.charCodeAt(i2);
443 for (i1 = 0; i1 < s1_len; ++i1) {
444 c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0);
446 b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
452 return s1_len + s2_len;
455 function initSearch(rawSearchIndex) {
456 var currentResults, index, searchIndex;
457 var MAX_LEV_DISTANCE = 3;
458 var MAX_RESULTS = 200;
459 var GENERICS_DATA = 1;
463 var params = getQueryStringParams();
465 // Populate search bar with query string search term when provided,
466 // but only if the input bar is empty. This avoid the obnoxious issue
467 // where you start trying to do a search, and the index loads, and
468 // suddenly your search is gone!
469 if (search_input.value === "") {
470 search_input.value = params.search || '';
474 * Executes the query and builds an index of results
475 * @param {[Object]} query [The user query]
476 * @param {[type]} searchWords [The list of search words to query
478 * @return {[type]} [A search index of results]
480 function execQuery(query, searchWords) {
481 function itemTypeFromName(typename) {
482 for (var i = 0; i < itemTypes.length; ++i) {
483 if (itemTypes[i] === typename) {
490 var valLower = query.query.toLowerCase(),
492 typeFilter = itemTypeFromName(query.type),
493 results = {}, results_in_args = {}, results_returned = {},
494 split = valLower.split("::");
496 for (var z = 0; z < split.length; ++z) {
497 if (split[z] === "") {
503 function transformResults(results, isType) {
505 for (i = 0; i < results.length; ++i) {
506 if (results[i].id > -1) {
507 var obj = searchIndex[results[i].id];
508 obj.lev = results[i].lev;
509 if (isType !== true || obj.type) {
510 var res = buildHrefAndPath(obj);
511 obj.displayPath = pathSplitter(res[0]);
512 obj.fullPath = obj.displayPath + obj.name;
513 // To be sure than it some items aren't considered as duplicate.
514 obj.fullPath += '|' + obj.ty;
517 if (out.length >= MAX_RESULTS) {
526 function sortResults(results, isType) {
528 for (var entry in results) {
529 if (results.hasOwnProperty(entry)) {
530 ar.push(results[entry]);
534 var nresults = results.length;
535 for (var i = 0; i < nresults; ++i) {
536 results[i].word = searchWords[results[i].id];
537 results[i].item = searchIndex[results[i].id] || {};
539 // if there are no results then return to default and fail
540 if (results.length === 0) {
544 results.sort(function(aaa, bbb) {
547 // Sort by non levenshtein results and then levenshtein results by the distance
548 // (less changes required to match means higher rankings)
551 if (a !== b) { return a - b; }
553 // sort by crate (non-current crate goes later)
554 a = (aaa.item.crate !== window.currentCrate);
555 b = (bbb.item.crate !== window.currentCrate);
556 if (a !== b) { return a - b; }
558 // sort by exact match (mismatch goes later)
559 a = (aaa.word !== valLower);
560 b = (bbb.word !== valLower);
561 if (a !== b) { return a - b; }
563 // sort by item name length (longer goes later)
566 if (a !== b) { return a - b; }
568 // sort by item name (lexicographically larger goes later)
571 if (a !== b) { return (a > b ? +1 : -1); }
573 // sort by index of keyword in item name (no literal occurrence goes later)
576 if (a !== b) { return a - b; }
577 // (later literal occurrence, if any, goes later)
580 if (a !== b) { return a - b; }
582 // special precedence for primitive and keyword pages
583 if ((aaa.item.ty === TY_PRIMITIVE && bbb.item.ty !== TY_KEYWORD) ||
584 (aaa.item.ty === TY_KEYWORD && bbb.item.ty !== TY_PRIMITIVE)) {
587 if ((bbb.item.ty === TY_PRIMITIVE && aaa.item.ty !== TY_PRIMITIVE) ||
588 (bbb.item.ty === TY_KEYWORD && aaa.item.ty !== TY_KEYWORD)) {
592 // sort by description (no description goes later)
593 a = (aaa.item.desc === '');
594 b = (bbb.item.desc === '');
595 if (a !== b) { return a - b; }
597 // sort by type (later occurrence in `itemTypes` goes later)
600 if (a !== b) { return a - b; }
602 // sort by path (lexicographically larger goes later)
605 if (a !== b) { return (a > b ? +1 : -1); }
611 for (var i = 0; i < results.length; ++i) {
612 var result = results[i];
614 // this validation does not make sense when searching by types
615 if (result.dontValidate) {
618 var name = result.item.name.toLowerCase(),
619 path = result.item.path.toLowerCase(),
620 parent = result.item.parent;
622 if (isType !== true &&
623 validateResult(name, path, split, parent) === false)
628 return transformResults(results);
631 function extractGenerics(val) {
632 val = val.toLowerCase();
633 if (val.indexOf('<') !== -1) {
634 var values = val.substring(val.indexOf('<') + 1, val.lastIndexOf('>'));
636 name: val.substring(0, val.indexOf('<')),
637 generics: values.split(/\s*,\s*/),
646 function checkGenerics(obj, val) {
647 // The names match, but we need to be sure that all generics kinda
649 var lev_distance = MAX_LEV_DISTANCE + 1;
650 if (val.generics.length > 0) {
651 if (obj.length > GENERICS_DATA &&
652 obj[GENERICS_DATA].length >= val.generics.length) {
653 var elems = obj[GENERICS_DATA].slice(0);
656 // We need to find the type that matches the most to remove it in order
658 for (var y = 0; y < val.generics.length; ++y) {
659 var lev = { pos: -1, lev: MAX_LEV_DISTANCE + 1};
660 for (var x = 0; x < elems.length; ++x) {
661 var tmp_lev = levenshtein(elems[x], val.generics[y]);
662 if (tmp_lev < lev.lev) {
667 if (lev.pos !== -1) {
668 elems.splice(lev.pos, 1);
669 lev_distance = Math.min(lev.lev, lev_distance);
673 return MAX_LEV_DISTANCE + 1;
676 return lev_distance;//Math.ceil(total / done);
679 return MAX_LEV_DISTANCE + 1;
682 // Check for type name and type generics (if any).
683 function checkType(obj, val, literalSearch) {
684 var lev_distance = MAX_LEV_DISTANCE + 1;
685 if (obj[NAME] === val.name) {
686 if (literalSearch === true) {
687 if (val.generics && val.generics.length !== 0) {
688 if (obj.length > GENERICS_DATA &&
689 obj[GENERICS_DATA].length >= val.generics.length) {
690 var elems = obj[GENERICS_DATA].slice(0);
694 for (var y = 0; allFound === true && y < val.generics.length; ++y) {
696 for (x = 0; allFound === false && x < elems.length; ++x) {
697 allFound = elems[x] === val.generics[y];
699 if (allFound === true) {
700 elems.splice(x - 1, 1);
703 if (allFound === true) {
712 // If the type has generics but don't match, then it won't return at this point.
713 // Otherwise, `checkGenerics` will return 0 and it'll return.
714 if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length !== 0) {
715 var tmp_lev = checkGenerics(obj, val);
716 if (tmp_lev <= MAX_LEV_DISTANCE) {
723 // Names didn't match so let's check if one of the generic types could.
724 if (literalSearch === true) {
725 if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
726 for (var x = 0; x < obj[GENERICS_DATA].length; ++x) {
727 if (obj[GENERICS_DATA][x] === val.name) {
734 var lev_distance = Math.min(levenshtein(obj[NAME], val.name),
736 if (lev_distance <= MAX_LEV_DISTANCE) {
737 lev_distance = Math.min(checkGenerics(obj, val), lev_distance);
738 } else if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
739 // We can check if the type we're looking for is inside the generics!
740 for (var x = 0; x < obj[GENERICS_DATA].length; ++x) {
741 lev_distance = Math.min(levenshtein(obj[GENERICS_DATA][x], val.name),
745 // Now whatever happens, the returned distance is "less good" so we should mark it
746 // as such, and so we add 1 to the distance to make it "less good".
747 return lev_distance + 1;
750 function findArg(obj, val, literalSearch) {
751 var lev_distance = MAX_LEV_DISTANCE + 1;
753 if (obj && obj.type && obj.type[INPUTS_DATA] &&
754 obj.type[INPUTS_DATA].length > 0) {
755 for (var i = 0; i < obj.type[INPUTS_DATA].length; i++) {
756 var tmp = checkType(obj.type[INPUTS_DATA][i], val, literalSearch);
757 if (literalSearch === true && tmp === true) {
760 lev_distance = Math.min(tmp, lev_distance);
761 if (lev_distance === 0) {
766 return literalSearch === true ? false : lev_distance;
769 function checkReturned(obj, val, literalSearch) {
770 var lev_distance = MAX_LEV_DISTANCE + 1;
772 if (obj && obj.type && obj.type.length > OUTPUT_DATA) {
773 var tmp = checkType(obj.type[OUTPUT_DATA], val, literalSearch);
774 if (literalSearch === true && tmp === true) {
777 lev_distance = Math.min(tmp, lev_distance);
778 if (lev_distance === 0) {
782 return literalSearch === true ? false : lev_distance;
785 function checkPath(contains, lastElem, ty) {
786 if (contains.length === 0) {
789 var ret_lev = MAX_LEV_DISTANCE + 1;
790 var path = ty.path.split("::");
792 if (ty.parent && ty.parent.name) {
793 path.push(ty.parent.name.toLowerCase());
796 if (contains.length > path.length) {
797 return MAX_LEV_DISTANCE + 1;
799 for (var i = 0; i < path.length; ++i) {
800 if (i + contains.length > path.length) {
805 for (var x = 0; x < contains.length; ++x) {
806 var lev = levenshtein(path[i + x], contains[x]);
807 if (lev > MAX_LEV_DISTANCE) {
813 if (aborted === false) {
814 ret_lev = Math.min(ret_lev, Math.round(lev_total / contains.length));
820 function typePassesFilter(filter, type) {
822 if (filter < 0) return true;
825 if (filter === type) return true;
827 // Match related items
828 var name = itemTypes[type];
829 switch (itemTypes[filter]) {
831 return (name == "associatedconstant");
833 return (name == "method" || name == "tymethod");
835 return (name == "primitive" || name == "keyword");
842 function generateId(ty) {
843 if (ty.parent && ty.parent.name) {
844 return itemTypes[ty.ty] + ty.path + ty.parent.name + ty.name;
846 return itemTypes[ty.ty] + ty.path + ty.name;
849 // quoted values mean literal search
850 var nSearchWords = searchWords.length;
851 if ((val.charAt(0) === "\"" || val.charAt(0) === "'") &&
852 val.charAt(val.length - 1) === val.charAt(0))
854 val = extractGenerics(val.substr(1, val.length - 2));
855 for (var i = 0; i < nSearchWords; ++i) {
856 var in_args = findArg(searchIndex[i], val, true);
857 var returned = checkReturned(searchIndex[i], val, true);
858 var ty = searchIndex[i];
859 var fullId = generateId(ty);
861 if (searchWords[i] === val.name) {
862 // filter type: ... queries
863 if (typePassesFilter(typeFilter, searchIndex[i].ty) &&
864 results[fullId] === undefined)
866 results[fullId] = {id: i, index: -1};
868 } else if ((in_args === true || returned === true) &&
869 typePassesFilter(typeFilter, searchIndex[i].ty)) {
870 if (in_args === true || returned === true) {
871 if (in_args === true) {
872 results_in_args[fullId] = {
878 if (returned === true) {
879 results_returned[fullId] = {
894 query.inputs = [val];
898 } else if (val.search("->") > -1) {
899 var trimmer = function(s) { return s.trim(); };
900 var parts = val.split("->").map(trimmer);
901 var input = parts[0];
902 // sort inputs so that order does not matter
903 var inputs = input.split(",").map(trimmer).sort();
904 for (var i = 0; i < inputs.length; ++i) {
905 inputs[i] = extractGenerics(inputs[i]);
907 var output = extractGenerics(parts[1]);
909 for (var i = 0; i < nSearchWords; ++i) {
910 var type = searchIndex[i].type;
911 var ty = searchIndex[i];
915 var fullId = generateId(ty);
917 // allow searching for void (no output) functions as well
918 var typeOutput = type.length > OUTPUT_DATA ? type[OUTPUT_DATA].name : "";
919 var returned = checkReturned(ty, output, true);
920 if (output.name === "*" || returned === true) {
928 for (var it = 0; allFound === true && it < inputs.length; it++) {
929 allFound = checkType(type, inputs[it], true);
933 if (in_args === true) {
934 results_in_args[fullId] = {
940 if (returned === true) {
941 results_returned[fullId] = {
947 if (module === true) {
956 query.inputs = inputs.map(function(input) {
959 query.output = output.name;
961 query.inputs = [val];
964 // gather matching search results up to a certain maximum
965 val = val.replace(/\_/g, "");
967 var valGenerics = extractGenerics(val);
969 var paths = valLower.split("::");
971 for (j = 0; j < paths.length; ++j) {
972 if (paths[j] === "") {
977 val = paths[paths.length - 1];
978 var contains = paths.slice(0, paths.length > 1 ? paths.length - 1 : 1);
980 for (j = 0; j < nSearchWords; ++j) {
982 var ty = searchIndex[j];
987 if (paths.length > 1) {
988 var lev = checkPath(contains, paths[paths.length - 1], ty);
989 if (lev > MAX_LEV_DISTANCE) {
991 } else if (lev > 0) {
996 var returned = MAX_LEV_DISTANCE + 1;
997 var in_args = MAX_LEV_DISTANCE + 1;
999 // we want lev results to go lower than others
1000 var lev = MAX_LEV_DISTANCE + 1;
1001 var fullId = generateId(ty);
1003 if (searchWords[j].indexOf(split[i]) > -1 ||
1004 searchWords[j].indexOf(val) > -1 ||
1005 searchWords[j].replace(/_/g, "").indexOf(val) > -1)
1007 // filter type: ... queries
1008 if (typePassesFilter(typeFilter, ty.ty) && results[fullId] === undefined) {
1009 index = searchWords[j].replace(/_/g, "").indexOf(val);
1012 if ((lev = levenshtein(searchWords[j], val)) <= MAX_LEV_DISTANCE) {
1013 if (typePassesFilter(typeFilter, ty.ty) === false) {
1014 lev = MAX_LEV_DISTANCE + 1;
1019 if ((in_args = findArg(ty, valGenerics)) <= MAX_LEV_DISTANCE) {
1020 if (typePassesFilter(typeFilter, ty.ty) === false) {
1021 in_args = MAX_LEV_DISTANCE + 1;
1024 if ((returned = checkReturned(ty, valGenerics)) <= MAX_LEV_DISTANCE) {
1025 if (typePassesFilter(typeFilter, ty.ty) === false) {
1026 returned = MAX_LEV_DISTANCE + 1;
1031 if (lev > 0 && val.length > 3 && searchWords[j].indexOf(val) > -1) {
1032 if (val.length < 6) {
1038 if (in_args <= MAX_LEV_DISTANCE) {
1039 if (results_in_args[fullId] === undefined) {
1040 results_in_args[fullId] = {
1046 results_in_args[fullId].lev =
1047 Math.min(results_in_args[fullId].lev, in_args);
1049 if (returned <= MAX_LEV_DISTANCE) {
1050 if (results_returned[fullId] === undefined) {
1051 results_returned[fullId] = {
1057 results_returned[fullId].lev =
1058 Math.min(results_returned[fullId].lev, returned);
1060 if (index !== -1 || lev <= MAX_LEV_DISTANCE) {
1061 if (index !== -1 && paths.length < 2) {
1064 if (results[fullId] === undefined) {
1071 results[fullId].lev = Math.min(results[fullId].lev, lev);
1077 'in_args': sortResults(results_in_args, true),
1078 'returned': sortResults(results_returned, true),
1079 'others': sortResults(results),
1081 if (ALIASES && ALIASES[window.currentCrate] &&
1082 ALIASES[window.currentCrate][query.raw]) {
1083 var aliases = ALIASES[window.currentCrate][query.raw];
1084 for (var i = 0; i < aliases.length; ++i) {
1085 aliases[i].is_alias = true;
1086 aliases[i].alias = query.raw;
1087 aliases[i].path = aliases[i].p;
1088 var res = buildHrefAndPath(aliases[i]);
1089 aliases[i].displayPath = pathSplitter(res[0]);
1090 aliases[i].fullPath = aliases[i].displayPath + aliases[i].name;
1091 aliases[i].href = res[1];
1092 ret['others'].unshift(aliases[i]);
1093 if (ret['others'].length > MAX_RESULTS) {
1094 ret['others'].pop();
1102 * Validate performs the following boolean logic. For example:
1103 * "File::open" will give IF A PARENT EXISTS => ("file" && "open")
1104 * exists in (name || path || parent) OR => ("file" && "open") exists in
1107 * This could be written functionally, but I wanted to minimise
1108 * functions on stack.
1110 * @param {[string]} name [The name of the result]
1111 * @param {[string]} path [The path of the result]
1112 * @param {[string]} keys [The keys to be used (["file", "open"])]
1113 * @param {[object]} parent [The parent of the result]
1114 * @return {[boolean]} [Whether the result is valid or not]
1116 function validateResult(name, path, keys, parent) {
1117 for (var i = 0; i < keys.length; ++i) {
1118 // each check is for validation so we negate the conditions and invalidate
1120 // check for an exact name match
1121 name.indexOf(keys[i]) > -1 ||
1122 // then an exact path match
1123 path.indexOf(keys[i]) > -1 ||
1124 // next if there is a parent, check for exact parent match
1125 (parent !== undefined &&
1126 parent.name.toLowerCase().indexOf(keys[i]) > -1) ||
1127 // lastly check to see if the name was a levenshtein match
1128 levenshtein(name, keys[i]) <= MAX_LEV_DISTANCE)) {
1135 function getQuery(raw) {
1136 var matches, type, query;
1139 matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i);
1141 type = matches[1].replace(/^const$/, 'constant');
1142 query = query.substring(matches[0].length);
1153 function initSearchNav() {
1156 var click_func = function(e) {
1158 // to retrieve the real "owner" of the event.
1159 while (el.tagName !== 'TR') {
1162 var dst = e.target.getElementsByTagName('a');
1163 if (dst.length < 1) {
1167 if (window.location.pathname === dst.pathname) {
1168 addClass(document.getElementById('search'), 'hidden');
1169 removeClass(document.getElementById('main'), 'hidden');
1170 document.location.href = dst.href;
1173 var mouseover_func = function(e) {
1175 // to retrieve the real "owner" of the event.
1176 while (el.tagName !== 'TR') {
1179 clearTimeout(hoverTimeout);
1180 hoverTimeout = setTimeout(function() {
1181 onEach(document.getElementsByClassName('search-results'), function(e) {
1182 onEach(e.getElementsByClassName('result'), function(i_e) {
1183 removeClass(i_e, 'highlighted');
1186 addClass(el, 'highlighted');
1189 onEach(document.getElementsByClassName('search-results'), function(e) {
1190 onEach(e.getElementsByClassName('result'), function(i_e) {
1191 i_e.onclick = click_func;
1192 i_e.onmouseover = mouseover_func;
1196 search_input.onkeydown = function(e) {
1197 // "actives" references the currently highlighted item in each search tab.
1198 // Each array in "actives" represents a tab.
1199 var actives = [[], [], []];
1200 // "current" is used to know which tab we're looking into.
1202 onEach(document.getElementsByClassName('search-results'), function(e) {
1203 onEach(e.getElementsByClassName('highlighted'), function(e) {
1204 actives[current].push(e);
1209 if (e.which === 38) { // up
1210 if (!actives[currentTab].length ||
1211 !actives[currentTab][0].previousElementSibling) {
1215 addClass(actives[currentTab][0].previousElementSibling, 'highlighted');
1216 removeClass(actives[currentTab][0], 'highlighted');
1217 } else if (e.which === 40) { // down
1218 if (!actives[currentTab].length) {
1219 var results = document.getElementsByClassName('search-results');
1220 if (results.length > 0) {
1221 var res = results[currentTab].getElementsByClassName('result');
1222 if (res.length > 0) {
1223 addClass(res[0], 'highlighted');
1226 } else if (actives[currentTab][0].nextElementSibling) {
1227 addClass(actives[currentTab][0].nextElementSibling, 'highlighted');
1228 removeClass(actives[currentTab][0], 'highlighted');
1230 } else if (e.which === 13) { // return
1231 if (actives[currentTab].length) {
1232 document.location.href =
1233 actives[currentTab][0].getElementsByTagName('a')[0].href;
1235 } else if (e.which === 9) { // tab
1237 printTab(currentTab > 0 ? currentTab - 1 : 2);
1239 printTab(currentTab > 1 ? 0 : currentTab + 1);
1242 } else if (e.which === 16) { // shift
1243 // Does nothing, it's just to avoid losing "focus" on the highlighted element.
1244 } else if (e.which === 27) { // escape
1245 removeClass(actives[currentTab][0], 'highlighted');
1246 search_input.value = '';
1248 } else if (actives[currentTab].length > 0) {
1249 removeClass(actives[currentTab][0], 'highlighted');
1254 function buildHrefAndPath(item) {
1257 var type = itemTypes[item.ty];
1258 var name = item.name;
1260 if (type === 'mod') {
1261 displayPath = item.path + '::';
1262 href = rootPath + item.path.replace(/::/g, '/') + '/' +
1263 name + '/index.html';
1264 } else if (type === "primitive" || type === "keyword") {
1266 href = rootPath + item.path.replace(/::/g, '/') +
1267 '/' + type + '.' + name + '.html';
1268 } else if (type === "externcrate") {
1270 href = rootPath + name + '/index.html';
1271 } else if (item.parent !== undefined) {
1272 var myparent = item.parent;
1273 var anchor = '#' + type + '.' + name;
1274 var parentType = itemTypes[myparent.ty];
1275 if (parentType === "primitive") {
1276 displayPath = myparent.name + '::';
1278 displayPath = item.path + '::' + myparent.name + '::';
1280 href = rootPath + item.path.replace(/::/g, '/') +
1282 '.' + myparent.name +
1285 displayPath = item.path + '::';
1286 href = rootPath + item.path.replace(/::/g, '/') +
1287 '/' + type + '.' + name + '.html';
1289 return [displayPath, href];
1292 function escape(content) {
1293 var h1 = document.createElement('h1');
1294 h1.textContent = content;
1295 return h1.innerHTML;
1298 function pathSplitter(path) {
1299 var tmp = '<span>' + path.replace(/::/g, '::</span><span>');
1300 if (tmp.endsWith("<span>")) {
1301 return tmp.slice(0, tmp.length - 6);
1306 function addTab(array, query, display) {
1307 var extraStyle = '';
1308 if (display === false) {
1309 extraStyle = ' style="display: none;"';
1313 var duplicates = {};
1315 if (array.length > 0) {
1316 output = '<table class="search-results"' + extraStyle + '>';
1318 array.forEach(function(item) {
1322 type = itemTypes[item.ty];
1324 if (item.is_alias !== true) {
1325 if (duplicates[item.fullPath]) {
1328 duplicates[item.fullPath] = true;
1332 output += '<tr class="' + type + ' result"><td>' +
1333 '<a href="' + item.href + '">' +
1334 (item.is_alias === true ?
1335 ('<span class="alias"><b>' + item.alias + ' </b></span><span ' +
1336 'class="grey"><i> - see </i></span>') : '') +
1337 item.displayPath + '<span class="' + type + '">' +
1338 name + '</span></a></td><td>' +
1339 '<a href="' + item.href + '">' +
1340 '<span class="desc">' + escape(item.desc) +
1341 ' </span></a></td></tr>';
1343 output += '</table>';
1345 output = '<div class="search-failed"' + extraStyle + '>No results :(<br/>' +
1346 'Try on <a href="https://duckduckgo.com/?q=' +
1347 encodeURIComponent('rust ' + query.query) +
1348 '">DuckDuckGo</a>?</div>';
1350 return [output, length];
1353 function makeTabHeader(tabNb, text, nbElems) {
1354 if (currentTab === tabNb) {
1355 return '<div class="selected">' + text +
1356 ' <div class="count">(' + nbElems + ')</div></div>';
1358 return '<div>' + text + ' <div class="count">(' + nbElems + ')</div></div>';
1361 function showResults(results) {
1362 if (results['others'].length === 1 &&
1363 getCurrentValue('rustdoc-go-to-only-result') === "true") {
1364 var elem = document.createElement('a');
1365 elem.href = results['others'][0].href;
1366 elem.style.display = 'none';
1367 // For firefox, we need the element to be in the DOM so it can be clicked.
1368 document.body.appendChild(elem);
1371 var query = getQuery(search_input.value);
1373 currentResults = query.id;
1375 var ret_others = addTab(results['others'], query);
1376 var ret_in_args = addTab(results['in_args'], query, false);
1377 var ret_returned = addTab(results['returned'], query, false);
1379 var output = '<h1>Results for ' + escape(query.query) +
1380 (query.type ? ' (type: ' + escape(query.type) + ')' : '') + '</h1>' +
1381 '<div id="titles">' +
1382 makeTabHeader(0, "In Names", ret_others[1]) +
1383 makeTabHeader(1, "In Parameters", ret_in_args[1]) +
1384 makeTabHeader(2, "In Return Types", ret_returned[1]) +
1385 '</div><div id="results">' +
1386 ret_others[0] + ret_in_args[0] + ret_returned[0] + '</div>';
1388 addClass(document.getElementById('main'), 'hidden');
1389 var search = document.getElementById('search');
1390 removeClass(search, 'hidden');
1391 search.innerHTML = output;
1392 var tds = search.getElementsByTagName('td');
1394 if (tds.length > 0) {
1395 td_width = tds[0].offsetWidth;
1397 var width = search.offsetWidth - 40 - td_width;
1398 onEach(search.getElementsByClassName('desc'), function(e) {
1399 e.style.width = width + 'px';
1402 var elems = document.getElementById('titles').childNodes;
1403 elems[0].onclick = function() { printTab(0); };
1404 elems[1].onclick = function() { printTab(1); };
1405 elems[2].onclick = function() { printTab(2); };
1406 printTab(currentTab);
1409 function execSearch(query, searchWords) {
1410 var queries = query.raw.split(",");
1417 for (var i = 0; i < queries.length; ++i) {
1418 var query = queries[i].trim();
1419 if (query.length !== 0) {
1420 var tmp = execQuery(getQuery(query), searchWords);
1422 results['in_args'].push(tmp['in_args']);
1423 results['returned'].push(tmp['returned']);
1424 results['others'].push(tmp['others']);
1427 if (queries.length > 1) {
1428 function getSmallest(arrays, positions, notDuplicates) {
1431 for (var it = 0; it < positions.length; ++it) {
1432 if (arrays[it].length > positions[it] &&
1433 (start === null || start > arrays[it][positions[it]].lev) &&
1434 !notDuplicates[arrays[it][positions[it]].fullPath]) {
1435 start = arrays[it][positions[it]].lev;
1441 function mergeArrays(arrays) {
1444 var notDuplicates = {};
1446 for (var x = 0; x < arrays.length; ++x) {
1449 while (ret.length < MAX_RESULTS) {
1450 var smallest = getSmallest(arrays, positions, notDuplicates);
1452 if (smallest === null) {
1455 for (x = 0; x < arrays.length && ret.length < MAX_RESULTS; ++x) {
1456 if (arrays[x].length > positions[x] &&
1457 arrays[x][positions[x]].lev === smallest &&
1458 !notDuplicates[arrays[x][positions[x]].fullPath]) {
1459 ret.push(arrays[x][positions[x]]);
1460 notDuplicates[arrays[x][positions[x]].fullPath] = true;
1469 'in_args': mergeArrays(results['in_args']),
1470 'returned': mergeArrays(results['returned']),
1471 'others': mergeArrays(results['others']),
1475 'in_args': results['in_args'][0],
1476 'returned': results['returned'][0],
1477 'others': results['others'][0],
1482 function search(e) {
1483 var params = getQueryStringParams();
1484 var query = getQuery(search_input.value.trim());
1490 if (query.query.length === 0 || query.id === currentResults) {
1491 if (query.query.length > 0) {
1492 putBackSearch(search_input);
1497 // Update document title to maintain a meaningful browser history
1498 document.title = "Results for " + query.query + " - Rust";
1500 // Because searching is incremental by character, only the most
1501 // recent search query is added to the browser history.
1502 if (browserSupportsHistoryApi()) {
1503 if (!history.state && !params.search) {
1504 history.pushState(query, "", "?search=" + encodeURIComponent(query.raw));
1506 history.replaceState(query, "", "?search=" + encodeURIComponent(query.raw));
1510 showResults(execSearch(query, index));
1513 function buildIndex(rawSearchIndex) {
1515 var searchWords = [];
1516 for (var crate in rawSearchIndex) {
1517 if (!rawSearchIndex.hasOwnProperty(crate)) { continue; }
1519 searchWords.push(crate);
1522 ty: 1, // == ExternCrate
1525 desc: rawSearchIndex[crate].doc,
1529 // an array of [(Number) item type,
1531 // (String) full path or empty string for previous path,
1532 // (String) description,
1533 // (Number | null) the parent path index to `paths`]
1534 // (Object | null) the type of the function (if any)
1535 var items = rawSearchIndex[crate].items;
1536 // an array of [(Number) item type,
1538 var paths = rawSearchIndex[crate].paths;
1540 // convert `paths` into an object form
1541 var len = paths.length;
1542 for (var i = 0; i < len; ++i) {
1543 paths[i] = {ty: paths[i][0], name: paths[i][1]};
1546 // convert `items` into an object form, and construct word indices.
1548 // before any analysis is performed lets gather the search terms to
1549 // search against apart from the rest of the data. This is a quick
1550 // operation that is cached for the life of the page state so that
1551 // all other search operations have access to this cached data for
1552 // faster analysis operations
1553 var len = items.length;
1555 for (var i = 0; i < len; ++i) {
1556 var rawRow = items[i];
1557 var row = {crate: crate, ty: rawRow[0], name: rawRow[1],
1558 path: rawRow[2] || lastPath, desc: rawRow[3],
1559 parent: paths[rawRow[4]], type: rawRow[5]};
1560 searchIndex.push(row);
1561 if (typeof row.name === "string") {
1562 var word = row.name.toLowerCase();
1563 searchWords.push(word);
1565 searchWords.push("");
1567 lastPath = row.path;
1573 function startSearch() {
1575 var callback = function() {
1576 clearTimeout(searchTimeout);
1577 if (search_input.value.length === 0) {
1578 if (browserSupportsHistoryApi()) {
1579 history.replaceState("", "std - Rust", "?search=");
1581 var main = document.getElementById('main');
1582 if (hasClass(main, 'content')) {
1583 removeClass(main, 'hidden');
1585 var search_c = document.getElementById('search');
1586 if (hasClass(search_c, 'content')) {
1587 addClass(search_c, 'hidden');
1590 searchTimeout = setTimeout(search, 500);
1593 search_input.onkeyup = callback;
1594 search_input.oninput = callback;
1595 document.getElementsByClassName("search-form")[0].onsubmit = function(e) {
1597 clearTimeout(searchTimeout);
1600 search_input.onchange = function(e) {
1601 // Do NOT e.preventDefault() here. It will prevent pasting.
1602 clearTimeout(searchTimeout);
1603 // zero-timeout necessary here because at the time of event handler execution the
1604 // pasted content is not in the input field yet. Shouldn’t make any difference for
1606 setTimeout(search, 0);
1608 search_input.onpaste = search_input.onchange;
1610 // Push and pop states are used to add search results to the browser
1612 if (browserSupportsHistoryApi()) {
1613 // Store the previous <title> so we can revert back to it later.
1614 var previousTitle = document.title;
1616 window.onpopstate = function(e) {
1617 var params = getQueryStringParams();
1618 // When browsing back from search results the main page
1619 // visibility must be reset.
1620 if (!params.search) {
1621 var main = document.getElementById('main');
1622 if (hasClass(main, 'content')) {
1623 removeClass(main, 'hidden');
1625 var search_c = document.getElementById('search');
1626 if (hasClass(search_c, 'content')) {
1627 addClass(search_c, 'hidden');
1630 // Revert to the previous title manually since the History
1631 // API ignores the title parameter.
1632 document.title = previousTitle;
1633 // When browsing forward to search results the previous
1634 // search will be repeated, so the currentResults are
1635 // cleared to ensure the search is successful.
1636 currentResults = null;
1637 // Synchronize search bar with query string state and
1638 // perform the search. This will empty the bar if there's
1639 // nothing there, which lets you really go back to a
1640 // previous state with nothing in the bar.
1641 if (params.search) {
1642 search_input.value = params.search;
1644 search_input.value = '';
1646 // Some browsers fire 'onpopstate' for every page load
1647 // (Chrome), while others fire the event only when actually
1648 // popping a state (Firefox), which is why search() is
1649 // called both here and at the end of the startSearch()
1657 index = buildIndex(rawSearchIndex);
1660 // Draw a convenient sidebar of known crates if we have a listing
1661 if (rootPath === '../' || rootPath === "./") {
1662 var sidebar = document.getElementsByClassName('sidebar-elems')[0];
1664 var div = document.createElement('div');
1665 div.className = 'block crate';
1666 div.innerHTML = '<h3>Crates</h3>';
1667 var ul = document.createElement('ul');
1668 div.appendChild(ul);
1671 for (var crate in rawSearchIndex) {
1672 if (!rawSearchIndex.hasOwnProperty(crate)) {
1678 for (var i = 0; i < crates.length; ++i) {
1679 var klass = 'crate';
1680 if (rootPath !== "./" && crates[i] === window.currentCrate) {
1681 klass += ' current';
1683 var link = document.createElement('a');
1684 link.href = rootPath + crates[i] + '/index.html';
1685 link.title = rawSearchIndex[crates[i]].doc;
1686 link.className = klass;
1687 link.textContent = crates[i];
1689 var li = document.createElement('li');
1690 li.appendChild(link);
1693 sidebar.appendChild(div);
1698 window.initSearch = initSearch;
1700 // delayed sidebar rendering.
1701 function initSidebarItems(items) {
1702 var sidebar = document.getElementsByClassName('sidebar-elems')[0];
1703 var current = window.sidebarCurrent;
1705 function block(shortty, longty) {
1706 var filtered = items[shortty];
1707 if (!filtered) { return; }
1709 var div = document.createElement('div');
1710 div.className = 'block ' + shortty;
1711 var h3 = document.createElement('h3');
1712 h3.textContent = longty;
1713 div.appendChild(h3);
1714 var ul = document.createElement('ul');
1716 for (var i = 0; i < filtered.length; ++i) {
1717 var item = filtered[i];
1719 var desc = item[1]; // can be null
1721 var klass = shortty;
1722 if (name === current.name && shortty === current.ty) {
1723 klass += ' current';
1726 if (shortty === 'mod') {
1727 path = name + '/index.html';
1729 path = shortty + '.' + name + '.html';
1731 var link = document.createElement('a');
1732 link.href = current.relpath + path;
1734 link.className = klass;
1735 link.textContent = name;
1736 var li = document.createElement('li');
1737 li.appendChild(link);
1740 div.appendChild(ul);
1742 sidebar.appendChild(div);
1746 block("primitive", "Primitive Types");
1747 block("mod", "Modules");
1748 block("macro", "Macros");
1749 block("struct", "Structs");
1750 block("enum", "Enums");
1751 block("union", "Unions");
1752 block("constant", "Constants");
1753 block("static", "Statics");
1754 block("trait", "Traits");
1755 block("fn", "Functions");
1756 block("type", "Type Definitions");
1757 block("foreigntype", "Foreign Types");
1758 block("keyword", "Keywords");
1761 window.initSidebarItems = initSidebarItems;
1763 window.register_implementors = function(imp) {
1764 var implementors = document.getElementById('implementors-list');
1765 var synthetic_implementors = document.getElementById('synthetic-implementors-list');
1767 var libs = Object.getOwnPropertyNames(imp);
1768 for (var i = 0; i < libs.length; ++i) {
1769 if (libs[i] === currentCrate) { continue; }
1770 var structs = imp[libs[i]];
1773 for (var j = 0; j < structs.length; ++j) {
1774 var struct = structs[j];
1776 var list = struct.synthetic ? synthetic_implementors : implementors;
1778 if (struct.synthetic) {
1779 for (var k = 0; k < struct.types.length; k++) {
1780 if (window.inlined_types.has(struct.types[k])) {
1781 continue struct_loop;
1783 window.inlined_types.add(struct.types[k]);
1787 var code = document.createElement('code');
1788 code.innerHTML = struct.text;
1790 var x = code.getElementsByTagName('a');
1791 for (var k = 0; k < x.length; k++) {
1792 var href = x[k].getAttribute('href');
1793 if (href && href.indexOf('http') !== 0) {
1794 x[k].setAttribute('href', rootPath + href);
1797 var display = document.createElement('h3');
1798 addClass(display, "impl");
1799 display.innerHTML = '<span class="in-band"><table class="table-display"><tbody>\
1800 <tr><td><code>' + code.outerHTML + '</code></td><td></td></tr></tbody></table>\
1802 list.appendChild(display);
1806 if (window.pending_implementors) {
1807 window.register_implementors(window.pending_implementors);
1810 function labelForToggleButton(sectionIsCollapsed) {
1811 if (sectionIsCollapsed) {
1812 // button will expand the section
1815 // button will collapse the section
1816 // note that this text is also set in the HTML template in render.rs
1817 return "\u2212"; // "\u2212" is '−' minus sign
1820 function onEveryMatchingChild(elem, className, func) {
1821 if (elem && className && func) {
1822 for (var i = 0; i < elem.childNodes.length; i++) {
1823 if (hasClass(elem.childNodes[i], className)) {
1824 func(elem.childNodes[i]);
1826 onEveryMatchingChild(elem.childNodes[i], className, func);
1832 function toggleAllDocs(pageId, fromAutoCollapse) {
1833 var toggle = document.getElementById("toggle-all-docs");
1837 if (hasClass(toggle, "will-expand")) {
1838 updateLocalStorage("rustdoc-collapse", "false");
1839 removeClass(toggle, "will-expand");
1840 onEveryMatchingChild(toggle, "inner", function(e) {
1841 e.innerHTML = labelForToggleButton(false);
1843 toggle.title = "collapse all docs";
1844 if (fromAutoCollapse !== true) {
1845 onEach(document.getElementsByClassName("collapse-toggle"), function(e) {
1846 collapseDocs(e, "show");
1850 updateLocalStorage("rustdoc-collapse", "true");
1851 addClass(toggle, "will-expand");
1852 onEveryMatchingChild(toggle, "inner", function(e) {
1853 e.innerHTML = labelForToggleButton(true);
1855 toggle.title = "expand all docs";
1856 if (fromAutoCollapse !== true) {
1857 onEach(document.getElementsByClassName("collapse-toggle"), function(e) {
1858 collapseDocs(e, "hide", pageId);
1864 function collapseDocs(toggle, mode, pageId) {
1865 if (!toggle || !toggle.parentNode) {
1869 function adjustToggle(arg) {
1870 return function(e) {
1871 if (hasClass(e, 'toggle-label')) {
1873 e.style.display = 'inline-block';
1875 e.style.display = 'none';
1878 if (hasClass(e, 'inner')) {
1879 e.innerHTML = labelForToggleButton(arg);
1884 if (!hasClass(toggle.parentNode, "impl")) {
1885 var relatedDoc = toggle.parentNode.nextElementSibling;
1886 if (hasClass(relatedDoc, "stability")) {
1887 relatedDoc = relatedDoc.nextElementSibling;
1889 if (hasClass(relatedDoc, "docblock")) {
1891 if (action === "toggle") {
1892 if (hasClass(relatedDoc, "hidden-by-usual-hider")) {
1898 if (action === "hide") {
1899 addClass(relatedDoc, "hidden-by-usual-hider");
1900 onEach(toggle.childNodes, adjustToggle(true));
1901 addClass(toggle.parentNode, 'collapsed');
1902 } else if (action === "show") {
1903 removeClass(relatedDoc, "hidden-by-usual-hider");
1904 removeClass(toggle.parentNode, 'collapsed');
1905 onEach(toggle.childNodes, adjustToggle(false));
1909 // we are collapsing the impl block
1910 function implHider(addOrRemove) {
1911 return function(n) {
1912 var is_method = hasClass(n, "method");
1913 if (is_method || hasClass(n, "type")) {
1914 if (is_method === true) {
1916 addClass(n, "hidden-by-impl-hider");
1918 removeClass(n, "hidden-by-impl-hider");
1921 var ns = n.nextElementSibling;
1924 hasClass(ns, "docblock") ||
1925 hasClass(ns, "stability"))) {
1927 addClass(ns, "hidden-by-impl-hider");
1929 removeClass(ns, "hidden-by-impl-hider");
1931 ns = ns.nextElementSibling;
1940 var parentElem = toggle.parentNode;
1941 var relatedDoc = parentElem;
1942 var docblock = relatedDoc.nextElementSibling;
1944 while (!hasClass(relatedDoc, "impl-items")) {
1945 relatedDoc = relatedDoc.nextElementSibling;
1948 if ((!relatedDoc && !hasClass(docblock, "docblock")) ||
1949 (pageId && onEach(relatedDoc.childNodes, function(e) {
1950 return e.id === pageId;
1955 // Hide all functions, but not associated types/consts
1958 if (action === "toggle") {
1959 if (hasClass(relatedDoc, "fns-now-collapsed") ||
1960 hasClass(docblock, "hidden-by-impl-hider")) {
1967 if (action === "show") {
1968 removeClass(relatedDoc, "fns-now-collapsed");
1969 removeClass(docblock, "hidden-by-usual-hider");
1970 onEach(toggle.childNodes, adjustToggle(false));
1971 onEach(relatedDoc.childNodes, implHider(false));
1972 } else if (action === "hide") {
1973 addClass(relatedDoc, "fns-now-collapsed");
1974 addClass(docblock, "hidden-by-usual-hider");
1975 onEach(toggle.childNodes, adjustToggle(true));
1976 onEach(relatedDoc.childNodes, implHider(true));
1981 function autoCollapse(pageId, collapse) {
1983 toggleAllDocs(pageId, true);
1985 var collapser = function(e) {
1986 // inherent impl ids are like 'impl' or impl-<number>'.
1987 // they will never be hidden by default.
1988 var n = e.parentElement;
1989 if (n.id.match(/^impl(?:-\d+)?$/) === null) {
1990 // Automatically minimize all non-inherent impls
1991 if (collapse || hasClass(n, 'impl')) {
1992 collapseDocs(e, "hide", pageId);
1996 if (getCurrentValue('rustdoc-trait-implementations') !== "false") {
1997 var impl_list = document.getElementById('implementations-list');
1999 if (impl_list !== null) {
2000 onEach(impl_list.getElementsByClassName("collapse-toggle"), collapser);
2003 if (getCurrentValue('rustdoc-method-docs') !== "false") {
2004 var implItems = document.getElementsByClassName('impl-items');
2006 if (implItems && implItems.length > 0) {
2007 onEach(implItems, function(elem) {
2008 onEach(elem.getElementsByClassName("collapse-toggle"), collapser);
2014 var x = document.getElementById('toggle-all-docs');
2016 x.onclick = toggleAllDocs;
2019 function insertAfter(newNode, referenceNode) {
2020 referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
2023 function checkIfThereAreMethods(elems) {
2024 var areThereMethods = false;
2026 onEach(elems, function(e) {
2027 if (hasClass(e, "method")) {
2028 areThereMethods = true;
2032 return areThereMethods;
2035 var toggle = document.createElement('a');
2036 toggle.href = 'javascript:void(0)';
2037 toggle.className = 'collapse-toggle';
2038 toggle.innerHTML = "[<span class='inner'>" + labelForToggleButton(false) + "</span>]";
2040 var func = function(e) {
2041 var next = e.nextElementSibling;
2042 if (hasClass(e, 'impl') && next && hasClass(next, 'docblock')) {
2043 next = next.nextElementSibling;
2048 if ((hasClass(e, 'method') || hasClass(e, 'associatedconstant') ||
2049 checkIfThereAreMethods(next.childNodes)) &&
2050 (hasClass(next, 'docblock') ||
2051 hasClass(e, 'impl') ||
2052 (hasClass(next, 'stability') &&
2053 hasClass(next.nextElementSibling, 'docblock')))) {
2054 insertAfter(toggle.cloneNode(true), e.childNodes[e.childNodes.length - 1]);
2057 onEach(document.getElementsByClassName('method'), func);
2058 onEach(document.getElementsByClassName('associatedconstant'), func);
2059 onEach(document.getElementsByClassName('impl'), func);
2060 onEach(document.getElementsByClassName('impl-items'), function(e) {
2061 onEach(e.getElementsByClassName('associatedconstant'), func);
2062 if (e.getElementsByClassName('hidden').length > 0) {
2063 var newToggle = document.createElement('a');
2064 newToggle.href = 'javascript:void(0)';
2065 newToggle.className = 'collapse-toggle hidden-default collapsed';
2066 newToggle.innerHTML = "[<span class='inner'>" + labelForToggleButton(true) + "</span>" +
2067 "] Show hidden default items";
2068 newToggle.onclick = function() {
2069 if (hasClass(this, "collapsed")) {
2070 removeClass(this, "collapsed");
2071 onEach(this.parentNode.getElementsByClassName("hidden"), function(x) {
2072 removeClass(x, "hidden");
2075 this.innerHTML = "[<span class='inner'>" + labelForToggleButton(false) +
2076 "</span>] Hide default items"
2078 addClass(this, "collapsed");
2079 onEach(this.parentNode.getElementsByClassName("x"), function(x) {
2080 addClass(x, "hidden");
2081 removeClass(x, "x");
2083 this.innerHTML = "[<span class='inner'>" + labelForToggleButton(true) +
2084 "</span>] Show hidden default items";
2087 e.insertBefore(newToggle, e.firstChild);
2091 function createToggle(otherMessage, fontSize, extraClass, show) {
2092 var span = document.createElement('span');
2093 span.className = 'toggle-label';
2095 span.style.display = 'none';
2097 if (!otherMessage) {
2098 span.innerHTML = ' Expand description';
2100 span.innerHTML = otherMessage;
2104 span.style.fontSize = fontSize;
2107 var mainToggle = toggle.cloneNode(true);
2108 mainToggle.appendChild(span);
2110 var wrapper = document.createElement('div');
2111 wrapper.className = 'toggle-wrapper';
2113 addClass(wrapper, 'collapsed');
2114 var inner = mainToggle.getElementsByClassName('inner');
2115 if (inner && inner.length > 0) {
2116 inner[0].innerHTML = '+';
2120 addClass(wrapper, extraClass);
2122 wrapper.appendChild(mainToggle);
2126 onEach(document.getElementsByClassName('docblock'), function(e) {
2127 if (hasClass(e, 'autohide')) {
2128 var wrap = e.previousElementSibling;
2129 if (wrap && hasClass(wrap, 'toggle-wrapper')) {
2130 var toggle = wrap.childNodes[0];
2132 if (e.childNodes[0].tagName === 'H3') {
2135 e.style.display = 'none';
2136 addClass(wrap, 'collapsed');
2137 onEach(toggle.getElementsByClassName('inner'), function(e) {
2138 e.innerHTML = labelForToggleButton(true);
2140 onEach(toggle.getElementsByClassName('toggle-label'), function(e) {
2141 e.style.display = 'inline-block';
2142 if (extra === true) {
2143 i_e.innerHTML = " Show " + e.childNodes[0].innerHTML;
2148 if (e.parentNode.id === "main") {
2154 if (hasClass(e, "type-decl")) {
2156 otherMessage = ' Show declaration';
2157 show = getCurrentValue('rustdoc-item-declarations') === "false";
2159 extraClass = 'collapsed';
2161 } else if (hasClass(e, "non-exhaustive")) {
2162 otherMessage = ' This ';
2163 if (hasClass(e, "non-exhaustive-struct")) {
2164 otherMessage += 'struct';
2165 } else if (hasClass(e, "non-exhaustive-enum")) {
2166 otherMessage += 'enum';
2167 } else if (hasClass(e, "non-exhaustive-type")) {
2168 otherMessage += 'type';
2170 otherMessage += ' is marked as non-exhaustive';
2171 } else if (hasClass(e.childNodes[0], "impl-items")) {
2172 extraClass = "marg-left";
2175 e.parentNode.insertBefore(createToggle(otherMessage, fontSize, extraClass, show), e);
2176 if (otherMessage && show) {
2177 collapseDocs(e.previousSibling.childNodes[0], "toggle");
2182 function createToggleWrapper(tog) {
2183 var span = document.createElement('span');
2184 span.className = 'toggle-label';
2185 span.style.display = 'none';
2186 span.innerHTML = ' Expand attributes';
2187 tog.appendChild(span);
2189 var wrapper = document.createElement('div');
2190 wrapper.className = 'toggle-wrapper toggle-attributes';
2191 wrapper.appendChild(tog);
2195 // In the search display, allows to switch between tabs.
2196 function printTab(nb) {
2197 if (nb === 0 || nb === 1 || nb === 2) {
2201 onEach(document.getElementById('titles').childNodes, function(elem) {
2202 if (nb_copy === 0) {
2203 addClass(elem, 'selected');
2205 removeClass(elem, 'selected');
2209 onEach(document.getElementById('results').childNodes, function(elem) {
2211 elem.style.display = '';
2213 elem.style.display = 'none';
2219 onEach(document.getElementById('main').getElementsByClassName('attributes'), function(i_e) {
2220 i_e.parentNode.insertBefore(createToggleWrapper(toggle.cloneNode(true)), i_e);
2221 if (getCurrentValue("rustdoc-item-attributes") !== "false") {
2222 collapseDocs(i_e.previousSibling.childNodes[0], "toggle");
2226 onEach(document.getElementsByClassName('rust-example-rendered'), function(e) {
2227 if (hasClass(e, 'compile_fail')) {
2228 e.addEventListener("mouseover", function(event) {
2229 e.previousElementSibling.childNodes[0].style.color = '#f00';
2231 e.addEventListener("mouseout", function(event) {
2232 e.previousElementSibling.childNodes[0].style.color = '';
2234 } else if (hasClass(e, 'ignore')) {
2235 e.addEventListener("mouseover", function(event) {
2236 e.previousElementSibling.childNodes[0].style.color = '#ff9200';
2238 e.addEventListener("mouseout", function(event) {
2239 e.previousElementSibling.childNodes[0].style.color = '';
2244 function showModal(content) {
2245 var modal = document.createElement('div');
2246 modal.id = "important";
2247 addClass(modal, 'modal');
2248 modal.innerHTML = '<div class="modal-content"><div class="close" id="modal-close">✕</div>' +
2249 '<div class="whiter"></div><span class="docblock">' + content +
2251 document.getElementsByTagName('body')[0].appendChild(modal);
2252 document.getElementById('modal-close').onclick = hideModal;
2253 modal.onclick = hideModal;
2256 function hideModal() {
2257 var modal = document.getElementById("important");
2259 modal.parentNode.removeChild(modal);
2263 onEach(document.getElementsByClassName('important-traits'), function(e) {
2264 e.onclick = function() {
2265 showModal(e.lastElementChild.innerHTML);
2269 function putBackSearch(search_input) {
2270 if (search_input.value !== "") {
2271 addClass(document.getElementById("main"), "hidden");
2272 removeClass(document.getElementById("search"), "hidden");
2273 if (browserSupportsHistoryApi()) {
2274 history.replaceState(search_input.value,
2276 "?search=" + encodeURIComponent(search_input.value));
2282 search_input.onfocus = function() {
2283 putBackSearch(this);
2287 var params = getQueryStringParams();
2288 if (params && params.search) {
2289 addClass(document.getElementById("main"), "hidden");
2290 var search = document.getElementById("search");
2291 removeClass(search, "hidden");
2292 search.innerHTML = '<h3 style="text-align: center;">Loading search results...</h3>';
2295 var sidebar_menu = document.getElementsByClassName("sidebar-menu")[0];
2297 sidebar_menu.onclick = function() {
2298 var sidebar = document.getElementsByClassName('sidebar')[0];
2299 if (hasClass(sidebar, "mobile") === true) {
2307 window.onresize = function() {
2311 autoCollapse(getPageId(), getCurrentValue("rustdoc-collapse") === "true");
2313 if (window.location.hash && window.location.hash.length > 0) {
2314 expandSection(window.location.hash.replace(/^#/, ''));
2318 // Sets the focus on the search bar at the top of the page
2319 function focusSearchBar() {
2320 document.getElementsByClassName('search-input')[0].focus();
2323 // Removes the focus from the search bar
2324 function defocusSearchBar() {
2325 document.getElementsByClassName('search-input')[0].blur();