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