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",
44 var search_input = document.getElementsByClassName('search-input')[0];
46 // On the search screen, so you remain on the last tab you opened.
49 // 1 for "In Parameters"
50 // 2 for "In Return Types"
53 var themesWidth = null;
55 if (!String.prototype.startsWith) {
56 String.prototype.startsWith = function(searchString, position) {
57 position = position || 0;
58 return this.indexOf(searchString, position) === position;
61 if (!String.prototype.endsWith) {
62 String.prototype.endsWith = function(suffix, length) {
63 var l = length || this.length;
64 return this.indexOf(suffix, l - suffix.length) !== -1;
68 function getPageId() {
69 var id = document.location.href.split('#')[1];
71 return id.split('?')[0].split('&')[0];
76 function hasClass(elem, className) {
77 if (elem && className && elem.className) {
78 var elemClass = elem.className;
79 var start = elemClass.indexOf(className);
82 } else if (elemClass.length === className.length) {
85 if (start > 0 && elemClass[start - 1] !== ' ') {
88 var end = start + className.length;
89 return !(end < elemClass.length && elemClass[end] !== ' ');
91 if (start > 0 && elemClass[start - 1] !== ' ') {
94 var end = start + className.length;
95 return !(end < elemClass.length && elemClass[end] !== ' ');
100 function addClass(elem, className) {
101 if (elem && className && !hasClass(elem, className)) {
102 if (elem.className && elem.className.length > 0) {
103 elem.className += ' ' + className;
105 elem.className = className;
110 function removeClass(elem, className) {
111 if (elem && className && elem.className) {
112 elem.className = (" " + elem.className + " ").replace(" " + className + " ", " ")
117 function isHidden(elem) {
118 return (elem.offsetParent === null)
121 function showSidebar() {
122 var elems = document.getElementsByClassName("sidebar-elems")[0];
124 addClass(elems, "show-it");
126 var sidebar = document.getElementsByClassName('sidebar')[0];
128 addClass(sidebar, 'mobile');
129 var filler = document.getElementById("sidebar-filler");
131 var div = document.createElement("div");
132 div.id = "sidebar-filler";
133 sidebar.appendChild(div);
136 var themePicker = document.getElementsByClassName("theme-picker");
137 if (themePicker && themePicker.length > 0) {
138 themePicker[0].style.display = "none";
142 function hideSidebar() {
143 var elems = document.getElementsByClassName("sidebar-elems")[0];
145 removeClass(elems, "show-it");
147 var sidebar = document.getElementsByClassName('sidebar')[0];
148 removeClass(sidebar, 'mobile');
149 var filler = document.getElementById("sidebar-filler");
153 document.getElementsByTagName("body")[0].style.marginTop = '';
154 var themePicker = document.getElementsByClassName("theme-picker");
155 if (themePicker && themePicker.length > 0) {
156 themePicker[0].style.display = null;
160 // used for special search precedence
161 var TY_PRIMITIVE = itemTypes.indexOf("primitive");
162 var TY_KEYWORD = itemTypes.indexOf("keyword");
164 onEach(document.getElementsByClassName('js-only'), function(e) {
165 removeClass(e, 'js-only');
168 function getQueryStringParams() {
170 window.location.search.substring(1).split("&").
172 var pair = s.split("=");
173 params[decodeURIComponent(pair[0])] =
174 typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]);
179 function browserSupportsHistoryApi() {
180 return document.location.protocol != "file:" &&
181 window.history && typeof window.history.pushState === "function";
184 function highlightSourceLines(ev) {
185 // If we're in mobile mode, we should add the sidebar in any case.
187 var search = document.getElementById("search");
188 var i, from, to, match = window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/);
190 from = parseInt(match[1], 10);
191 to = Math.min(50000, parseInt(match[2] || match[1], 10));
192 from = Math.min(from, to);
193 var elem = document.getElementById(from);
198 var x = document.getElementById(from);
203 onEach(document.getElementsByClassName('line-numbers'), function(e) {
204 onEach(e.getElementsByTagName('span'), function(i_e) {
205 removeClass(i_e, 'line-highlighted');
208 for (i = from; i <= to; ++i) {
209 addClass(document.getElementById(i), 'line-highlighted');
211 } else if (ev !== null && search && !hasClass(search, "hidden") && ev.newURL) {
212 addClass(search, "hidden");
213 removeClass(document.getElementById("main"), "hidden");
214 var hash = ev.newURL.slice(ev.newURL.indexOf('#') + 1);
215 if (browserSupportsHistoryApi()) {
216 history.replaceState(hash, "", "?search=#" + hash);
218 var elem = document.getElementById(hash);
220 elem.scrollIntoView();
224 highlightSourceLines(null);
225 window.onhashchange = highlightSourceLines;
227 // Gets the human-readable string for the virtual-key code of the
228 // given KeyboardEvent, ev.
230 // This function is meant as a polyfill for KeyboardEvent#key,
231 // since it is not supported in Trident. We also test for
232 // KeyboardEvent#keyCode because the handleShortcut handler is
233 // also registered for the keydown event, because Blink doesn't fire
234 // keypress on hitting the Escape key.
236 // So I guess you could say things are getting pretty interoperable.
237 function getVirtualKey(ev) {
238 if ("key" in ev && typeof ev.key != "undefined")
241 var c = ev.charCode || ev.keyCode;
244 return String.fromCharCode(c);
247 function displayHelp(display, ev) {
248 if (display === true) {
249 if (hasClass(help, "hidden")) {
251 removeClass(help, "hidden");
252 addClass(document.body, "blur");
254 } else if (!hasClass(help, "hidden")) {
256 addClass(help, "hidden");
257 removeClass(document.body, "blur");
261 function handleEscape(ev, help) {
263 var search = document.getElementById("search");
264 if (!hasClass(help, "hidden")) {
265 displayHelp(false, ev);
266 } else if (!hasClass(search, "hidden")) {
268 addClass(search, "hidden");
269 removeClass(document.getElementById("main"), "hidden");
274 function handleShortcut(ev) {
275 // Don't interfere with browser shortcuts
276 if (ev.ctrlKey || ev.altKey || ev.metaKey) {
280 var help = document.getElementById("help");
281 if (document.activeElement.tagName === "INPUT") {
282 switch (getVirtualKey(ev)) {
284 handleEscape(ev, help);
288 switch (getVirtualKey(ev)) {
290 handleEscape(ev, help);
295 displayHelp(false, ev);
310 displayHelp(true, ev);
317 document.onkeypress = handleShortcut;
318 document.onkeydown = handleShortcut;
319 document.onclick = function(ev) {
320 if (hasClass(ev.target, 'collapse-toggle')) {
321 collapseDocs(ev.target, "toggle");
322 } else if (hasClass(ev.target.parentNode, 'collapse-toggle')) {
323 collapseDocs(ev.target.parentNode, "toggle");
324 } else if (ev.target.tagName === 'SPAN' && hasClass(ev.target.parentNode, 'line-numbers')) {
327 var set_fragment = function(name) {
328 if (browserSupportsHistoryApi()) {
329 history.replaceState(null, null, '#' + name);
332 location.replace('#' + name);
336 var cur_id = parseInt(ev.target.id, 10);
338 if (ev.shiftKey && prev_id) {
339 if (prev_id > cur_id) {
345 set_fragment(prev_id + '-' + cur_id);
349 set_fragment(cur_id);
351 } else if (!hasClass(document.getElementById("help"), "hidden")) {
352 addClass(document.getElementById("help"), "hidden");
353 removeClass(document.body, "blur");
357 var x = document.getElementsByClassName('version-selector');
359 x[0].onchange = function() {
361 url = document.location.href,
363 len = rootPath.match(/\.\.\//g).length + 1;
365 for (i = 0; i < len; ++i) {
366 match = url.match(/\/[^\/]*$/);
368 stripped = match[0] + stripped;
370 url = url.substring(0, url.length - match[0].length);
373 url += '/' + document.getElementsByClassName('version-selector')[0].value + stripped;
375 document.location.href = url;
380 * A function to compute the Levenshtein distance between two strings
381 * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported
382 * Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode
383 * This code is an unmodified version of the code written by Marco de Wit
384 * and was found at http://stackoverflow.com/a/18514751/745719
386 var levenshtein_row2 = [];
387 function levenshtein(s1, s2) {
391 var s1_len = s1.length, s2_len = s2.length;
392 if (s1_len && s2_len) {
393 var i1 = 0, i2 = 0, a, b, c, c2, row = levenshtein_row2;
394 while (i1 < s1_len) {
397 while (i2 < s2_len) {
398 c2 = s2.charCodeAt(i2);
402 for (i1 = 0; i1 < s1_len; ++i1) {
403 c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0);
405 b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
411 return s1_len + s2_len;
414 function initSearch(rawSearchIndex) {
415 var currentResults, index, searchIndex;
416 var MAX_LEV_DISTANCE = 3;
417 var MAX_RESULTS = 200;
418 var GENERICS_DATA = 1;
422 var params = getQueryStringParams();
424 // Populate search bar with query string search term when provided,
425 // but only if the input bar is empty. This avoid the obnoxious issue
426 // where you start trying to do a search, and the index loads, and
427 // suddenly your search is gone!
428 if (search_input.value === "") {
429 search_input.value = params.search || '';
433 * Executes the query and builds an index of results
434 * @param {[Object]} query [The user query]
435 * @param {[type]} searchWords [The list of search words to query
437 * @return {[type]} [A search index of results]
439 function execQuery(query, searchWords) {
440 function itemTypeFromName(typename) {
441 for (var i = 0; i < itemTypes.length; ++i) {
442 if (itemTypes[i] === typename) {
449 var valLower = query.query.toLowerCase(),
451 typeFilter = itemTypeFromName(query.type),
452 results = {}, results_in_args = {}, results_returned = {},
453 split = valLower.split("::");
455 for (var z = 0; z < split.length; ++z) {
456 if (split[z] === "") {
462 function transformResults(results, isType) {
464 for (i = 0; i < results.length; ++i) {
465 if (results[i].id > -1) {
466 var obj = searchIndex[results[i].id];
467 obj.lev = results[i].lev;
468 if (isType !== true || obj.type) {
469 var res = buildHrefAndPath(obj);
470 obj.displayPath = pathSplitter(res[0]);
471 obj.fullPath = obj.displayPath + obj.name;
472 // To be sure than it some items aren't considered as duplicate.
473 obj.fullPath += '|' + obj.ty;
476 if (out.length >= MAX_RESULTS) {
485 function sortResults(results, isType) {
487 for (var entry in results) {
488 if (results.hasOwnProperty(entry)) {
489 ar.push(results[entry]);
493 var nresults = results.length;
494 for (var i = 0; i < nresults; ++i) {
495 results[i].word = searchWords[results[i].id];
496 results[i].item = searchIndex[results[i].id] || {};
498 // if there are no results then return to default and fail
499 if (results.length === 0) {
503 results.sort(function(aaa, bbb) {
506 // Sort by non levenshtein results and then levenshtein results by the distance
507 // (less changes required to match means higher rankings)
510 if (a !== b) { return a - b; }
512 // sort by crate (non-current crate goes later)
513 a = (aaa.item.crate !== window.currentCrate);
514 b = (bbb.item.crate !== window.currentCrate);
515 if (a !== b) { return a - b; }
517 // sort by exact match (mismatch goes later)
518 a = (aaa.word !== valLower);
519 b = (bbb.word !== valLower);
520 if (a !== b) { return a - b; }
522 // sort by item name length (longer goes later)
525 if (a !== b) { return a - b; }
527 // sort by item name (lexicographically larger goes later)
530 if (a !== b) { return (a > b ? +1 : -1); }
532 // sort by index of keyword in item name (no literal occurrence goes later)
535 if (a !== b) { return a - b; }
536 // (later literal occurrence, if any, goes later)
539 if (a !== b) { return a - b; }
541 // special precedence for primitive and keyword pages
542 if ((aaa.item.ty === TY_PRIMITIVE && bbb.item.ty !== TY_KEYWORD) ||
543 (aaa.item.ty === TY_KEYWORD && bbb.item.ty !== TY_PRIMITIVE)) {
546 if ((bbb.item.ty === TY_PRIMITIVE && aaa.item.ty !== TY_PRIMITIVE) ||
547 (bbb.item.ty === TY_KEYWORD && aaa.item.ty !== TY_KEYWORD)) {
551 // sort by description (no description goes later)
552 a = (aaa.item.desc === '');
553 b = (bbb.item.desc === '');
554 if (a !== b) { return a - b; }
556 // sort by type (later occurrence in `itemTypes` goes later)
559 if (a !== b) { return a - b; }
561 // sort by path (lexicographically larger goes later)
564 if (a !== b) { return (a > b ? +1 : -1); }
570 for (var i = 0; i < results.length; ++i) {
571 var result = results[i];
573 // this validation does not make sense when searching by types
574 if (result.dontValidate) {
577 var name = result.item.name.toLowerCase(),
578 path = result.item.path.toLowerCase(),
579 parent = result.item.parent;
581 if (isType !== true &&
582 validateResult(name, path, split, parent) === false)
587 return transformResults(results);
590 function extractGenerics(val) {
591 val = val.toLowerCase();
592 if (val.indexOf('<') !== -1) {
593 var values = val.substring(val.indexOf('<') + 1, val.lastIndexOf('>'));
595 name: val.substring(0, val.indexOf('<')),
596 generics: values.split(/\s*,\s*/),
605 function checkGenerics(obj, val) {
606 // The names match, but we need to be sure that all generics kinda
608 var lev_distance = MAX_LEV_DISTANCE + 1;
609 if (val.generics.length > 0) {
610 if (obj.length > GENERICS_DATA &&
611 obj[GENERICS_DATA].length >= val.generics.length) {
612 var elems = obj[GENERICS_DATA].slice(0);
615 // We need to find the type that matches the most to remove it in order
617 for (var y = 0; y < val.generics.length; ++y) {
618 var lev = { pos: -1, lev: MAX_LEV_DISTANCE + 1};
619 for (var x = 0; x < elems.length; ++x) {
620 var tmp_lev = levenshtein(elems[x], val.generics[y]);
621 if (tmp_lev < lev.lev) {
626 if (lev.pos !== -1) {
627 elems.splice(lev.pos, 1);
628 lev_distance = Math.min(lev.lev, lev_distance);
632 return MAX_LEV_DISTANCE + 1;
635 return lev_distance;//Math.ceil(total / done);
638 return MAX_LEV_DISTANCE + 1;
641 // Check for type name and type generics (if any).
642 function checkType(obj, val, literalSearch) {
643 var lev_distance = MAX_LEV_DISTANCE + 1;
644 if (obj[NAME] === val.name) {
645 if (literalSearch === true) {
646 if (val.generics && val.generics.length !== 0) {
647 if (obj.length > GENERICS_DATA &&
648 obj[GENERICS_DATA].length >= val.generics.length) {
649 var elems = obj[GENERICS_DATA].slice(0);
653 for (var y = 0; allFound === true && y < val.generics.length; ++y) {
655 for (x = 0; allFound === false && x < elems.length; ++x) {
656 allFound = elems[x] === val.generics[y];
658 if (allFound === true) {
659 elems.splice(x - 1, 1);
662 if (allFound === true) {
671 // If the type has generics but don't match, then it won't return at this point.
672 // Otherwise, `checkGenerics` will return 0 and it'll return.
673 if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length !== 0) {
674 var tmp_lev = checkGenerics(obj, val);
675 if (tmp_lev <= MAX_LEV_DISTANCE) {
682 // Names didn't match so let's check if one of the generic types could.
683 if (literalSearch === true) {
684 if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
685 for (var x = 0; x < obj[GENERICS_DATA].length; ++x) {
686 if (obj[GENERICS_DATA][x] === val.name) {
693 var lev_distance = Math.min(levenshtein(obj[NAME], val.name),
695 if (lev_distance <= MAX_LEV_DISTANCE) {
696 lev_distance = Math.min(checkGenerics(obj, val), lev_distance);
697 } else if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
698 // We can check if the type we're looking for is inside the generics!
699 for (var x = 0; x < obj[GENERICS_DATA].length; ++x) {
700 lev_distance = Math.min(levenshtein(obj[GENERICS_DATA][x], val.name),
704 // Now whatever happens, the returned distance is "less good" so we should mark it
705 // as such, and so we add 1 to the distance to make it "less good".
706 return lev_distance + 1;
709 function findArg(obj, val, literalSearch) {
710 var lev_distance = MAX_LEV_DISTANCE + 1;
712 if (obj && obj.type && obj.type[INPUTS_DATA] &&
713 obj.type[INPUTS_DATA].length > 0) {
714 for (var i = 0; i < obj.type[INPUTS_DATA].length; i++) {
715 var tmp = checkType(obj.type[INPUTS_DATA][i], val, literalSearch);
716 if (literalSearch === true && tmp === true) {
719 lev_distance = Math.min(tmp, lev_distance);
720 if (lev_distance === 0) {
725 return literalSearch === true ? false : lev_distance;
728 function checkReturned(obj, val, literalSearch) {
729 var lev_distance = MAX_LEV_DISTANCE + 1;
731 if (obj && obj.type && obj.type.length > OUTPUT_DATA) {
732 var tmp = checkType(obj.type[OUTPUT_DATA], val, literalSearch);
733 if (literalSearch === true && tmp === true) {
736 lev_distance = Math.min(tmp, lev_distance);
737 if (lev_distance === 0) {
741 return literalSearch === true ? false : lev_distance;
744 function checkPath(startsWith, lastElem, ty) {
745 if (startsWith.length === 0) {
748 var ret_lev = MAX_LEV_DISTANCE + 1;
749 var path = ty.path.split("::");
751 if (ty.parent && ty.parent.name) {
752 path.push(ty.parent.name.toLowerCase());
755 if (startsWith.length > path.length) {
756 return MAX_LEV_DISTANCE + 1;
758 for (var i = 0; i < path.length; ++i) {
759 if (i + startsWith.length > path.length) {
764 for (var x = 0; x < startsWith.length; ++x) {
765 var lev = levenshtein(path[i + x], startsWith[x]);
766 if (lev > MAX_LEV_DISTANCE) {
772 if (aborted === false) {
773 ret_lev = Math.min(ret_lev, Math.round(lev_total / startsWith.length));
779 function typePassesFilter(filter, type) {
781 if (filter < 0) return true;
784 if (filter === type) return true;
786 // Match related items
787 var name = itemTypes[type];
788 switch (itemTypes[filter]) {
790 return (name == "associatedconstant");
792 return (name == "method" || name == "tymethod");
794 return (name == "primitive" || name == "keyword");
801 function generateId(ty) {
802 if (ty.parent && ty.parent.name) {
803 return itemTypes[ty.ty] + ty.path + ty.parent.name + ty.name;
805 return itemTypes[ty.ty] + ty.path + ty.name;
808 // quoted values mean literal search
809 var nSearchWords = searchWords.length;
810 if ((val.charAt(0) === "\"" || val.charAt(0) === "'") &&
811 val.charAt(val.length - 1) === val.charAt(0))
813 val = extractGenerics(val.substr(1, val.length - 2));
814 for (var i = 0; i < nSearchWords; ++i) {
815 var in_args = findArg(searchIndex[i], val, true);
816 var returned = checkReturned(searchIndex[i], val, true);
817 var ty = searchIndex[i];
818 var fullId = generateId(ty);
820 if (searchWords[i] === val.name) {
821 // filter type: ... queries
822 if (typePassesFilter(typeFilter, searchIndex[i].ty) &&
823 results[fullId] === undefined)
825 results[fullId] = {id: i, index: -1};
827 } else if ((in_args === true || returned === true) &&
828 typePassesFilter(typeFilter, searchIndex[i].ty)) {
829 if (in_args === true || returned === true) {
830 if (in_args === true) {
831 results_in_args[fullId] = {
837 if (returned === true) {
838 results_returned[fullId] = {
853 query.inputs = [val];
857 } else if (val.search("->") > -1) {
858 var trimmer = function(s) { return s.trim(); };
859 var parts = val.split("->").map(trimmer);
860 var input = parts[0];
861 // sort inputs so that order does not matter
862 var inputs = input.split(",").map(trimmer).sort();
863 for (var i = 0; i < inputs.length; ++i) {
864 inputs[i] = extractGenerics(inputs[i]);
866 var output = extractGenerics(parts[1]);
868 for (var i = 0; i < nSearchWords; ++i) {
869 var type = searchIndex[i].type;
870 var ty = searchIndex[i];
874 var fullId = generateId(ty);
876 // allow searching for void (no output) functions as well
877 var typeOutput = type.length > OUTPUT_DATA ? type[OUTPUT_DATA].name : "";
878 var returned = checkReturned(ty, output, true);
879 if (output.name === "*" || returned === true) {
887 for (var it = 0; allFound === true && it < inputs.length; it++) {
888 allFound = checkType(type, inputs[it], true);
892 if (in_args === true) {
893 results_in_args[fullId] = {
899 if (returned === true) {
900 results_returned[fullId] = {
906 if (module === true) {
915 query.inputs = inputs.map(function(input) {
918 query.output = output.name;
920 query.inputs = [val];
923 // gather matching search results up to a certain maximum
924 val = val.replace(/\_/g, "");
926 var valGenerics = extractGenerics(val);
928 var paths = valLower.split("::");
930 for (j = 0; j < paths.length; ++j) {
931 if (paths[j] === "") {
936 val = paths[paths.length - 1];
937 var startsWith = paths.slice(0, paths.length > 1 ? paths.length - 1 : 1);
939 for (j = 0; j < nSearchWords; ++j) {
941 var ty = searchIndex[j];
946 if (paths.length > 1) {
947 var lev = checkPath(startsWith, paths[paths.length - 1], ty);
948 if (lev > MAX_LEV_DISTANCE) {
950 } else if (lev > 0) {
955 var returned = MAX_LEV_DISTANCE + 1;
956 var in_args = MAX_LEV_DISTANCE + 1;
958 // we want lev results to go lower than others
959 var lev = MAX_LEV_DISTANCE + 1;
960 var fullId = generateId(ty);
962 if (searchWords[j].indexOf(split[i]) > -1 ||
963 searchWords[j].indexOf(val) > -1 ||
964 searchWords[j].replace(/_/g, "").indexOf(val) > -1)
966 // filter type: ... queries
967 if (typePassesFilter(typeFilter, ty.ty) && results[fullId] === undefined) {
968 index = searchWords[j].replace(/_/g, "").indexOf(val);
971 if ((lev = levenshtein(searchWords[j], val)) <= MAX_LEV_DISTANCE) {
972 if (typePassesFilter(typeFilter, ty.ty) === false) {
973 lev = MAX_LEV_DISTANCE + 1;
978 if ((in_args = findArg(ty, valGenerics)) <= MAX_LEV_DISTANCE) {
979 if (typePassesFilter(typeFilter, ty.ty) === false) {
980 in_args = MAX_LEV_DISTANCE + 1;
983 if ((returned = checkReturned(ty, valGenerics)) <= MAX_LEV_DISTANCE) {
984 if (typePassesFilter(typeFilter, ty.ty) === false) {
985 returned = MAX_LEV_DISTANCE + 1;
990 if (lev > 0 && val.length > 3 && searchWords[j].startsWith(val)) {
991 if (val.length < 6) {
997 if (in_args <= MAX_LEV_DISTANCE) {
998 if (results_in_args[fullId] === undefined) {
999 results_in_args[fullId] = {
1005 results_in_args[fullId].lev =
1006 Math.min(results_in_args[fullId].lev, in_args);
1008 if (returned <= MAX_LEV_DISTANCE) {
1009 if (results_returned[fullId] === undefined) {
1010 results_returned[fullId] = {
1016 results_returned[fullId].lev =
1017 Math.min(results_returned[fullId].lev, returned);
1019 if (index !== -1 || lev <= MAX_LEV_DISTANCE) {
1020 if (index !== -1 && paths.length < 2) {
1023 if (results[fullId] === undefined) {
1030 results[fullId].lev = Math.min(results[fullId].lev, lev);
1036 'in_args': sortResults(results_in_args, true),
1037 'returned': sortResults(results_returned, true),
1038 'others': sortResults(results),
1040 if (ALIASES && ALIASES[window.currentCrate] &&
1041 ALIASES[window.currentCrate][query.raw]) {
1042 var aliases = ALIASES[window.currentCrate][query.raw];
1043 for (var i = 0; i < aliases.length; ++i) {
1044 aliases[i].is_alias = true;
1045 aliases[i].alias = query.raw;
1046 aliases[i].path = aliases[i].p;
1047 var res = buildHrefAndPath(aliases[i]);
1048 aliases[i].displayPath = pathSplitter(res[0]);
1049 aliases[i].fullPath = aliases[i].displayPath + aliases[i].name;
1050 aliases[i].href = res[1];
1051 ret['others'].unshift(aliases[i]);
1052 if (ret['others'].length > MAX_RESULTS) {
1053 ret['others'].pop();
1061 * Validate performs the following boolean logic. For example:
1062 * "File::open" will give IF A PARENT EXISTS => ("file" && "open")
1063 * exists in (name || path || parent) OR => ("file" && "open") exists in
1066 * This could be written functionally, but I wanted to minimise
1067 * functions on stack.
1069 * @param {[string]} name [The name of the result]
1070 * @param {[string]} path [The path of the result]
1071 * @param {[string]} keys [The keys to be used (["file", "open"])]
1072 * @param {[object]} parent [The parent of the result]
1073 * @return {[boolean]} [Whether the result is valid or not]
1075 function validateResult(name, path, keys, parent) {
1076 for (var i = 0; i < keys.length; ++i) {
1077 // each check is for validation so we negate the conditions and invalidate
1079 // check for an exact name match
1080 name.indexOf(keys[i]) > -1 ||
1081 // then an exact path match
1082 path.indexOf(keys[i]) > -1 ||
1083 // next if there is a parent, check for exact parent match
1084 (parent !== undefined &&
1085 parent.name.toLowerCase().indexOf(keys[i]) > -1) ||
1086 // lastly check to see if the name was a levenshtein match
1087 levenshtein(name, keys[i]) <= MAX_LEV_DISTANCE)) {
1094 function getQuery(raw) {
1095 var matches, type, query;
1098 matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i);
1100 type = matches[1].replace(/^const$/, 'constant');
1101 query = query.substring(matches[0].length);
1112 function initSearchNav() {
1115 var click_func = function(e) {
1117 // to retrieve the real "owner" of the event.
1118 while (el.tagName !== 'TR') {
1121 var dst = e.target.getElementsByTagName('a');
1122 if (dst.length < 1) {
1126 if (window.location.pathname === dst.pathname) {
1127 addClass(document.getElementById('search'), 'hidden');
1128 removeClass(document.getElementById('main'), 'hidden');
1129 document.location.href = dst.href;
1132 var mouseover_func = function(e) {
1134 // to retrieve the real "owner" of the event.
1135 while (el.tagName !== 'TR') {
1138 clearTimeout(hoverTimeout);
1139 hoverTimeout = setTimeout(function() {
1140 onEach(document.getElementsByClassName('search-results'), function(e) {
1141 onEach(e.getElementsByClassName('result'), function(i_e) {
1142 removeClass(i_e, 'highlighted');
1145 addClass(el, 'highlighted');
1148 onEach(document.getElementsByClassName('search-results'), function(e) {
1149 onEach(e.getElementsByClassName('result'), function(i_e) {
1150 i_e.onclick = click_func;
1151 i_e.onmouseover = mouseover_func;
1155 search_input.onkeydown = function(e) {
1156 // "actives" references the currently highlighted item in each search tab.
1157 // Each array in "actives" represents a tab.
1158 var actives = [[], [], []];
1159 // "current" is used to know which tab we're looking into.
1161 onEach(document.getElementsByClassName('search-results'), function(e) {
1162 onEach(e.getElementsByClassName('highlighted'), function(e) {
1163 actives[current].push(e);
1168 if (e.which === 38) { // up
1169 if (!actives[currentTab].length ||
1170 !actives[currentTab][0].previousElementSibling) {
1174 addClass(actives[currentTab][0].previousElementSibling, 'highlighted');
1175 removeClass(actives[currentTab][0], 'highlighted');
1176 } else if (e.which === 40) { // down
1177 if (!actives[currentTab].length) {
1178 var results = document.getElementsByClassName('search-results');
1179 if (results.length > 0) {
1180 var res = results[currentTab].getElementsByClassName('result');
1181 if (res.length > 0) {
1182 addClass(res[0], 'highlighted');
1185 } else if (actives[currentTab][0].nextElementSibling) {
1186 addClass(actives[currentTab][0].nextElementSibling, 'highlighted');
1187 removeClass(actives[currentTab][0], 'highlighted');
1189 } else if (e.which === 13) { // return
1190 if (actives[currentTab].length) {
1191 document.location.href =
1192 actives[currentTab][0].getElementsByTagName('a')[0].href;
1194 } else if (e.which === 9) { // tab
1196 printTab(currentTab > 0 ? currentTab - 1 : 2);
1198 printTab(currentTab > 1 ? 0 : currentTab + 1);
1201 } else if (e.which === 16) { // shift
1202 // Does nothing, it's just to avoid losing "focus" on the highlighted element.
1203 } else if (e.which === 27) { // escape
1204 removeClass(actives[currentTab][0], 'highlighted');
1205 search_input.value = '';
1207 } else if (actives[currentTab].length > 0) {
1208 removeClass(actives[currentTab][0], 'highlighted');
1213 function buildHrefAndPath(item) {
1216 var type = itemTypes[item.ty];
1217 var name = item.name;
1219 if (type === 'mod') {
1220 displayPath = item.path + '::';
1221 href = rootPath + item.path.replace(/::/g, '/') + '/' +
1222 name + '/index.html';
1223 } else if (type === "primitive" || type === "keyword") {
1225 href = rootPath + item.path.replace(/::/g, '/') +
1226 '/' + type + '.' + name + '.html';
1227 } else if (type === "externcrate") {
1229 href = rootPath + name + '/index.html';
1230 } else if (item.parent !== undefined) {
1231 var myparent = item.parent;
1232 var anchor = '#' + type + '.' + name;
1233 var parentType = itemTypes[myparent.ty];
1234 if (parentType === "primitive") {
1235 displayPath = myparent.name + '::';
1237 displayPath = item.path + '::' + myparent.name + '::';
1239 href = rootPath + item.path.replace(/::/g, '/') +
1241 '.' + myparent.name +
1244 displayPath = item.path + '::';
1245 href = rootPath + item.path.replace(/::/g, '/') +
1246 '/' + type + '.' + name + '.html';
1248 return [displayPath, href];
1251 function escape(content) {
1252 var h1 = document.createElement('h1');
1253 h1.textContent = content;
1254 return h1.innerHTML;
1257 function pathSplitter(path) {
1258 var tmp = '<span>' + path.replace(/::/g, '::</span><span>');
1259 if (tmp.endsWith("<span>")) {
1260 return tmp.slice(0, tmp.length - 6);
1265 function addTab(array, query, display) {
1266 var extraStyle = '';
1267 if (display === false) {
1268 extraStyle = ' style="display: none;"';
1272 var duplicates = {};
1274 if (array.length > 0) {
1275 output = '<table class="search-results"' + extraStyle + '>';
1277 array.forEach(function(item) {
1281 type = itemTypes[item.ty];
1283 if (item.is_alias !== true) {
1284 if (duplicates[item.fullPath]) {
1287 duplicates[item.fullPath] = true;
1291 output += '<tr class="' + type + ' result"><td>' +
1292 '<a href="' + item.href + '">' +
1293 (item.is_alias === true ?
1294 ('<span class="alias"><b>' + item.alias + ' </b></span><span ' +
1295 'class="grey"><i> - see </i></span>') : '') +
1296 item.displayPath + '<span class="' + type + '">' +
1297 name + '</span></a></td><td>' +
1298 '<a href="' + item.href + '">' +
1299 '<span class="desc">' + escape(item.desc) +
1300 ' </span></a></td></tr>';
1302 output += '</table>';
1304 output = '<div class="search-failed"' + extraStyle + '>No results :(<br/>' +
1305 'Try on <a href="https://duckduckgo.com/?q=' +
1306 encodeURIComponent('rust ' + query.query) +
1307 '">DuckDuckGo</a>?</div>';
1309 return [output, length];
1312 function makeTabHeader(tabNb, text, nbElems) {
1313 if (currentTab === tabNb) {
1314 return '<div class="selected">' + text +
1315 ' <div class="count">(' + nbElems + ')</div></div>';
1317 return '<div>' + text + ' <div class="count">(' + nbElems + ')</div></div>';
1320 function showResults(results) {
1321 if (results['others'].length === 1 &&
1322 getCurrentValue('rustdoc-go-to-only-result') === "true") {
1323 var elem = document.createElement('a');
1324 elem.href = results['others'][0].href;
1325 elem.style.display = 'none';
1326 // For firefox, we need the element to be in the DOM so it can be clicked.
1327 document.body.appendChild(elem);
1330 var query = getQuery(search_input.value);
1332 currentResults = query.id;
1334 var ret_others = addTab(results['others'], query);
1335 var ret_in_args = addTab(results['in_args'], query, false);
1336 var ret_returned = addTab(results['returned'], query, false);
1338 var output = '<h1>Results for ' + escape(query.query) +
1339 (query.type ? ' (type: ' + escape(query.type) + ')' : '') + '</h1>' +
1340 '<div id="titles">' +
1341 makeTabHeader(0, "In Names", ret_others[1]) +
1342 makeTabHeader(1, "In Parameters", ret_in_args[1]) +
1343 makeTabHeader(2, "In Return Types", ret_returned[1]) +
1344 '</div><div id="results">' +
1345 ret_others[0] + ret_in_args[0] + ret_returned[0] + '</div>';
1347 addClass(document.getElementById('main'), 'hidden');
1348 var search = document.getElementById('search');
1349 removeClass(search, 'hidden');
1350 search.innerHTML = output;
1351 var tds = search.getElementsByTagName('td');
1353 if (tds.length > 0) {
1354 td_width = tds[0].offsetWidth;
1356 var width = search.offsetWidth - 40 - td_width;
1357 onEach(search.getElementsByClassName('desc'), function(e) {
1358 e.style.width = width + 'px';
1361 var elems = document.getElementById('titles').childNodes;
1362 elems[0].onclick = function() { printTab(0); };
1363 elems[1].onclick = function() { printTab(1); };
1364 elems[2].onclick = function() { printTab(2); };
1365 printTab(currentTab);
1368 function execSearch(query, searchWords) {
1369 var queries = query.raw.split(",");
1376 for (var i = 0; i < queries.length; ++i) {
1377 var query = queries[i].trim();
1378 if (query.length !== 0) {
1379 var tmp = execQuery(getQuery(query), searchWords);
1381 results['in_args'].push(tmp['in_args']);
1382 results['returned'].push(tmp['returned']);
1383 results['others'].push(tmp['others']);
1386 if (queries.length > 1) {
1387 function getSmallest(arrays, positions, notDuplicates) {
1390 for (var it = 0; it < positions.length; ++it) {
1391 if (arrays[it].length > positions[it] &&
1392 (start === null || start > arrays[it][positions[it]].lev) &&
1393 !notDuplicates[arrays[it][positions[it]].fullPath]) {
1394 start = arrays[it][positions[it]].lev;
1400 function mergeArrays(arrays) {
1403 var notDuplicates = {};
1405 for (var x = 0; x < arrays.length; ++x) {
1408 while (ret.length < MAX_RESULTS) {
1409 var smallest = getSmallest(arrays, positions, notDuplicates);
1411 if (smallest === null) {
1414 for (x = 0; x < arrays.length && ret.length < MAX_RESULTS; ++x) {
1415 if (arrays[x].length > positions[x] &&
1416 arrays[x][positions[x]].lev === smallest &&
1417 !notDuplicates[arrays[x][positions[x]].fullPath]) {
1418 ret.push(arrays[x][positions[x]]);
1419 notDuplicates[arrays[x][positions[x]].fullPath] = true;
1428 'in_args': mergeArrays(results['in_args']),
1429 'returned': mergeArrays(results['returned']),
1430 'others': mergeArrays(results['others']),
1434 'in_args': results['in_args'][0],
1435 'returned': results['returned'][0],
1436 'others': results['others'][0],
1441 function search(e) {
1442 var params = getQueryStringParams();
1443 var query = getQuery(search_input.value.trim());
1449 if (query.query.length === 0 || query.id === currentResults) {
1450 if (query.query.length > 0) {
1451 putBackSearch(search_input);
1456 // Update document title to maintain a meaningful browser history
1457 document.title = "Results for " + query.query + " - Rust";
1459 // Because searching is incremental by character, only the most
1460 // recent search query is added to the browser history.
1461 if (browserSupportsHistoryApi()) {
1462 if (!history.state && !params.search) {
1463 history.pushState(query, "", "?search=" + encodeURIComponent(query.raw));
1465 history.replaceState(query, "", "?search=" + encodeURIComponent(query.raw));
1469 showResults(execSearch(query, index));
1472 function buildIndex(rawSearchIndex) {
1474 var searchWords = [];
1475 for (var crate in rawSearchIndex) {
1476 if (!rawSearchIndex.hasOwnProperty(crate)) { continue; }
1478 searchWords.push(crate);
1481 ty: 1, // == ExternCrate
1484 desc: rawSearchIndex[crate].doc,
1488 // an array of [(Number) item type,
1490 // (String) full path or empty string for previous path,
1491 // (String) description,
1492 // (Number | null) the parent path index to `paths`]
1493 // (Object | null) the type of the function (if any)
1494 var items = rawSearchIndex[crate].items;
1495 // an array of [(Number) item type,
1497 var paths = rawSearchIndex[crate].paths;
1499 // convert `paths` into an object form
1500 var len = paths.length;
1501 for (var i = 0; i < len; ++i) {
1502 paths[i] = {ty: paths[i][0], name: paths[i][1]};
1505 // convert `items` into an object form, and construct word indices.
1507 // before any analysis is performed lets gather the search terms to
1508 // search against apart from the rest of the data. This is a quick
1509 // operation that is cached for the life of the page state so that
1510 // all other search operations have access to this cached data for
1511 // faster analysis operations
1512 var len = items.length;
1514 for (var i = 0; i < len; ++i) {
1515 var rawRow = items[i];
1516 var row = {crate: crate, ty: rawRow[0], name: rawRow[1],
1517 path: rawRow[2] || lastPath, desc: rawRow[3],
1518 parent: paths[rawRow[4]], type: rawRow[5]};
1519 searchIndex.push(row);
1520 if (typeof row.name === "string") {
1521 var word = row.name.toLowerCase();
1522 searchWords.push(word);
1524 searchWords.push("");
1526 lastPath = row.path;
1532 function startSearch() {
1534 var callback = function() {
1535 clearTimeout(searchTimeout);
1536 if (search_input.value.length === 0) {
1537 if (browserSupportsHistoryApi()) {
1538 history.replaceState("", "std - Rust", "?search=");
1540 var main = document.getElementById('main');
1541 if (hasClass(main, 'content')) {
1542 removeClass(main, 'hidden');
1544 var search_c = document.getElementById('search');
1545 if (hasClass(search_c, 'content')) {
1546 addClass(search_c, 'hidden');
1549 searchTimeout = setTimeout(search, 500);
1552 search_input.onkeyup = callback;
1553 search_input.oninput = callback;
1554 document.getElementsByClassName("search-form")[0].onsubmit = function(e) {
1556 clearTimeout(searchTimeout);
1559 search_input.onchange = function(e) {
1560 // Do NOT e.preventDefault() here. It will prevent pasting.
1561 clearTimeout(searchTimeout);
1562 // zero-timeout necessary here because at the time of event handler execution the
1563 // pasted content is not in the input field yet. Shouldn’t make any difference for
1565 setTimeout(search, 0);
1567 search_input.onpaste = search_input.onchange;
1569 // Push and pop states are used to add search results to the browser
1571 if (browserSupportsHistoryApi()) {
1572 // Store the previous <title> so we can revert back to it later.
1573 var previousTitle = document.title;
1575 window.onpopstate = function(e) {
1576 var params = getQueryStringParams();
1577 // When browsing back from search results the main page
1578 // visibility must be reset.
1579 if (!params.search) {
1580 var main = document.getElementById('main');
1581 if (hasClass(main, 'content')) {
1582 removeClass(main, 'hidden');
1584 var search_c = document.getElementById('search');
1585 if (hasClass(search_c, 'content')) {
1586 addClass(search_c, 'hidden');
1589 // Revert to the previous title manually since the History
1590 // API ignores the title parameter.
1591 document.title = previousTitle;
1592 // When browsing forward to search results the previous
1593 // search will be repeated, so the currentResults are
1594 // cleared to ensure the search is successful.
1595 currentResults = null;
1596 // Synchronize search bar with query string state and
1597 // perform the search. This will empty the bar if there's
1598 // nothing there, which lets you really go back to a
1599 // previous state with nothing in the bar.
1600 if (params.search) {
1601 search_input.value = params.search;
1603 search_input.value = '';
1605 // Some browsers fire 'onpopstate' for every page load
1606 // (Chrome), while others fire the event only when actually
1607 // popping a state (Firefox), which is why search() is
1608 // called both here and at the end of the startSearch()
1616 index = buildIndex(rawSearchIndex);
1619 // Draw a convenient sidebar of known crates if we have a listing
1620 if (rootPath === '../' || rootPath === "./") {
1621 var sidebar = document.getElementsByClassName('sidebar-elems')[0];
1623 var div = document.createElement('div');
1624 div.className = 'block crate';
1625 div.innerHTML = '<h3>Crates</h3>';
1626 var ul = document.createElement('ul');
1627 div.appendChild(ul);
1630 for (var crate in rawSearchIndex) {
1631 if (!rawSearchIndex.hasOwnProperty(crate)) {
1637 for (var i = 0; i < crates.length; ++i) {
1638 var klass = 'crate';
1639 if (rootPath !== "./" && crates[i] === window.currentCrate) {
1640 klass += ' current';
1642 var link = document.createElement('a');
1643 link.href = rootPath + crates[i] + '/index.html';
1644 link.title = rawSearchIndex[crates[i]].doc;
1645 link.className = klass;
1646 link.textContent = crates[i];
1648 var li = document.createElement('li');
1649 li.appendChild(link);
1652 sidebar.appendChild(div);
1657 window.initSearch = initSearch;
1659 // delayed sidebar rendering.
1660 function initSidebarItems(items) {
1661 var sidebar = document.getElementsByClassName('sidebar-elems')[0];
1662 var current = window.sidebarCurrent;
1664 function block(shortty, longty) {
1665 var filtered = items[shortty];
1666 if (!filtered) { return; }
1668 var div = document.createElement('div');
1669 div.className = 'block ' + shortty;
1670 var h3 = document.createElement('h3');
1671 h3.textContent = longty;
1672 div.appendChild(h3);
1673 var ul = document.createElement('ul');
1675 for (var i = 0; i < filtered.length; ++i) {
1676 var item = filtered[i];
1678 var desc = item[1]; // can be null
1680 var klass = shortty;
1681 if (name === current.name && shortty === current.ty) {
1682 klass += ' current';
1685 if (shortty === 'mod') {
1686 path = name + '/index.html';
1688 path = shortty + '.' + name + '.html';
1690 var link = document.createElement('a');
1691 link.href = current.relpath + path;
1693 link.className = klass;
1694 link.textContent = name;
1695 var li = document.createElement('li');
1696 li.appendChild(link);
1699 div.appendChild(ul);
1701 sidebar.appendChild(div);
1705 block("primitive", "Primitive Types");
1706 block("mod", "Modules");
1707 block("macro", "Macros");
1708 block("struct", "Structs");
1709 block("enum", "Enums");
1710 block("union", "Unions");
1711 block("constant", "Constants");
1712 block("static", "Statics");
1713 block("trait", "Traits");
1714 block("fn", "Functions");
1715 block("type", "Type Definitions");
1716 block("foreigntype", "Foreign Types");
1717 block("keyword", "Keywords");
1720 window.initSidebarItems = initSidebarItems;
1722 window.register_implementors = function(imp) {
1723 var implementors = document.getElementById('implementors-list');
1724 var synthetic_implementors = document.getElementById('synthetic-implementors-list');
1726 var libs = Object.getOwnPropertyNames(imp);
1727 for (var i = 0; i < libs.length; ++i) {
1728 if (libs[i] === currentCrate) { continue; }
1729 var structs = imp[libs[i]];
1732 for (var j = 0; j < structs.length; ++j) {
1733 var struct = structs[j];
1735 var list = struct.synthetic ? synthetic_implementors : implementors;
1737 if (struct.synthetic) {
1738 for (var k = 0; k < struct.types.length; k++) {
1739 if (window.inlined_types.has(struct.types[k])) {
1740 continue struct_loop;
1742 window.inlined_types.add(struct.types[k]);
1746 var code = document.createElement('code');
1747 code.innerHTML = struct.text;
1749 var x = code.getElementsByTagName('a');
1750 for (var k = 0; k < x.length; k++) {
1751 var href = x[k].getAttribute('href');
1752 if (href && href.indexOf('http') !== 0) {
1753 x[k].setAttribute('href', rootPath + href);
1756 var li = document.createElement('li');
1757 li.appendChild(code);
1758 list.appendChild(li);
1762 if (window.pending_implementors) {
1763 window.register_implementors(window.pending_implementors);
1766 function labelForToggleButton(sectionIsCollapsed) {
1767 if (sectionIsCollapsed) {
1768 // button will expand the section
1771 // button will collapse the section
1772 // note that this text is also set in the HTML template in render.rs
1773 return "\u2212"; // "\u2212" is '−' minus sign
1776 function onEveryMatchingChild(elem, className, func) {
1777 if (elem && className && func) {
1778 for (var i = 0; i < elem.childNodes.length; i++) {
1779 if (hasClass(elem.childNodes[i], className)) {
1780 func(elem.childNodes[i]);
1782 onEveryMatchingChild(elem.childNodes[i], className, func);
1788 function toggleAllDocs(pageId, fromAutoCollapse) {
1789 var toggle = document.getElementById("toggle-all-docs");
1793 if (hasClass(toggle, "will-expand")) {
1794 updateLocalStorage("rustdoc-collapse", "false");
1795 removeClass(toggle, "will-expand");
1796 onEveryMatchingChild(toggle, "inner", function(e) {
1797 e.innerHTML = labelForToggleButton(false);
1799 toggle.title = "collapse all docs";
1800 if (fromAutoCollapse !== true) {
1801 onEach(document.getElementsByClassName("collapse-toggle"), function(e) {
1802 collapseDocs(e, "show");
1806 updateLocalStorage("rustdoc-collapse", "true");
1807 addClass(toggle, "will-expand");
1808 onEveryMatchingChild(toggle, "inner", function(e) {
1809 e.innerHTML = labelForToggleButton(true);
1811 toggle.title = "expand all docs";
1812 if (fromAutoCollapse !== true) {
1813 onEach(document.getElementsByClassName("collapse-toggle"), function(e) {
1814 collapseDocs(e, "hide", pageId);
1820 function collapseDocs(toggle, mode, pageId) {
1821 if (!toggle || !toggle.parentNode) {
1825 function adjustToggle(arg) {
1826 return function(e) {
1827 if (hasClass(e, 'toggle-label')) {
1829 e.style.display = 'inline-block';
1831 e.style.display = 'none';
1834 if (hasClass(e, 'inner')) {
1835 e.innerHTML = labelForToggleButton(arg);
1840 if (!hasClass(toggle.parentNode, "impl")) {
1841 var relatedDoc = toggle.parentNode.nextElementSibling;
1842 if (hasClass(relatedDoc, "stability")) {
1843 relatedDoc = relatedDoc.nextElementSibling;
1845 if (hasClass(relatedDoc, "docblock")) {
1847 if (action === "toggle") {
1848 if (hasClass(relatedDoc, "hidden-by-usual-hider")) {
1854 if (action === "hide") {
1855 addClass(relatedDoc, "hidden-by-usual-hider");
1856 onEach(toggle.childNodes, adjustToggle(true));
1857 addClass(toggle.parentNode, 'collapsed');
1858 } else if (action === "show") {
1859 removeClass(relatedDoc, "hidden-by-usual-hider");
1860 removeClass(toggle.parentNode, 'collapsed');
1861 onEach(toggle.childNodes, adjustToggle(false));
1865 // we are collapsing the impl block
1866 function implHider(addOrRemove) {
1867 return function(n) {
1868 var is_method = hasClass(n, "method");
1869 if (is_method || hasClass(n, "type")) {
1870 if (is_method === true) {
1872 addClass(n, "hidden-by-impl-hider");
1874 removeClass(n, "hidden-by-impl-hider");
1877 var ns = n.nextElementSibling;
1880 hasClass(ns, "docblock") ||
1881 hasClass(ns, "stability"))) {
1883 addClass(ns, "hidden-by-impl-hider");
1885 removeClass(ns, "hidden-by-impl-hider");
1887 ns = ns.nextElementSibling;
1896 var parentElem = toggle.parentNode;
1897 var relatedDoc = parentElem;
1898 var docblock = relatedDoc.nextElementSibling;
1900 while (!hasClass(relatedDoc, "impl-items")) {
1901 relatedDoc = relatedDoc.nextElementSibling;
1904 if ((!relatedDoc && !hasClass(docblock, "docblock")) ||
1905 (pageId && onEach(relatedDoc.childNodes, function(e) {
1906 return e.id === pageId;
1911 // Hide all functions, but not associated types/consts
1914 if (action === "toggle") {
1915 if (hasClass(relatedDoc, "fns-now-collapsed") ||
1916 hasClass(docblock, "hidden-by-impl-hider")) {
1923 if (action === "show") {
1924 removeClass(relatedDoc, "fns-now-collapsed");
1925 removeClass(docblock, "hidden-by-usual-hider");
1926 onEach(toggle.childNodes, adjustToggle(false));
1927 onEach(relatedDoc.childNodes, implHider(false));
1928 } else if (action === "hide") {
1929 addClass(relatedDoc, "fns-now-collapsed");
1930 addClass(docblock, "hidden-by-usual-hider");
1931 onEach(toggle.childNodes, adjustToggle(true));
1932 onEach(relatedDoc.childNodes, implHider(true));
1937 function autoCollapse(pageId, collapse) {
1939 toggleAllDocs(pageId, true);
1941 if (getCurrentValue('rustdoc-trait-implementations') !== "false") {
1942 onEach(document.getElementsByClassName("collapse-toggle"), function(e) {
1943 // inherent impl ids are like 'impl' or impl-<number>'.
1944 // they will never be hidden by default.
1945 var n = e.parentNode;
1946 if (n.id.match(/^impl(?:-\d+)?$/) === null) {
1947 // Automatically minimize all non-inherent impls
1948 if (collapse || hasClass(n, 'impl')) {
1949 collapseDocs(e, "hide", pageId);
1956 var x = document.getElementById('toggle-all-docs');
1958 x.onclick = toggleAllDocs;
1961 function insertAfter(newNode, referenceNode) {
1962 referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
1965 function checkIfThereAreMethods(elems) {
1966 var areThereMethods = false;
1968 onEach(elems, function(e) {
1969 if (hasClass(e, "method")) {
1970 areThereMethods = true;
1974 return areThereMethods;
1977 var toggle = document.createElement('a');
1978 toggle.href = 'javascript:void(0)';
1979 toggle.className = 'collapse-toggle';
1980 toggle.innerHTML = "[<span class='inner'>" + labelForToggleButton(false) + "</span>]";
1982 var func = function(e) {
1983 var next = e.nextElementSibling;
1984 if (hasClass(e, 'impl') && next && hasClass(next, 'docblock')) {
1985 next = next.nextElementSibling;
1990 if ((checkIfThereAreMethods(next.childNodes) || hasClass(e, 'method')) &&
1991 (hasClass(next, 'docblock') ||
1992 hasClass(e, 'impl') ||
1993 (hasClass(next, 'stability') &&
1994 hasClass(next.nextElementSibling, 'docblock')))) {
1995 insertAfter(toggle.cloneNode(true), e.childNodes[e.childNodes.length - 1]);
1998 onEach(document.getElementsByClassName('method'), func);
1999 onEach(document.getElementsByClassName('impl'), func);
2000 onEach(document.getElementsByClassName('impl-items'), function(e) {
2001 onEach(e.getElementsByClassName('associatedconstant'), func);
2004 function createToggle(otherMessage, fontSize, extraClass) {
2005 var span = document.createElement('span');
2006 span.className = 'toggle-label';
2007 span.style.display = 'none';
2008 if (!otherMessage) {
2009 span.innerHTML = ' Expand description';
2011 span.innerHTML = otherMessage;
2015 span.style.fontSize = fontSize;
2018 var mainToggle = toggle.cloneNode(true);
2019 mainToggle.appendChild(span);
2021 var wrapper = document.createElement('div');
2022 wrapper.className = 'toggle-wrapper';
2024 wrapper.className += ' ' + extraClass;
2026 wrapper.appendChild(mainToggle);
2030 onEach(document.getElementsByClassName('docblock'), function(e) {
2031 if (hasClass(e, 'autohide')) {
2032 var wrap = e.previousElementSibling;
2033 if (wrap && hasClass(wrap, 'toggle-wrapper')) {
2034 var toggle = wrap.childNodes[0];
2036 if (e.childNodes[0].tagName === 'H3') {
2039 e.style.display = 'none';
2040 addClass(wrap, 'collapsed');
2041 onEach(toggle.getElementsByClassName('inner'), function(e) {
2042 e.innerHTML = labelForToggleButton(true);
2044 onEach(toggle.getElementsByClassName('toggle-label'), function(e) {
2045 e.style.display = 'inline-block';
2046 if (extra === true) {
2047 i_e.innerHTML = " Show " + e.childNodes[0].innerHTML;
2052 if (e.parentNode.id === "main") {
2057 if (hasClass(e, "type-decl")) {
2059 otherMessage = ' Show declaration';
2060 } else if (hasClass(e, "non-exhaustive")) {
2061 otherMessage = ' This ';
2062 if (hasClass(e, "non-exhaustive-struct")) {
2063 otherMessage += 'struct';
2064 } else if (hasClass(e, "non-exhaustive-enum")) {
2065 otherMessage += 'enum';
2066 } else if (hasClass(e, "non-exhaustive-type")) {
2067 otherMessage += 'type';
2069 otherMessage += ' is marked as non-exhaustive';
2070 } else if (hasClass(e.childNodes[0], "impl-items")) {
2071 extraClass = "marg-left";
2074 e.parentNode.insertBefore(createToggle(otherMessage, fontSize, extraClass), e);
2075 if (otherMessage && getCurrentValue('rustdoc-item-declarations') !== "false") {
2076 collapseDocs(e.previousSibling.childNodes[0], "toggle");
2081 function createToggleWrapper(tog) {
2082 var span = document.createElement('span');
2083 span.className = 'toggle-label';
2084 span.style.display = 'none';
2085 span.innerHTML = ' Expand attributes';
2086 tog.appendChild(span);
2088 var wrapper = document.createElement('div');
2089 wrapper.className = 'toggle-wrapper toggle-attributes';
2090 wrapper.appendChild(tog);
2094 // In the search display, allows to switch between tabs.
2095 function printTab(nb) {
2096 if (nb === 0 || nb === 1 || nb === 2) {
2100 onEach(document.getElementById('titles').childNodes, function(elem) {
2101 if (nb_copy === 0) {
2102 addClass(elem, 'selected');
2104 removeClass(elem, 'selected');
2108 onEach(document.getElementById('results').childNodes, function(elem) {
2110 elem.style.display = '';
2112 elem.style.display = 'none';
2118 onEach(document.getElementById('main').getElementsByClassName('attributes'), function(i_e) {
2119 i_e.parentNode.insertBefore(createToggleWrapper(toggle.cloneNode(true)), i_e);
2120 if (getCurrentValue("rustdoc-item-attributes") !== "false") {
2121 collapseDocs(i_e.previousSibling.childNodes[0], "toggle");
2125 onEach(document.getElementsByClassName('rust-example-rendered'), function(e) {
2126 if (hasClass(e, 'compile_fail')) {
2127 e.addEventListener("mouseover", function(event) {
2128 e.previousElementSibling.childNodes[0].style.color = '#f00';
2130 e.addEventListener("mouseout", function(event) {
2131 e.previousElementSibling.childNodes[0].style.color = '';
2133 } else if (hasClass(e, 'ignore')) {
2134 e.addEventListener("mouseover", function(event) {
2135 e.previousElementSibling.childNodes[0].style.color = '#ff9200';
2137 e.addEventListener("mouseout", function(event) {
2138 e.previousElementSibling.childNodes[0].style.color = '';
2143 function showModal(content) {
2144 var modal = document.createElement('div');
2145 modal.id = "important";
2146 addClass(modal, 'modal');
2147 modal.innerHTML = '<div class="modal-content"><div class="close" id="modal-close">✕</div>' +
2148 '<div class="whiter"></div><span class="docblock">' + content +
2150 document.getElementsByTagName('body')[0].appendChild(modal);
2151 document.getElementById('modal-close').onclick = hideModal;
2152 modal.onclick = hideModal;
2155 function hideModal() {
2156 var modal = document.getElementById("important");
2158 modal.parentNode.removeChild(modal);
2162 onEach(document.getElementsByClassName('important-traits'), function(e) {
2163 e.onclick = function() {
2164 showModal(e.lastElementChild.innerHTML);
2168 function putBackSearch(search_input) {
2169 if (search_input.value !== "") {
2170 addClass(document.getElementById("main"), "hidden");
2171 removeClass(document.getElementById("search"), "hidden");
2172 if (browserSupportsHistoryApi()) {
2173 history.replaceState(search_input.value,
2175 "?search=" + encodeURIComponent(search_input.value));
2181 search_input.onfocus = function() {
2182 putBackSearch(this);
2186 var params = getQueryStringParams();
2187 if (params && params.search) {
2188 addClass(document.getElementById("main"), "hidden");
2189 var search = document.getElementById("search");
2190 removeClass(search, "hidden");
2191 search.innerHTML = '<h3 style="text-align: center;">Loading search results...</h3>';
2194 var sidebar_menu = document.getElementsByClassName("sidebar-menu")[0];
2196 sidebar_menu.onclick = function() {
2197 var sidebar = document.getElementsByClassName('sidebar')[0];
2198 if (hasClass(sidebar, "mobile") === true) {
2206 window.onresize = function() {
2210 autoCollapse(getPageId(), getCurrentValue("rustdoc-collapse") === "true");
2213 // Sets the focus on the search bar at the top of the page
2214 function focusSearchBar() {
2215 document.getElementsByClassName('search-input')[0].focus();
2218 // Removes the focus from the search bar
2219 function defocusSearchBar() {
2220 document.getElementsByClassName('search-input')[0].blur();