You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

915 lines
36 KiB

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