PageRenderTime 107ms CodeModel.GetById 28ms RepoModel.GetById 2ms app.codeStats 0ms

/src/librustdoc/html/static/main.js

https://github.com/blaenk/rust
JavaScript | 1033 lines | 778 code | 112 blank | 143 comment | 182 complexity | 4b8d12aa66f9c6dede63f2ab1d0fd6dd MD5 | raw file
Possible License(s): 0BSD, MIT
  1. // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
  2. // file at the top-level directory of this distribution and at
  3. // http://rust-lang.org/COPYRIGHT.
  4. //
  5. // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
  6. // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
  7. // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
  8. // option. This file may not be copied, modified, or distributed
  9. // except according to those terms.
  10. /*jslint browser: true, es5: true */
  11. /*globals $: true, rootPath: true */
  12. (function() {
  13. "use strict";
  14. // This mapping table should match the discriminants of
  15. // `rustdoc::html::item_type::ItemType` type in Rust.
  16. var itemTypes = ["mod",
  17. "externcrate",
  18. "import",
  19. "struct",
  20. "enum",
  21. "fn",
  22. "type",
  23. "static",
  24. "trait",
  25. "impl",
  26. "tymethod",
  27. "method",
  28. "structfield",
  29. "variant",
  30. "macro",
  31. "primitive",
  32. "associatedtype",
  33. "constant",
  34. "associatedconstant"];
  35. // used for special search precedence
  36. var TY_PRIMITIVE = itemTypes.indexOf("primitive");
  37. $('.js-only').removeClass('js-only');
  38. function getQueryStringParams() {
  39. var params = {};
  40. window.location.search.substring(1).split("&").
  41. map(function(s) {
  42. var pair = s.split("=");
  43. params[decodeURIComponent(pair[0])] =
  44. typeof pair[1] === "undefined" ?
  45. null : decodeURIComponent(pair[1]);
  46. });
  47. return params;
  48. }
  49. function browserSupportsHistoryApi() {
  50. return document.location.protocol != "file:" &&
  51. window.history && typeof window.history.pushState === "function";
  52. }
  53. function highlightSourceLines(ev) {
  54. var i, from, to, match = window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/);
  55. if (match) {
  56. from = parseInt(match[1], 10);
  57. to = Math.min(50000, parseInt(match[2] || match[1], 10));
  58. from = Math.min(from, to);
  59. if ($('#' + from).length === 0) {
  60. return;
  61. }
  62. if (ev === null) { $('#' + from)[0].scrollIntoView(); };
  63. $('.line-numbers span').removeClass('line-highlighted');
  64. for (i = from; i <= to; ++i) {
  65. $('#' + i).addClass('line-highlighted');
  66. }
  67. }
  68. }
  69. highlightSourceLines(null);
  70. $(window).on('hashchange', highlightSourceLines);
  71. // Gets the human-readable string for the virtual-key code of the
  72. // given KeyboardEvent, ev.
  73. //
  74. // This function is meant as a polyfill for KeyboardEvent#key,
  75. // since it is not supported in Trident. We also test for
  76. // KeyboardEvent#keyCode because the handleShortcut handler is
  77. // also registered for the keydown event, because Blink doesn't fire
  78. // keypress on hitting the Escape key.
  79. //
  80. // So I guess you could say things are getting pretty interoperable.
  81. function getVirtualKey(ev) {
  82. if ("key" in ev && typeof ev.key != "undefined")
  83. return ev.key;
  84. var c = ev.charCode || ev.keyCode;
  85. if (c == 27)
  86. return "Escape";
  87. return String.fromCharCode(c);
  88. }
  89. function handleShortcut(ev) {
  90. if (document.activeElement.tagName == "INPUT")
  91. return;
  92. // Don't interfere with browser shortcuts
  93. if (ev.ctrlKey || ev.altKey || ev.metaKey)
  94. return;
  95. switch (getVirtualKey(ev)) {
  96. case "Escape":
  97. if (!$("#help").hasClass("hidden")) {
  98. ev.preventDefault();
  99. $("#help").addClass("hidden");
  100. $("body").removeClass("blur");
  101. } else if (!$("#search").hasClass("hidden")) {
  102. ev.preventDefault();
  103. $("#search").addClass("hidden");
  104. $("#main").removeClass("hidden");
  105. }
  106. break;
  107. case "s":
  108. case "S":
  109. ev.preventDefault();
  110. focusSearchBar();
  111. break;
  112. case "?":
  113. if (ev.shiftKey && $("#help").hasClass("hidden")) {
  114. ev.preventDefault();
  115. $("#help").removeClass("hidden");
  116. $("body").addClass("blur");
  117. }
  118. break;
  119. }
  120. }
  121. $(document).on("keypress", handleShortcut);
  122. $(document).on("keydown", handleShortcut);
  123. $(document).on("click", function(ev) {
  124. if (!$(ev.target).closest("#help > div").length) {
  125. $("#help").addClass("hidden");
  126. $("body").removeClass("blur");
  127. }
  128. });
  129. $('.version-selector').on('change', function() {
  130. var i, match,
  131. url = document.location.href,
  132. stripped = '',
  133. len = rootPath.match(/\.\.\//g).length + 1;
  134. for (i = 0; i < len; ++i) {
  135. match = url.match(/\/[^\/]*$/);
  136. if (i < len - 1) {
  137. stripped = match[0] + stripped;
  138. }
  139. url = url.substring(0, url.length - match[0].length);
  140. }
  141. url += '/' + $('.version-selector').val() + stripped;
  142. document.location.href = url;
  143. });
  144. /**
  145. * A function to compute the Levenshtein distance between two strings
  146. * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported
  147. * Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode
  148. * This code is an unmodified version of the code written by Marco de Wit
  149. * and was found at http://stackoverflow.com/a/18514751/745719
  150. */
  151. var levenshtein = (function() {
  152. var row2 = [];
  153. return function(s1, s2) {
  154. if (s1 === s2) {
  155. return 0;
  156. }
  157. var s1_len = s1.length, s2_len = s2.length;
  158. if (s1_len && s2_len) {
  159. var i1 = 0, i2 = 0, a, b, c, c2, row = row2;
  160. while (i1 < s1_len) {
  161. row[i1] = ++i1;
  162. }
  163. while (i2 < s2_len) {
  164. c2 = s2.charCodeAt(i2);
  165. a = i2;
  166. ++i2;
  167. b = i2;
  168. for (i1 = 0; i1 < s1_len; ++i1) {
  169. c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0);
  170. a = row[i1];
  171. b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
  172. row[i1] = b;
  173. }
  174. }
  175. return b;
  176. }
  177. return s1_len + s2_len;
  178. };
  179. })();
  180. function initSearch(rawSearchIndex) {
  181. var currentResults, index, searchIndex;
  182. var MAX_LEV_DISTANCE = 3;
  183. var params = getQueryStringParams();
  184. // Populate search bar with query string search term when provided,
  185. // but only if the input bar is empty. This avoid the obnoxious issue
  186. // where you start trying to do a search, and the index loads, and
  187. // suddenly your search is gone!
  188. if ($(".search-input")[0].value === "") {
  189. $(".search-input")[0].value = params.search || '';
  190. }
  191. /**
  192. * Executes the query and builds an index of results
  193. * @param {[Object]} query [The user query]
  194. * @param {[type]} max [The maximum results returned]
  195. * @param {[type]} searchWords [The list of search words to query
  196. * against]
  197. * @return {[type]} [A search index of results]
  198. */
  199. function execQuery(query, max, searchWords) {
  200. var valLower = query.query.toLowerCase(),
  201. val = valLower,
  202. typeFilter = itemTypeFromName(query.type),
  203. results = [],
  204. split = valLower.split("::");
  205. // remove empty keywords
  206. for (var j = 0; j < split.length; ++j) {
  207. split[j].toLowerCase();
  208. if (split[j] === "") {
  209. split.splice(j, 1);
  210. }
  211. }
  212. function typePassesFilter(filter, type) {
  213. // No filter
  214. if (filter < 0) return true;
  215. // Exact match
  216. if (filter === type) return true;
  217. // Match related items
  218. var name = itemTypes[type];
  219. switch (itemTypes[filter]) {
  220. case "constant":
  221. return (name == "associatedconstant");
  222. case "fn":
  223. return (name == "method" || name == "tymethod");
  224. case "type":
  225. return (name == "primitive");
  226. }
  227. // No match
  228. return false;
  229. }
  230. // quoted values mean literal search
  231. var nSearchWords = searchWords.length;
  232. if ((val.charAt(0) === "\"" || val.charAt(0) === "'") &&
  233. val.charAt(val.length - 1) === val.charAt(0))
  234. {
  235. val = val.substr(1, val.length - 2);
  236. for (var i = 0; i < nSearchWords; ++i) {
  237. if (searchWords[i] === val) {
  238. // filter type: ... queries
  239. if (typePassesFilter(typeFilter, searchIndex[i].ty)) {
  240. results.push({id: i, index: -1});
  241. }
  242. }
  243. if (results.length === max) {
  244. break;
  245. }
  246. }
  247. // searching by type
  248. } else if (val.search("->") > -1) {
  249. var trimmer = function (s) { return s.trim(); };
  250. var parts = val.split("->").map(trimmer);
  251. var input = parts[0];
  252. // sort inputs so that order does not matter
  253. var inputs = input.split(",").map(trimmer).sort().toString();
  254. var output = parts[1];
  255. for (var i = 0; i < nSearchWords; ++i) {
  256. var type = searchIndex[i].type;
  257. if (!type) {
  258. continue;
  259. }
  260. // sort index inputs so that order does not matter
  261. var typeInputs = type.inputs.map(function (input) {
  262. return input.name;
  263. }).sort();
  264. // allow searching for void (no output) functions as well
  265. var typeOutput = type.output ? type.output.name : "";
  266. if ((inputs === "*" || inputs === typeInputs.toString()) &&
  267. (output === "*" || output == typeOutput)) {
  268. results.push({id: i, index: -1, dontValidate: true});
  269. }
  270. }
  271. } else {
  272. // gather matching search results up to a certain maximum
  273. val = val.replace(/\_/g, "");
  274. for (var i = 0; i < split.length; ++i) {
  275. for (var j = 0; j < nSearchWords; ++j) {
  276. var lev_distance;
  277. if (searchWords[j].indexOf(split[i]) > -1 ||
  278. searchWords[j].indexOf(val) > -1 ||
  279. searchWords[j].replace(/_/g, "").indexOf(val) > -1)
  280. {
  281. // filter type: ... queries
  282. if (typePassesFilter(typeFilter, searchIndex[j].ty)) {
  283. results.push({
  284. id: j,
  285. index: searchWords[j].replace(/_/g, "").indexOf(val),
  286. lev: 0,
  287. });
  288. }
  289. } else if (
  290. (lev_distance = levenshtein(searchWords[j], val)) <=
  291. MAX_LEV_DISTANCE) {
  292. if (typePassesFilter(typeFilter, searchIndex[j].ty)) {
  293. results.push({
  294. id: j,
  295. index: 0,
  296. // we want lev results to go lower than others
  297. lev: lev_distance,
  298. });
  299. }
  300. }
  301. if (results.length === max) {
  302. break;
  303. }
  304. }
  305. }
  306. }
  307. var nresults = results.length;
  308. for (var i = 0; i < nresults; ++i) {
  309. results[i].word = searchWords[results[i].id];
  310. results[i].item = searchIndex[results[i].id] || {};
  311. }
  312. // if there are no results then return to default and fail
  313. if (results.length === 0) {
  314. return [];
  315. }
  316. results.sort(function sortResults(aaa, bbb) {
  317. var a, b;
  318. // Sort by non levenshtein results and then levenshtein results by the distance
  319. // (less changes required to match means higher rankings)
  320. a = (aaa.lev);
  321. b = (bbb.lev);
  322. if (a !== b) { return a - b; }
  323. // sort by crate (non-current crate goes later)
  324. a = (aaa.item.crate !== window.currentCrate);
  325. b = (bbb.item.crate !== window.currentCrate);
  326. if (a !== b) { return a - b; }
  327. // sort by exact match (mismatch goes later)
  328. a = (aaa.word !== valLower);
  329. b = (bbb.word !== valLower);
  330. if (a !== b) { return a - b; }
  331. // sort by item name length (longer goes later)
  332. a = aaa.word.length;
  333. b = bbb.word.length;
  334. if (a !== b) { return a - b; }
  335. // sort by item name (lexicographically larger goes later)
  336. a = aaa.word;
  337. b = bbb.word;
  338. if (a !== b) { return (a > b ? +1 : -1); }
  339. // sort by index of keyword in item name (no literal occurrence goes later)
  340. a = (aaa.index < 0);
  341. b = (bbb.index < 0);
  342. if (a !== b) { return a - b; }
  343. // (later literal occurrence, if any, goes later)
  344. a = aaa.index;
  345. b = bbb.index;
  346. if (a !== b) { return a - b; }
  347. // special precedence for primitive pages
  348. if ((aaa.item.ty === TY_PRIMITIVE) && (bbb.item.ty !== TY_PRIMITIVE)) {
  349. return -1;
  350. }
  351. if ((bbb.item.ty === TY_PRIMITIVE) && (aaa.item.ty !== TY_PRIMITIVE)) {
  352. return 1;
  353. }
  354. // sort by description (no description goes later)
  355. a = (aaa.item.desc === '');
  356. b = (bbb.item.desc === '');
  357. if (a !== b) { return a - b; }
  358. // sort by type (later occurrence in `itemTypes` goes later)
  359. a = aaa.item.ty;
  360. b = bbb.item.ty;
  361. if (a !== b) { return a - b; }
  362. // sort by path (lexicographically larger goes later)
  363. a = aaa.item.path;
  364. b = bbb.item.path;
  365. if (a !== b) { return (a > b ? +1 : -1); }
  366. // que sera, sera
  367. return 0;
  368. });
  369. // remove duplicates, according to the data provided
  370. for (var i = results.length - 1; i > 0; i -= 1) {
  371. if (results[i].word === results[i - 1].word &&
  372. results[i].item.ty === results[i - 1].item.ty &&
  373. results[i].item.path === results[i - 1].item.path &&
  374. (results[i].item.parent || {}).name === (results[i - 1].item.parent || {}).name)
  375. {
  376. results[i].id = -1;
  377. }
  378. }
  379. for (var i = 0; i < results.length; ++i) {
  380. var result = results[i],
  381. name = result.item.name.toLowerCase(),
  382. path = result.item.path.toLowerCase(),
  383. parent = result.item.parent;
  384. // this validation does not make sense when searching by types
  385. if (result.dontValidate) {
  386. continue;
  387. }
  388. var valid = validateResult(name, path, split, parent);
  389. if (!valid) {
  390. result.id = -1;
  391. }
  392. }
  393. return results;
  394. }
  395. /**
  396. * Validate performs the following boolean logic. For example:
  397. * "File::open" will give IF A PARENT EXISTS => ("file" && "open")
  398. * exists in (name || path || parent) OR => ("file" && "open") exists in
  399. * (name || path )
  400. *
  401. * This could be written functionally, but I wanted to minimise
  402. * functions on stack.
  403. *
  404. * @param {[string]} name [The name of the result]
  405. * @param {[string]} path [The path of the result]
  406. * @param {[string]} keys [The keys to be used (["file", "open"])]
  407. * @param {[object]} parent [The parent of the result]
  408. * @return {[boolean]} [Whether the result is valid or not]
  409. */
  410. function validateResult(name, path, keys, parent) {
  411. for (var i = 0; i < keys.length; ++i) {
  412. // each check is for validation so we negate the conditions and invalidate
  413. if (!(
  414. // check for an exact name match
  415. name.toLowerCase().indexOf(keys[i]) > -1 ||
  416. // then an exact path match
  417. path.toLowerCase().indexOf(keys[i]) > -1 ||
  418. // next if there is a parent, check for exact parent match
  419. (parent !== undefined &&
  420. parent.name.toLowerCase().indexOf(keys[i]) > -1) ||
  421. // lastly check to see if the name was a levenshtein match
  422. levenshtein(name.toLowerCase(), keys[i]) <=
  423. MAX_LEV_DISTANCE)) {
  424. return false;
  425. }
  426. }
  427. return true;
  428. }
  429. function getQuery() {
  430. var matches, type, query, raw = $('.search-input').val();
  431. query = raw;
  432. matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i);
  433. if (matches) {
  434. type = matches[1].replace(/^const$/, 'constant');
  435. query = query.substring(matches[0].length);
  436. }
  437. return {
  438. raw: raw,
  439. query: query,
  440. type: type,
  441. id: query + type
  442. };
  443. }
  444. function initSearchNav() {
  445. var hoverTimeout, $results = $('.search-results .result');
  446. $results.on('click', function() {
  447. var dst = $(this).find('a')[0];
  448. if (window.location.pathname === dst.pathname) {
  449. $('#search').addClass('hidden');
  450. $('#main').removeClass('hidden');
  451. document.location.href = dst.href;
  452. }
  453. }).on('mouseover', function() {
  454. var $el = $(this);
  455. clearTimeout(hoverTimeout);
  456. hoverTimeout = setTimeout(function() {
  457. $results.removeClass('highlighted');
  458. $el.addClass('highlighted');
  459. }, 20);
  460. });
  461. $(document).off('keydown.searchnav');
  462. $(document).on('keydown.searchnav', function(e) {
  463. var $active = $results.filter('.highlighted');
  464. if (e.which === 38) { // up
  465. if (!$active.length || !$active.prev()) {
  466. return;
  467. }
  468. $active.prev().addClass('highlighted');
  469. $active.removeClass('highlighted');
  470. } else if (e.which === 40) { // down
  471. if (!$active.length) {
  472. $results.first().addClass('highlighted');
  473. } else if ($active.next().length) {
  474. $active.next().addClass('highlighted');
  475. $active.removeClass('highlighted');
  476. }
  477. } else if (e.which === 13) { // return
  478. if ($active.length) {
  479. document.location.href = $active.find('a').prop('href');
  480. }
  481. } else {
  482. $active.removeClass('highlighted');
  483. }
  484. });
  485. }
  486. function escape(content) {
  487. return $('<h1/>').text(content).html();
  488. }
  489. function showResults(results) {
  490. var output, shown, query = getQuery();
  491. currentResults = query.id;
  492. output = '<h1>Results for ' + escape(query.query) +
  493. (query.type ? ' (type: ' + escape(query.type) + ')' : '') + '</h1>';
  494. output += '<table class="search-results">';
  495. if (results.length > 0) {
  496. shown = [];
  497. results.forEach(function(item) {
  498. var name, type, href, displayPath;
  499. if (shown.indexOf(item) !== -1) {
  500. return;
  501. }
  502. shown.push(item);
  503. name = item.name;
  504. type = itemTypes[item.ty];
  505. if (type === 'mod') {
  506. displayPath = item.path + '::';
  507. href = rootPath + item.path.replace(/::/g, '/') + '/' +
  508. name + '/index.html';
  509. } else if (type === 'static' || type === 'reexport') {
  510. displayPath = item.path + '::';
  511. href = rootPath + item.path.replace(/::/g, '/') +
  512. '/index.html';
  513. } else if (type === "primitive") {
  514. displayPath = "";
  515. href = rootPath + item.path.replace(/::/g, '/') +
  516. '/' + type + '.' + name + '.html';
  517. } else if (type === "externcrate") {
  518. displayPath = "";
  519. href = rootPath + name + '/index.html';
  520. } else if (item.parent !== undefined) {
  521. var myparent = item.parent;
  522. var anchor = '#' + type + '.' + name;
  523. displayPath = item.path + '::' + myparent.name + '::';
  524. href = rootPath + item.path.replace(/::/g, '/') +
  525. '/' + itemTypes[myparent.ty] +
  526. '.' + myparent.name +
  527. '.html' + anchor;
  528. } else {
  529. displayPath = item.path + '::';
  530. href = rootPath + item.path.replace(/::/g, '/') +
  531. '/' + type + '.' + name + '.html';
  532. }
  533. output += '<tr class="' + type + ' result"><td>' +
  534. '<a href="' + href + '">' +
  535. displayPath + '<span class="' + type + '">' +
  536. name + '</span></a></td><td>' +
  537. '<a href="' + href + '">' +
  538. '<span class="desc">' + item.desc +
  539. '&nbsp;</span></a></td></tr>';
  540. });
  541. } else {
  542. output += 'No results :( <a href="https://duckduckgo.com/?q=' +
  543. encodeURIComponent('rust ' + query.query) +
  544. '">Try on DuckDuckGo?</a>';
  545. }
  546. output += "</p>";
  547. $('#main.content').addClass('hidden');
  548. $('#search.content').removeClass('hidden').html(output);
  549. $('#search .desc').width($('#search').width() - 40 -
  550. $('#search td:first-child').first().width());
  551. initSearchNav();
  552. }
  553. function search(e) {
  554. var query,
  555. filterdata = [],
  556. obj, i, len,
  557. results = [],
  558. maxResults = 200,
  559. resultIndex;
  560. var params = getQueryStringParams();
  561. query = getQuery();
  562. if (e) {
  563. e.preventDefault();
  564. }
  565. if (!query.query || query.id === currentResults) {
  566. return;
  567. }
  568. // Update document title to maintain a meaningful browser history
  569. $(document).prop("title", "Results for " + query.query + " - Rust");
  570. // Because searching is incremental by character, only the most
  571. // recent search query is added to the browser history.
  572. if (browserSupportsHistoryApi()) {
  573. if (!history.state && !params.search) {
  574. history.pushState(query, "", "?search=" +
  575. encodeURIComponent(query.raw));
  576. } else {
  577. history.replaceState(query, "", "?search=" +
  578. encodeURIComponent(query.raw));
  579. }
  580. }
  581. resultIndex = execQuery(query, 20000, index);
  582. len = resultIndex.length;
  583. for (i = 0; i < len; ++i) {
  584. if (resultIndex[i].id > -1) {
  585. obj = searchIndex[resultIndex[i].id];
  586. filterdata.push([obj.name, obj.ty, obj.path, obj.desc]);
  587. results.push(obj);
  588. }
  589. if (results.length >= maxResults) {
  590. break;
  591. }
  592. }
  593. showResults(results);
  594. }
  595. function itemTypeFromName(typename) {
  596. for (var i = 0; i < itemTypes.length; ++i) {
  597. if (itemTypes[i] === typename) { return i; }
  598. }
  599. return -1;
  600. }
  601. function buildIndex(rawSearchIndex) {
  602. searchIndex = [];
  603. var searchWords = [];
  604. for (var crate in rawSearchIndex) {
  605. if (!rawSearchIndex.hasOwnProperty(crate)) { continue; }
  606. searchWords.push(crate);
  607. searchIndex.push({
  608. crate: crate,
  609. ty: 1, // == ExternCrate
  610. name: crate,
  611. path: "",
  612. desc: rawSearchIndex[crate].doc,
  613. type: null,
  614. });
  615. // an array of [(Number) item type,
  616. // (String) name,
  617. // (String) full path or empty string for previous path,
  618. // (String) description,
  619. // (Number | null) the parent path index to `paths`]
  620. // (Object | null) the type of the function (if any)
  621. var items = rawSearchIndex[crate].items;
  622. // an array of [(Number) item type,
  623. // (String) name]
  624. var paths = rawSearchIndex[crate].paths;
  625. // convert `paths` into an object form
  626. var len = paths.length;
  627. for (var i = 0; i < len; ++i) {
  628. paths[i] = {ty: paths[i][0], name: paths[i][1]};
  629. }
  630. // convert `items` into an object form, and construct word indices.
  631. //
  632. // before any analysis is performed lets gather the search terms to
  633. // search against apart from the rest of the data. This is a quick
  634. // operation that is cached for the life of the page state so that
  635. // all other search operations have access to this cached data for
  636. // faster analysis operations
  637. var len = items.length;
  638. var lastPath = "";
  639. for (var i = 0; i < len; ++i) {
  640. var rawRow = items[i];
  641. var row = {crate: crate, ty: rawRow[0], name: rawRow[1],
  642. path: rawRow[2] || lastPath, desc: rawRow[3],
  643. parent: paths[rawRow[4]], type: rawRow[5]};
  644. searchIndex.push(row);
  645. if (typeof row.name === "string") {
  646. var word = row.name.toLowerCase();
  647. searchWords.push(word);
  648. } else {
  649. searchWords.push("");
  650. }
  651. lastPath = row.path;
  652. }
  653. }
  654. return searchWords;
  655. }
  656. function startSearch() {
  657. var searchTimeout;
  658. $(".search-input").on("keyup input",function() {
  659. clearTimeout(searchTimeout);
  660. if ($(this).val().length === 0) {
  661. if (browserSupportsHistoryApi()) {
  662. history.replaceState("", "std - Rust", "?search=");
  663. }
  664. $('#main.content').removeClass('hidden');
  665. $('#search.content').addClass('hidden');
  666. } else {
  667. searchTimeout = setTimeout(search, 500);
  668. }
  669. });
  670. $('.search-form').on('submit', function(e){
  671. e.preventDefault();
  672. clearTimeout(searchTimeout);
  673. search();
  674. });
  675. $('.search-input').on('change paste', function(e) {
  676. // Do NOT e.preventDefault() here. It will prevent pasting.
  677. clearTimeout(searchTimeout);
  678. // zero-timeout necessary here because at the time of event handler execution the
  679. // pasted content is not in the input field yet. Shouldn’t make any difference for
  680. // change, though.
  681. setTimeout(search, 0);
  682. });
  683. // Push and pop states are used to add search results to the browser
  684. // history.
  685. if (browserSupportsHistoryApi()) {
  686. // Store the previous <title> so we can revert back to it later.
  687. var previousTitle = $(document).prop("title");
  688. $(window).on('popstate', function(e) {
  689. var params = getQueryStringParams();
  690. // When browsing back from search results the main page
  691. // visibility must be reset.
  692. if (!params.search) {
  693. $('#main.content').removeClass('hidden');
  694. $('#search.content').addClass('hidden');
  695. }
  696. // Revert to the previous title manually since the History
  697. // API ignores the title parameter.
  698. $(document).prop("title", previousTitle);
  699. // When browsing forward to search results the previous
  700. // search will be repeated, so the currentResults are
  701. // cleared to ensure the search is successful.
  702. currentResults = null;
  703. // Synchronize search bar with query string state and
  704. // perform the search. This will empty the bar if there's
  705. // nothing there, which lets you really go back to a
  706. // previous state with nothing in the bar.
  707. $('.search-input').val(params.search);
  708. // Some browsers fire 'onpopstate' for every page load
  709. // (Chrome), while others fire the event only when actually
  710. // popping a state (Firefox), which is why search() is
  711. // called both here and at the end of the startSearch()
  712. // function.
  713. search();
  714. });
  715. }
  716. search();
  717. }
  718. function plainSummaryLine(markdown) {
  719. markdown.replace(/\n/g, ' ')
  720. .replace(/'/g, "\'")
  721. .replace(/^#+? (.+?)/, "$1")
  722. .replace(/\[(.*?)\]\(.*?\)/g, "$1")
  723. .replace(/\[(.*?)\]\[.*?\]/g, "$1");
  724. }
  725. index = buildIndex(rawSearchIndex);
  726. startSearch();
  727. // Draw a convenient sidebar of known crates if we have a listing
  728. if (rootPath === '../') {
  729. var sidebar = $('.sidebar');
  730. var div = $('<div>').attr('class', 'block crate');
  731. div.append($('<h3>').text('Crates'));
  732. var ul = $('<ul>').appendTo(div);
  733. var crates = [];
  734. for (var crate in rawSearchIndex) {
  735. if (!rawSearchIndex.hasOwnProperty(crate)) { continue; }
  736. crates.push(crate);
  737. }
  738. crates.sort();
  739. for (var i = 0; i < crates.length; ++i) {
  740. var klass = 'crate';
  741. if (crates[i] === window.currentCrate) {
  742. klass += ' current';
  743. }
  744. if (rawSearchIndex[crates[i]].items[0]) {
  745. var desc = rawSearchIndex[crates[i]].items[0][3];
  746. var link = $('<a>', {'href': '../' + crates[i] + '/index.html',
  747. 'title': plainSummaryLine(desc),
  748. 'class': klass}).text(crates[i]);
  749. ul.append($('<li>').append(link));
  750. }
  751. }
  752. sidebar.append(div);
  753. }
  754. }
  755. window.initSearch = initSearch;
  756. // delayed sidebar rendering.
  757. function initSidebarItems(items) {
  758. var sidebar = $('.sidebar');
  759. var current = window.sidebarCurrent;
  760. function block(shortty, longty) {
  761. var filtered = items[shortty];
  762. if (!filtered) { return; }
  763. var div = $('<div>').attr('class', 'block ' + shortty);
  764. div.append($('<h3>').text(longty));
  765. var ul = $('<ul>').appendTo(div);
  766. for (var i = 0; i < filtered.length; ++i) {
  767. var item = filtered[i];
  768. var name = item[0];
  769. var desc = item[1]; // can be null
  770. var klass = shortty;
  771. if (name === current.name && shortty === current.ty) {
  772. klass += ' current';
  773. }
  774. var path;
  775. if (shortty === 'mod') {
  776. path = name + '/index.html';
  777. } else {
  778. path = shortty + '.' + name + '.html';
  779. }
  780. var link = $('<a>', {'href': current.relpath + path,
  781. 'title': desc,
  782. 'class': klass}).text(name);
  783. ul.append($('<li>').append(link));
  784. }
  785. sidebar.append(div);
  786. }
  787. block("mod", "Modules");
  788. block("struct", "Structs");
  789. block("enum", "Enums");
  790. block("trait", "Traits");
  791. block("fn", "Functions");
  792. block("macro", "Macros");
  793. }
  794. window.initSidebarItems = initSidebarItems;
  795. window.register_implementors = function(imp) {
  796. var list = $('#implementors-list');
  797. var libs = Object.getOwnPropertyNames(imp);
  798. for (var i = 0; i < libs.length; ++i) {
  799. if (libs[i] === currentCrate) { continue; }
  800. var structs = imp[libs[i]];
  801. for (var j = 0; j < structs.length; ++j) {
  802. var code = $('<code>').append(structs[j]);
  803. $.each(code.find('a'), function(idx, a) {
  804. var href = $(a).attr('href');
  805. if (href && href.indexOf('http') !== 0) {
  806. $(a).attr('href', rootPath + href);
  807. }
  808. });
  809. var li = $('<li>').append(code);
  810. list.append(li);
  811. }
  812. }
  813. };
  814. if (window.pending_implementors) {
  815. window.register_implementors(window.pending_implementors);
  816. }
  817. // See documentation in html/render.rs for what this is doing.
  818. var query = getQueryStringParams();
  819. if (query['gotosrc']) {
  820. window.location = $('#src-' + query['gotosrc']).attr('href');
  821. }
  822. if (query['gotomacrosrc']) {
  823. window.location = $('.srclink').attr('href');
  824. }
  825. function labelForToggleButton(sectionIsCollapsed) {
  826. if (sectionIsCollapsed) {
  827. // button will expand the section
  828. return "+";
  829. }
  830. // button will collapse the section
  831. // note that this text is also set in the HTML template in render.rs
  832. return "\u2212"; // "\u2212" is '−' minus sign
  833. }
  834. $("#toggle-all-docs").on("click", function() {
  835. var toggle = $("#toggle-all-docs");
  836. if (toggle.hasClass("will-expand")) {
  837. toggle.removeClass("will-expand");
  838. toggle.children(".inner").text(labelForToggleButton(false));
  839. toggle.attr("title", "collapse all docs");
  840. $(".docblock").show();
  841. $(".toggle-label").hide();
  842. $(".toggle-wrapper").removeClass("collapsed");
  843. $(".collapse-toggle").children(".inner").text(labelForToggleButton(false));
  844. } else {
  845. toggle.addClass("will-expand");
  846. toggle.children(".inner").text(labelForToggleButton(true));
  847. toggle.attr("title", "expand all docs");
  848. $(".docblock").hide();
  849. $(".toggle-label").show();
  850. $(".toggle-wrapper").addClass("collapsed");
  851. $(".collapse-toggle").children(".inner").text(labelForToggleButton(true));
  852. }
  853. });
  854. $(document).on("click", ".collapse-toggle", function() {
  855. var toggle = $(this);
  856. var relatedDoc = toggle.parent().next();
  857. if (relatedDoc.is(".stability")) {
  858. relatedDoc = relatedDoc.next();
  859. }
  860. if (relatedDoc.is(".docblock")) {
  861. if (relatedDoc.is(":visible")) {
  862. relatedDoc.slideUp({duration: 'fast', easing: 'linear'});
  863. toggle.parent(".toggle-wrapper").addClass("collapsed");
  864. toggle.children(".inner").text(labelForToggleButton(true));
  865. toggle.children(".toggle-label").fadeIn();
  866. } else {
  867. relatedDoc.slideDown({duration: 'fast', easing: 'linear'});
  868. toggle.parent(".toggle-wrapper").removeClass("collapsed");
  869. toggle.children(".inner").text(labelForToggleButton(false));
  870. toggle.children(".toggle-label").hide();
  871. }
  872. }
  873. });
  874. $(function() {
  875. var toggle = $("<a/>", {'href': 'javascript:void(0)', 'class': 'collapse-toggle'})
  876. .html("[<span class='inner'></span>]");
  877. toggle.children(".inner").text(labelForToggleButton(false));
  878. $(".method").each(function() {
  879. if ($(this).next().is(".docblock") ||
  880. ($(this).next().is(".stability") && $(this).next().next().is(".docblock"))) {
  881. $(this).children().last().after(toggle.clone());
  882. }
  883. });
  884. var mainToggle =
  885. $(toggle).append(
  886. $('<span/>', {'class': 'toggle-label'})
  887. .css('display', 'none')
  888. .html('&nbsp;Expand&nbsp;description'));
  889. var wrapper = $("<div class='toggle-wrapper'>").append(mainToggle);
  890. $("#main > .docblock").before(wrapper);
  891. });
  892. $('pre.line-numbers').on('click', 'span', function() {
  893. var prev_id = 0;
  894. function set_fragment(name) {
  895. if (browserSupportsHistoryApi()) {
  896. history.replaceState(null, null, '#' + name);
  897. $(window).trigger('hashchange');
  898. } else {
  899. location.replace('#' + name);
  900. }
  901. }
  902. return function(ev) {
  903. var cur_id = parseInt(ev.target.id, 10);
  904. if (ev.shiftKey && prev_id) {
  905. if (prev_id > cur_id) {
  906. var tmp = prev_id;
  907. prev_id = cur_id;
  908. cur_id = tmp;
  909. }
  910. set_fragment(prev_id + '-' + cur_id);
  911. } else {
  912. prev_id = cur_id;
  913. set_fragment(cur_id);
  914. }
  915. };
  916. }());
  917. }());
  918. // Sets the focus on the search bar at the top of the page
  919. function focusSearchBar() {
  920. $('.search-input').focus();
  921. }