]>
Commit | Line | Data |
---|---|---|
0731742a | 1 | // Local js definitions: |
17df50a5 XL |
2 | /* global addClass, getSettingValue, hasClass, searchState */ |
3 | /* global onEach, onEachLazy, removeClass */ | |
1a4d82fc | 4 | |
04454e1e FG |
5 | "use strict"; |
6 | ||
a2a8927a XL |
7 | // Get a value from the rustdoc-vars div, which is used to convey data from |
8 | // Rust to the JS. If there is no such element, return null. | |
9 | function getVar(name) { | |
04454e1e | 10 | const el = document.getElementById("rustdoc-vars"); |
a2a8927a XL |
11 | if (el) { |
12 | return el.attributes["data-" + name].value; | |
13 | } else { | |
14 | return null; | |
5869c6ff | 15 | } |
a2a8927a XL |
16 | } |
17 | ||
18 | // Given a basename (e.g. "storage") and an extension (e.g. ".js"), return a URL | |
19 | // for a resource under the root-path, with the resource-suffix. | |
20 | function resourcePath(basename, extension) { | |
21 | return getVar("root-path") + basename + getVar("resource-suffix") + extension; | |
22 | } | |
23 | ||
04454e1e FG |
24 | function hideMain() { |
25 | addClass(document.getElementById(MAIN_ID), "hidden"); | |
26 | } | |
27 | ||
28 | function showMain() { | |
29 | removeClass(document.getElementById(MAIN_ID), "hidden"); | |
30 | } | |
31 | ||
064997fb FG |
32 | function elemIsInParent(elem, parent) { |
33 | while (elem && elem !== document.body) { | |
34 | if (elem === parent) { | |
35 | return true; | |
36 | } | |
37 | elem = elem.parentElement; | |
38 | } | |
39 | return false; | |
40 | } | |
41 | ||
42 | function blurHandler(event, parentElem, hideCallback) { | |
43 | if (!elemIsInParent(document.activeElement, parentElem) && | |
44 | !elemIsInParent(event.relatedTarget, parentElem) | |
45 | ) { | |
46 | hideCallback(); | |
47 | } | |
48 | } | |
49 | ||
487cf647 FG |
50 | window.rootPath = getVar("root-path"); |
51 | window.currentCrate = getVar("current-crate"); | |
fc512014 | 52 | |
923072b8 FG |
53 | function setMobileTopbar() { |
54 | // FIXME: It would be nicer to generate this text content directly in HTML, | |
55 | // but with the current code it's hard to get the right information in the right place. | |
2b03887a | 56 | const mobileLocationTitle = document.querySelector(".mobile-topbar h2"); |
923072b8 FG |
57 | const locationTitle = document.querySelector(".sidebar h2.location"); |
58 | if (mobileLocationTitle && locationTitle) { | |
59 | mobileLocationTitle.innerHTML = locationTitle.innerHTML; | |
60 | } | |
61 | } | |
62 | ||
fc512014 XL |
63 | // Gets the human-readable string for the virtual-key code of the |
64 | // given KeyboardEvent, ev. | |
65 | // | |
66 | // This function is meant as a polyfill for KeyboardEvent#key, | |
67 | // since it is not supported in IE 11 or Chrome for Android. We also test for | |
68 | // KeyboardEvent#keyCode because the handleShortcut handler is | |
69 | // also registered for the keydown event, because Blink doesn't fire | |
70 | // keypress on hitting the Escape key. | |
71 | // | |
72 | // So I guess you could say things are getting pretty interoperable. | |
73 | function getVirtualKey(ev) { | |
923072b8 | 74 | if ("key" in ev && typeof ev.key !== "undefined") { |
fc512014 XL |
75 | return ev.key; |
76 | } | |
77 | ||
04454e1e | 78 | const c = ev.charCode || ev.keyCode; |
923072b8 | 79 | if (c === 27) { |
fc512014 XL |
80 | return "Escape"; |
81 | } | |
82 | return String.fromCharCode(c); | |
83 | } | |
84 | ||
04454e1e FG |
85 | const MAIN_ID = "main-content"; |
86 | const SETTINGS_BUTTON_ID = "settings-menu"; | |
87 | const ALTERNATIVE_DISPLAY_ID = "alternative-display"; | |
88 | const NOT_DISPLAYED_ID = "not-displayed"; | |
064997fb | 89 | const HELP_BUTTON_ID = "help-button"; |
e1599b0c | 90 | |
04454e1e FG |
91 | function getSettingsButton() { |
92 | return document.getElementById(SETTINGS_BUTTON_ID); | |
93 | } | |
94 | ||
064997fb FG |
95 | function getHelpButton() { |
96 | return document.getElementById(HELP_BUTTON_ID); | |
97 | } | |
98 | ||
5869c6ff XL |
99 | // Returns the current URL without any query parameter or hash. |
100 | function getNakedUrl() { | |
101 | return window.location.href.split("?")[0].split("#")[0]; | |
102 | } | |
103 | ||
04454e1e FG |
104 | /** |
105 | * This function inserts `newNode` after `referenceNode`. It doesn't work if `referenceNode` | |
106 | * doesn't have a parent node. | |
107 | * | |
108 | * @param {HTMLElement} newNode | |
109 | * @param {HTMLElement} referenceNode | |
110 | */ | |
111 | function insertAfter(newNode, referenceNode) { | |
112 | referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); | |
113 | } | |
114 | ||
115 | /** | |
116 | * This function creates a new `<section>` with the given `id` and `classes` if it doesn't already | |
117 | * exist. | |
118 | * | |
119 | * More information about this in `switchDisplayedElement` documentation. | |
120 | * | |
121 | * @param {string} id | |
122 | * @param {string} classes | |
123 | */ | |
124 | function getOrCreateSection(id, classes) { | |
125 | let el = document.getElementById(id); | |
126 | ||
127 | if (!el) { | |
128 | el = document.createElement("section"); | |
129 | el.id = id; | |
130 | el.className = classes; | |
131 | insertAfter(el, document.getElementById(MAIN_ID)); | |
132 | } | |
133 | return el; | |
134 | } | |
135 | ||
136 | /** | |
137 | * Returns the `<section>` element which contains the displayed element. | |
138 | * | |
139 | * @return {HTMLElement} | |
140 | */ | |
141 | function getAlternativeDisplayElem() { | |
142 | return getOrCreateSection(ALTERNATIVE_DISPLAY_ID, "content hidden"); | |
143 | } | |
144 | ||
145 | /** | |
146 | * Returns the `<section>` element which contains the not-displayed elements. | |
147 | * | |
148 | * @return {HTMLElement} | |
149 | */ | |
150 | function getNotDisplayedElem() { | |
151 | return getOrCreateSection(NOT_DISPLAYED_ID, "hidden"); | |
152 | } | |
153 | ||
154 | /** | |
155 | * To nicely switch between displayed "extra" elements (such as search results or settings menu) | |
156 | * and to alternate between the displayed and not displayed elements, we hold them in two different | |
157 | * `<section>` elements. They work in pair: one holds the hidden elements while the other | |
158 | * contains the displayed element (there can be only one at the same time!). So basically, we switch | |
159 | * elements between the two `<section>` elements. | |
160 | * | |
161 | * @param {HTMLElement} elemToDisplay | |
162 | */ | |
163 | function switchDisplayedElement(elemToDisplay) { | |
164 | const el = getAlternativeDisplayElem(); | |
165 | ||
166 | if (el.children.length > 0) { | |
167 | getNotDisplayedElem().appendChild(el.firstElementChild); | |
168 | } | |
169 | if (elemToDisplay === null) { | |
170 | addClass(el, "hidden"); | |
171 | showMain(); | |
172 | return; | |
173 | } | |
174 | el.appendChild(elemToDisplay); | |
175 | hideMain(); | |
176 | removeClass(el, "hidden"); | |
177 | } | |
178 | ||
179 | function browserSupportsHistoryApi() { | |
180 | return window.history && typeof window.history.pushState === "function"; | |
181 | } | |
182 | ||
487cf647 | 183 | function loadCss(cssUrl) { |
04454e1e | 184 | const link = document.createElement("link"); |
487cf647 | 185 | link.href = cssUrl; |
04454e1e FG |
186 | link.rel = "stylesheet"; |
187 | document.getElementsByTagName("head")[0].appendChild(link); | |
188 | } | |
189 | ||
1a4d82fc | 190 | (function() { |
2b03887a FG |
191 | const isHelpPage = window.location.pathname.endsWith("/help.html"); |
192 | ||
04454e1e FG |
193 | function loadScript(url) { |
194 | const script = document.createElement("script"); | |
195 | script.src = url; | |
196 | document.head.append(script); | |
197 | } | |
198 | ||
199 | getSettingsButton().onclick = event => { | |
2b03887a FG |
200 | if (event.ctrlKey || event.altKey || event.metaKey) { |
201 | return; | |
202 | } | |
487cf647 | 203 | window.hideAllModals(false); |
04454e1e FG |
204 | addClass(getSettingsButton(), "rotate"); |
205 | event.preventDefault(); | |
206 | // Sending request for the CSS and the JS files at the same time so it will | |
207 | // hopefully be loaded when the JS will generate the settings content. | |
487cf647 FG |
208 | loadCss(getVar("static-root-path") + getVar("settings-css")); |
209 | loadScript(getVar("static-root-path") + getVar("settings-js")); | |
04454e1e | 210 | }; |
1a4d82fc | 211 | |
cdc7bbd5 | 212 | window.searchState = { |
17df50a5 XL |
213 | loadingText: "Loading search results...", |
214 | input: document.getElementsByClassName("search-input")[0], | |
04454e1e FG |
215 | outputElement: () => { |
216 | let el = document.getElementById("search"); | |
217 | if (!el) { | |
218 | el = document.createElement("section"); | |
219 | el.id = "search"; | |
220 | getNotDisplayedElem().appendChild(el); | |
221 | } | |
222 | return el; | |
17df50a5 XL |
223 | }, |
224 | title: document.title, | |
225 | titleBeforeSearch: document.title, | |
226 | timeout: null, | |
227 | // On the search screen, so you remain on the last tab you opened. | |
228 | // | |
229 | // 0 for "In Names" | |
230 | // 1 for "In Parameters" | |
231 | // 2 for "In Return Types" | |
232 | currentTab: 0, | |
233 | // tab and back preserves the element that was focused. | |
234 | focusedByTab: [null, null, null], | |
04454e1e | 235 | clearInputTimeout: () => { |
17df50a5 XL |
236 | if (searchState.timeout !== null) { |
237 | clearTimeout(searchState.timeout); | |
238 | searchState.timeout = null; | |
239 | } | |
240 | }, | |
04454e1e | 241 | isDisplayed: () => searchState.outputElement().parentElement.id === ALTERNATIVE_DISPLAY_ID, |
17df50a5 | 242 | // Sets the focus on the search bar at the top of the page |
04454e1e | 243 | focus: () => { |
17df50a5 XL |
244 | searchState.input.focus(); |
245 | }, | |
246 | // Removes the focus from the search bar. | |
04454e1e | 247 | defocus: () => { |
17df50a5 XL |
248 | searchState.input.blur(); |
249 | }, | |
04454e1e FG |
250 | showResults: search => { |
251 | if (search === null || typeof search === "undefined") { | |
17df50a5 | 252 | search = searchState.outputElement(); |
cdc7bbd5 | 253 | } |
04454e1e | 254 | switchDisplayedElement(search); |
17df50a5 | 255 | searchState.mouseMovedAfterSearch = false; |
cdc7bbd5 | 256 | document.title = searchState.title; |
17df50a5 | 257 | }, |
04454e1e FG |
258 | hideResults: () => { |
259 | switchDisplayedElement(null); | |
17df50a5 XL |
260 | document.title = searchState.titleBeforeSearch; |
261 | // We also remove the query parameter from the URL. | |
04454e1e | 262 | if (browserSupportsHistoryApi()) { |
5099ac24 | 263 | history.replaceState(null, window.currentCrate + " - Rust", |
17df50a5 XL |
264 | getNakedUrl() + window.location.hash); |
265 | } | |
266 | }, | |
04454e1e FG |
267 | getQueryStringParams: () => { |
268 | const params = {}; | |
17df50a5 | 269 | window.location.search.substring(1).split("&"). |
04454e1e FG |
270 | map(s => { |
271 | const pair = s.split("="); | |
17df50a5 XL |
272 | params[decodeURIComponent(pair[0])] = |
273 | typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]); | |
274 | }); | |
275 | return params; | |
276 | }, | |
04454e1e FG |
277 | setup: () => { |
278 | const search_input = searchState.input; | |
17df50a5 XL |
279 | if (!searchState.input) { |
280 | return; | |
281 | } | |
04454e1e | 282 | let searchLoaded = false; |
17df50a5 XL |
283 | function loadSearch() { |
284 | if (!searchLoaded) { | |
285 | searchLoaded = true; | |
487cf647 | 286 | loadScript(getVar("static-root-path") + getVar("search-js")); |
923072b8 | 287 | loadScript(resourcePath("search-index", ".js")); |
17df50a5 XL |
288 | } |
289 | } | |
c34b1796 | 290 | |
04454e1e | 291 | search_input.addEventListener("focus", () => { |
5099ac24 | 292 | search_input.origPlaceholder = search_input.placeholder; |
17df50a5 XL |
293 | search_input.placeholder = "Type your search here."; |
294 | loadSearch(); | |
295 | }); | |
f035d41b | 296 | |
04454e1e | 297 | if (search_input.value !== "") { |
3c0e092e XL |
298 | loadSearch(); |
299 | } | |
17df50a5 | 300 | |
04454e1e | 301 | const params = searchState.getQueryStringParams(); |
17df50a5 | 302 | if (params.search !== undefined) { |
487cf647 | 303 | searchState.setLoadingSearch(); |
17df50a5 XL |
304 | loadSearch(); |
305 | } | |
306 | }, | |
487cf647 FG |
307 | setLoadingSearch: () => { |
308 | const search = searchState.outputElement(); | |
309 | search.innerHTML = "<h3 class=\"search-loading\">" + searchState.loadingText + "</h3>"; | |
310 | searchState.showResults(search); | |
311 | }, | |
cdc7bbd5 | 312 | }; |
f9f354fc | 313 | |
83c7162d | 314 | function getPageId() { |
f035d41b | 315 | if (window.location.hash) { |
04454e1e | 316 | const tmp = window.location.hash.replace(/^#/, ""); |
f035d41b XL |
317 | if (tmp.length > 0) { |
318 | return tmp; | |
319 | } | |
83c7162d XL |
320 | } |
321 | return null; | |
322 | } | |
323 | ||
04454e1e FG |
324 | const toggleAllDocsId = "toggle-all-docs"; |
325 | let savedHash = ""; | |
0731742a | 326 | |
60c5eb7d | 327 | function handleHashes(ev) { |
04454e1e | 328 | if (ev !== null && searchState.isDisplayed() && ev.newURL) { |
60c5eb7d XL |
329 | // This block occurs when clicking on an element in the navbar while |
330 | // in a search. | |
04454e1e FG |
331 | switchDisplayedElement(null); |
332 | const hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1); | |
333 | if (browserSupportsHistoryApi()) { | |
5869c6ff | 334 | // `window.location.search`` contains all the query parameters, not just `search`. |
5099ac24 | 335 | history.replaceState(null, "", |
5869c6ff | 336 | getNakedUrl() + window.location.search + "#" + hash); |
dc9dc135 | 337 | } |
04454e1e | 338 | const elem = document.getElementById(hash); |
60c5eb7d XL |
339 | if (elem) { |
340 | elem.scrollIntoView(); | |
dc9dc135 | 341 | } |
60c5eb7d XL |
342 | } |
343 | // This part is used in case an element is not visible. | |
344 | if (savedHash !== window.location.hash) { | |
345 | savedHash = window.location.hash; | |
346 | if (savedHash.length === 0) { | |
1a4d82fc JJ |
347 | return; |
348 | } | |
17df50a5 | 349 | expandSection(savedHash.slice(1)); // we remove the '#' |
60c5eb7d XL |
350 | } |
351 | } | |
352 | ||
353 | function onHashChange(ev) { | |
354 | // If we're in mobile mode, we should hide the sidebar in any case. | |
f2b60f7d | 355 | hideSidebar(); |
60c5eb7d | 356 | handleHashes(ev); |
1a4d82fc | 357 | } |
b7449926 | 358 | |
cdc7bbd5 XL |
359 | function openParentDetails(elem) { |
360 | while (elem) { | |
361 | if (elem.tagName === "DETAILS") { | |
362 | elem.open = true; | |
363 | } | |
364 | elem = elem.parentNode; | |
365 | } | |
366 | } | |
367 | ||
b7449926 | 368 | function expandSection(id) { |
17df50a5 | 369 | openParentDetails(document.getElementById(id)); |
b7449926 XL |
370 | } |
371 | ||
e1599b0c | 372 | function handleEscape(ev) { |
04454e1e | 373 | searchState.clearInputTimeout(); |
064997fb FG |
374 | switchDisplayedElement(null); |
375 | if (browserSupportsHistoryApi()) { | |
376 | history.replaceState(null, window.currentCrate + " - Rust", | |
377 | getNakedUrl() + window.location.hash); | |
0531ce1d | 378 | } |
064997fb | 379 | ev.preventDefault(); |
cdc7bbd5 | 380 | searchState.defocus(); |
9ffffee4 | 381 | window.hideAllModals(true); // true = reset focus for tooltips |
0531ce1d | 382 | } |
1a4d82fc | 383 | |
0531ce1d | 384 | function handleShortcut(ev) { |
92a42be0 | 385 | // Don't interfere with browser shortcuts |
064997fb | 386 | const disableShortcuts = getSettingValue("disable-shortcuts") === "true"; |
17df50a5 | 387 | if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts) { |
92a42be0 | 388 | return; |
0531ce1d | 389 | } |
92a42be0 | 390 | |
064997fb | 391 | if (document.activeElement.tagName === "INPUT" && |
9c376795 FG |
392 | document.activeElement.type !== "checkbox" && |
393 | document.activeElement.type !== "radio") { | |
0531ce1d XL |
394 | switch (getVirtualKey(ev)) { |
395 | case "Escape": | |
e1599b0c | 396 | handleEscape(ev); |
0531ce1d | 397 | break; |
9346a6ac | 398 | } |
0531ce1d XL |
399 | } else { |
400 | switch (getVirtualKey(ev)) { | |
401 | case "Escape": | |
e1599b0c | 402 | handleEscape(ev); |
0531ce1d | 403 | break; |
c1a9b12d | 404 | |
0531ce1d XL |
405 | case "s": |
406 | case "S": | |
0531ce1d | 407 | ev.preventDefault(); |
cdc7bbd5 | 408 | searchState.focus(); |
0531ce1d | 409 | break; |
c1a9b12d | 410 | |
0531ce1d | 411 | case "+": |
2b03887a FG |
412 | ev.preventDefault(); |
413 | expandAllDocs(); | |
414 | break; | |
0531ce1d XL |
415 | case "-": |
416 | ev.preventDefault(); | |
2b03887a | 417 | collapseAllDocs(); |
0531ce1d | 418 | break; |
a7813a04 | 419 | |
0531ce1d | 420 | case "?": |
064997fb | 421 | showHelp(); |
0531ce1d | 422 | break; |
29967ef6 | 423 | |
29967ef6 | 424 | default: |
923072b8 | 425 | break; |
1a4d82fc | 426 | } |
83c7162d | 427 | } |
cdc7bbd5 | 428 | } |
83c7162d | 429 | |
cdc7bbd5 XL |
430 | document.addEventListener("keypress", handleShortcut); |
431 | document.addEventListener("keydown", handleShortcut); | |
1a4d82fc | 432 | |
923072b8 FG |
433 | function addSidebarItems() { |
434 | if (!window.SIDEBAR_ITEMS) { | |
435 | return; | |
17df50a5 | 436 | } |
923072b8 | 437 | const sidebar = document.getElementsByClassName("sidebar-elems")[0]; |
17df50a5 | 438 | |
5099ac24 FG |
439 | /** |
440 | * Append to the sidebar a "block" of links - a heading along with a list (`<ul>`) of items. | |
441 | * | |
442 | * @param {string} shortty - A short type name, like "primitive", "mod", or "macro" | |
443 | * @param {string} id - The HTML id of the corresponding section on the module page. | |
444 | * @param {string} longty - A long, capitalized, plural name, like "Primitive Types", | |
445 | * "Modules", or "Macros". | |
446 | */ | |
447 | function block(shortty, id, longty) { | |
923072b8 | 448 | const filtered = window.SIDEBAR_ITEMS[shortty]; |
0731742a XL |
449 | if (!filtered) { |
450 | return; | |
451 | } | |
c34b1796 | 452 | |
04454e1e | 453 | const h3 = document.createElement("h3"); |
5099ac24 | 454 | h3.innerHTML = `<a href="index.html#${id}">${longty}</a>`; |
04454e1e | 455 | const ul = document.createElement("ul"); |
2b03887a | 456 | ul.className = "block " + shortty; |
c34b1796 | 457 | |
9ffffee4 | 458 | for (const name of filtered) { |
04454e1e | 459 | let path; |
0731742a XL |
460 | if (shortty === "mod") { |
461 | path = name + "/index.html"; | |
c34b1796 | 462 | } else { |
0731742a | 463 | path = shortty + "." + name + ".html"; |
c34b1796 | 464 | } |
923072b8 | 465 | const current_page = document.location.href.split("/").pop(); |
04454e1e | 466 | const link = document.createElement("a"); |
923072b8 | 467 | link.href = path; |
2b03887a FG |
468 | if (path === current_page) { |
469 | link.className = "current"; | |
470 | } | |
7cac9316 | 471 | link.textContent = name; |
04454e1e | 472 | const li = document.createElement("li"); |
7cac9316 XL |
473 | li.appendChild(link); |
474 | ul.appendChild(li); | |
c34b1796 | 475 | } |
2b03887a FG |
476 | sidebar.appendChild(h3); |
477 | sidebar.appendChild(ul); | |
c34b1796 AL |
478 | } |
479 | ||
17df50a5 | 480 | if (sidebar) { |
064997fb FG |
481 | block("primitive", "primitives", "Primitive Types"); |
482 | block("mod", "modules", "Modules"); | |
483 | block("macro", "macros", "Macros"); | |
484 | block("struct", "structs", "Structs"); | |
485 | block("enum", "enums", "Enums"); | |
486 | block("union", "unions", "Unions"); | |
487 | block("constant", "constants", "Constants"); | |
488 | block("static", "static", "Statics"); | |
489 | block("trait", "traits", "Traits"); | |
490 | block("fn", "functions", "Functions"); | |
491 | block("type", "types", "Type Definitions"); | |
492 | block("foreigntype", "foreign-types", "Foreign Types"); | |
493 | block("keyword", "keywords", "Keywords"); | |
494 | block("traitalias", "trait-aliases", "Trait Aliases"); | |
17df50a5 | 495 | } |
923072b8 | 496 | } |
c34b1796 | 497 | |
04454e1e FG |
498 | window.register_implementors = imp => { |
499 | const implementors = document.getElementById("implementors-list"); | |
500 | const synthetic_implementors = document.getElementById("synthetic-implementors-list"); | |
501 | const inlined_types = new Set(); | |
0531ce1d | 502 | |
f2b60f7d FG |
503 | const TEXT_IDX = 0; |
504 | const SYNTHETIC_IDX = 1; | |
505 | const TYPES_IDX = 2; | |
506 | ||
dfeec247 XL |
507 | if (synthetic_implementors) { |
508 | // This `inlined_types` variable is used to avoid having the same implementation | |
509 | // showing up twice. For example "String" in the "Sync" doc page. | |
510 | // | |
511 | // By the way, this is only used by and useful for traits implemented automatically | |
512 | // (like "Send" and "Sync"). | |
04454e1e FG |
513 | onEachLazy(synthetic_implementors.getElementsByClassName("impl"), el => { |
514 | const aliases = el.getAttribute("data-aliases"); | |
dfeec247 XL |
515 | if (!aliases) { |
516 | return; | |
517 | } | |
04454e1e | 518 | aliases.split(",").forEach(alias => { |
dfeec247 XL |
519 | inlined_types.add(alias); |
520 | }); | |
521 | }); | |
522 | } | |
523 | ||
04454e1e | 524 | let currentNbImpls = implementors.getElementsByClassName("impl").length; |
9c376795 | 525 | const traitName = document.querySelector(".main-heading h1 > .trait").textContent; |
04454e1e FG |
526 | const baseIdName = "impl-" + traitName + "-"; |
527 | const libs = Object.getOwnPropertyNames(imp); | |
528 | // We don't want to include impls from this JS file, when the HTML already has them. | |
529 | // The current crate should always be ignored. Other crates that should also be | |
530 | // ignored are included in the attribute `data-ignore-extern-crates`. | |
f2b60f7d FG |
531 | const script = document |
532 | .querySelector("script[data-ignore-extern-crates]"); | |
533 | const ignoreExternCrates = script ? script.getAttribute("data-ignore-extern-crates") : ""; | |
04454e1e FG |
534 | for (const lib of libs) { |
535 | if (lib === window.currentCrate || ignoreExternCrates.indexOf(lib) !== -1) { | |
536 | continue; | |
537 | } | |
538 | const structs = imp[lib]; | |
0531ce1d XL |
539 | |
540 | struct_loop: | |
04454e1e | 541 | for (const struct of structs) { |
f2b60f7d | 542 | const list = struct[SYNTHETIC_IDX] ? synthetic_implementors : implementors; |
0531ce1d | 543 | |
f2b60f7d FG |
544 | // The types list is only used for synthetic impls. |
545 | // If this changes, `main.js` and `write_shared.rs` both need changed. | |
546 | if (struct[SYNTHETIC_IDX]) { | |
547 | for (const struct_type of struct[TYPES_IDX]) { | |
04454e1e | 548 | if (inlined_types.has(struct_type)) { |
0531ce1d XL |
549 | continue struct_loop; |
550 | } | |
04454e1e | 551 | inlined_types.add(struct_type); |
0531ce1d XL |
552 | } |
553 | } | |
554 | ||
04454e1e | 555 | const code = document.createElement("h3"); |
f2b60f7d | 556 | code.innerHTML = struct[TEXT_IDX]; |
17df50a5 | 557 | addClass(code, "code-header"); |
7cac9316 | 558 | |
04454e1e FG |
559 | onEachLazy(code.getElementsByTagName("a"), elem => { |
560 | const href = elem.getAttribute("href"); | |
5869c6ff | 561 | |
9c376795 | 562 | if (href && !/^(?:[a-z+]+:)?\/\//.test(href)) { |
5869c6ff | 563 | elem.setAttribute("href", window.rootPath + href); |
1a4d82fc | 564 | } |
5869c6ff XL |
565 | }); |
566 | ||
04454e1e FG |
567 | const currentId = baseIdName + currentNbImpls; |
568 | const anchor = document.createElement("a"); | |
136023e0 XL |
569 | anchor.href = "#" + currentId; |
570 | addClass(anchor, "anchor"); | |
571 | ||
04454e1e | 572 | const display = document.createElement("div"); |
136023e0 | 573 | display.id = currentId; |
b7449926 | 574 | addClass(display, "impl"); |
136023e0 XL |
575 | display.appendChild(anchor); |
576 | display.appendChild(code); | |
b7449926 | 577 | list.appendChild(display); |
136023e0 | 578 | currentNbImpls += 1; |
1a4d82fc JJ |
579 | } |
580 | } | |
581 | }; | |
582 | if (window.pending_implementors) { | |
583 | window.register_implementors(window.pending_implementors); | |
584 | } | |
585 | ||
923072b8 FG |
586 | function addSidebarCrates() { |
587 | if (!window.ALL_CRATES) { | |
588 | return; | |
589 | } | |
590 | const sidebarElems = document.getElementsByClassName("sidebar-elems")[0]; | |
591 | if (!sidebarElems) { | |
592 | return; | |
593 | } | |
594 | // Draw a convenient sidebar of known crates if we have a listing | |
2b03887a FG |
595 | const h3 = document.createElement("h3"); |
596 | h3.innerHTML = "Crates"; | |
923072b8 | 597 | const ul = document.createElement("ul"); |
2b03887a | 598 | ul.className = "block crate"; |
923072b8 FG |
599 | |
600 | for (const crate of window.ALL_CRATES) { | |
923072b8 FG |
601 | const link = document.createElement("a"); |
602 | link.href = window.rootPath + crate + "/index.html"; | |
2b03887a FG |
603 | if (window.rootPath !== "./" && crate === window.currentCrate) { |
604 | link.className = "current"; | |
605 | } | |
923072b8 FG |
606 | link.textContent = crate; |
607 | ||
608 | const li = document.createElement("li"); | |
609 | li.appendChild(link); | |
610 | ul.appendChild(li); | |
611 | } | |
2b03887a FG |
612 | sidebarElems.appendChild(h3); |
613 | sidebarElems.appendChild(ul); | |
923072b8 FG |
614 | } |
615 | ||
2b03887a FG |
616 | function expandAllDocs() { |
617 | const innerToggle = document.getElementById(toggleAllDocsId); | |
618 | removeClass(innerToggle, "will-expand"); | |
9c376795 | 619 | onEachLazy(document.getElementsByClassName("toggle"), e => { |
487cf647 | 620 | if (!hasClass(e, "type-contents-toggle") && !hasClass(e, "more-examples-toggle")) { |
2b03887a FG |
621 | e.open = true; |
622 | } | |
623 | }); | |
624 | innerToggle.title = "collapse all docs"; | |
625 | innerToggle.children[0].innerText = "\u2212"; // "\u2212" is "−" minus sign | |
626 | } | |
923072b8 | 627 | |
2b03887a FG |
628 | function collapseAllDocs() { |
629 | const innerToggle = document.getElementById(toggleAllDocsId); | |
630 | addClass(innerToggle, "will-expand"); | |
9c376795 | 631 | onEachLazy(document.getElementsByClassName("toggle"), e => { |
2b03887a FG |
632 | if (e.parentNode.id !== "implementations-list" || |
633 | (!hasClass(e, "implementors-toggle") && | |
634 | !hasClass(e, "type-contents-toggle")) | |
635 | ) { | |
636 | e.open = false; | |
637 | } | |
638 | }); | |
639 | innerToggle.title = "expand all docs"; | |
640 | innerToggle.children[0].innerText = "+"; | |
d9579d0f | 641 | } |
1a4d82fc | 642 | |
17df50a5 | 643 | function toggleAllDocs() { |
04454e1e | 644 | const innerToggle = document.getElementById(toggleAllDocsId); |
0731742a | 645 | if (!innerToggle) { |
83c7162d XL |
646 | return; |
647 | } | |
0731742a | 648 | if (hasClass(innerToggle, "will-expand")) { |
2b03887a | 649 | expandAllDocs(); |
d9579d0f | 650 | } else { |
2b03887a | 651 | collapseAllDocs(); |
0531ce1d XL |
652 | } |
653 | } | |
654 | ||
f9f354fc | 655 | (function() { |
04454e1e | 656 | const toggles = document.getElementById(toggleAllDocsId); |
1b1a35ee XL |
657 | if (toggles) { |
658 | toggles.onclick = toggleAllDocs; | |
659 | } | |
660 | ||
04454e1e FG |
661 | const hideMethodDocs = getSettingValue("auto-hide-method-docs") === "true"; |
662 | const hideImplementations = getSettingValue("auto-hide-trait-implementations") === "true"; | |
663 | const hideLargeItemContents = getSettingValue("auto-hide-large-items") !== "false"; | |
f9f354fc | 664 | |
136023e0 | 665 | function setImplementorsTogglesOpen(id, open) { |
04454e1e | 666 | const list = document.getElementById(id); |
17df50a5 | 667 | if (list !== null) { |
04454e1e | 668 | onEachLazy(list.getElementsByClassName("implementors-toggle"), e => { |
136023e0 | 669 | e.open = open; |
17df50a5 | 670 | }); |
cdc7bbd5 | 671 | } |
60c5eb7d | 672 | } |
17df50a5 | 673 | |
136023e0 XL |
674 | if (hideImplementations) { |
675 | setImplementorsTogglesOpen("trait-implementations-list", false); | |
676 | setImplementorsTogglesOpen("blanket-implementations-list", false); | |
7cac9316 | 677 | } |
cdc7bbd5 | 678 | |
9c376795 | 679 | onEachLazy(document.getElementsByClassName("toggle"), e => { |
17df50a5 XL |
680 | if (!hideLargeItemContents && hasClass(e, "type-contents-toggle")) { |
681 | e.open = true; | |
f9f354fc | 682 | } |
17df50a5 XL |
683 | if (hideMethodDocs && hasClass(e, "method-toggle")) { |
684 | e.open = false; | |
532ac7d7 | 685 | } |
a1dfa0c6 | 686 | |
17df50a5 | 687 | }); |
f035d41b | 688 | |
04454e1e | 689 | const pageId = getPageId(); |
f035d41b XL |
690 | if (pageId !== null) { |
691 | expandSection(pageId); | |
692 | } | |
f9f354fc | 693 | }()); |
7cac9316 | 694 | |
2b03887a FG |
695 | window.rustdoc_add_line_numbers_to_examples = () => { |
696 | onEachLazy(document.getElementsByClassName("rust-example-rendered"), x => { | |
697 | const parent = x.parentNode; | |
698 | const line_numbers = parent.querySelectorAll(".example-line-numbers"); | |
699 | if (line_numbers.length > 0) { | |
700 | return; | |
701 | } | |
702 | const count = x.textContent.split("\n").length; | |
703 | const elems = []; | |
704 | for (let i = 0; i < count; ++i) { | |
705 | elems.push(i + 1); | |
706 | } | |
707 | const node = document.createElement("pre"); | |
708 | addClass(node, "example-line-numbers"); | |
709 | node.innerHTML = elems.join("\n"); | |
710 | parent.insertBefore(node, x); | |
711 | }); | |
712 | }; | |
713 | ||
714 | window.rustdoc_remove_line_numbers_from_examples = () => { | |
715 | onEachLazy(document.getElementsByClassName("rust-example-rendered"), x => { | |
716 | const parent = x.parentNode; | |
717 | const line_numbers = parent.querySelectorAll(".example-line-numbers"); | |
718 | for (const node of line_numbers) { | |
719 | parent.removeChild(node); | |
720 | } | |
721 | }); | |
722 | }; | |
723 | ||
487cf647 FG |
724 | if (getSettingValue("line-numbers") === "true") { |
725 | window.rustdoc_add_line_numbers_to_examples(); | |
726 | } | |
abe05a73 | 727 | |
f2b60f7d FG |
728 | let oldSidebarScrollPosition = null; |
729 | ||
2b03887a FG |
730 | // Scroll locking used both here and in source-script.js |
731 | ||
732 | window.rustdocMobileScrollLock = function() { | |
733 | const mobile_topbar = document.querySelector(".mobile-topbar"); | |
734 | if (window.innerWidth <= window.RUSTDOC_MOBILE_BREAKPOINT) { | |
f2b60f7d FG |
735 | // This is to keep the scroll position on mobile. |
736 | oldSidebarScrollPosition = window.scrollY; | |
737 | document.body.style.width = `${document.body.offsetWidth}px`; | |
738 | document.body.style.position = "fixed"; | |
739 | document.body.style.top = `-${oldSidebarScrollPosition}px`; | |
2b03887a FG |
740 | if (mobile_topbar) { |
741 | mobile_topbar.style.top = `${oldSidebarScrollPosition}px`; | |
742 | mobile_topbar.style.position = "relative"; | |
743 | } | |
f2b60f7d FG |
744 | } else { |
745 | oldSidebarScrollPosition = null; | |
746 | } | |
2b03887a | 747 | }; |
f2b60f7d | 748 | |
2b03887a FG |
749 | window.rustdocMobileScrollUnlock = function() { |
750 | const mobile_topbar = document.querySelector(".mobile-topbar"); | |
f2b60f7d FG |
751 | if (oldSidebarScrollPosition !== null) { |
752 | // This is to keep the scroll position on mobile. | |
753 | document.body.style.width = ""; | |
754 | document.body.style.position = ""; | |
755 | document.body.style.top = ""; | |
2b03887a FG |
756 | if (mobile_topbar) { |
757 | mobile_topbar.style.top = ""; | |
758 | mobile_topbar.style.position = ""; | |
759 | } | |
f2b60f7d FG |
760 | // The scroll position is lost when resetting the style, hence why we store it in |
761 | // `oldSidebarScrollPosition`. | |
762 | window.scrollTo(0, oldSidebarScrollPosition); | |
763 | oldSidebarScrollPosition = null; | |
764 | } | |
2b03887a FG |
765 | }; |
766 | ||
767 | function showSidebar() { | |
487cf647 | 768 | window.hideAllModals(false); |
2b03887a FG |
769 | window.rustdocMobileScrollLock(); |
770 | const sidebar = document.getElementsByClassName("sidebar")[0]; | |
771 | addClass(sidebar, "shown"); | |
772 | } | |
773 | ||
774 | function hideSidebar() { | |
775 | window.rustdocMobileScrollUnlock(); | |
04454e1e | 776 | const sidebar = document.getElementsByClassName("sidebar")[0]; |
5099ac24 FG |
777 | removeClass(sidebar, "shown"); |
778 | } | |
779 | ||
f2b60f7d | 780 | window.addEventListener("resize", () => { |
2b03887a | 781 | if (window.innerWidth > window.RUSTDOC_MOBILE_BREAKPOINT && |
f2b60f7d FG |
782 | oldSidebarScrollPosition !== null) { |
783 | // If the user opens the sidebar in "mobile" mode, and then grows the browser window, | |
784 | // we need to switch away from mobile mode and make the main content area scrollable. | |
785 | hideSidebar(); | |
786 | } | |
9ffffee4 FG |
787 | if (window.CURRENT_TOOLTIP_ELEMENT) { |
788 | // As a workaround to the behavior of `contains: layout` used in doc togglers, | |
789 | // tooltip popovers are positioned using javascript. | |
487cf647 FG |
790 | // |
791 | // This means when the window is resized, we need to redo the layout. | |
9ffffee4 FG |
792 | const base = window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE; |
793 | const force_visible = base.TOOLTIP_FORCE_VISIBLE; | |
794 | hideTooltip(false); | |
487cf647 | 795 | if (force_visible) { |
9ffffee4 FG |
796 | showTooltip(base); |
797 | base.TOOLTIP_FORCE_VISIBLE = true; | |
487cf647 FG |
798 | } |
799 | } | |
f2b60f7d FG |
800 | }); |
801 | ||
9c376795 FG |
802 | const mainElem = document.getElementById(MAIN_ID); |
803 | if (mainElem) { | |
804 | mainElem.addEventListener("click", hideSidebar); | |
17df50a5 | 805 | } |
17df50a5 | 806 | |
9c376795 | 807 | onEachLazy(document.querySelectorAll("a[href^='#']"), el => { |
17df50a5 XL |
808 | // For clicks on internal links (<A> tags with a hash property), we expand the section we're |
809 | // jumping to *before* jumping there. We can't do this in onHashChange, because it changes | |
810 | // the height of the document so we wind up scrolled to the wrong place. | |
9c376795 FG |
811 | el.addEventListener("click", () => { |
812 | expandSection(el.hash.slice(1)); | |
813 | hideSidebar(); | |
814 | }); | |
17df50a5 XL |
815 | }); |
816 | ||
9c376795 | 817 | onEachLazy(document.querySelectorAll(".toggle > summary:not(.hideme)"), el => { |
04454e1e FG |
818 | el.addEventListener("click", e => { |
819 | if (e.target.tagName !== "SUMMARY" && e.target.tagName !== "A") { | |
3c0e092e XL |
820 | e.preventDefault(); |
821 | } | |
822 | }); | |
823 | }); | |
824 | ||
9ffffee4 FG |
825 | function showTooltip(e) { |
826 | const notable_ty = e.getAttribute("data-notable-ty"); | |
827 | if (!window.NOTABLE_TRAITS && notable_ty) { | |
487cf647 FG |
828 | const data = document.getElementById("notable-traits-data"); |
829 | if (data) { | |
830 | window.NOTABLE_TRAITS = JSON.parse(data.innerText); | |
831 | } else { | |
9ffffee4 | 832 | throw new Error("showTooltip() called with notable without any notable traits!"); |
487cf647 FG |
833 | } |
834 | } | |
9ffffee4 | 835 | if (window.CURRENT_TOOLTIP_ELEMENT && window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE === e) { |
487cf647 FG |
836 | // Make this function idempotent. |
837 | return; | |
838 | } | |
839 | window.hideAllModals(false); | |
487cf647 | 840 | const wrapper = document.createElement("div"); |
9ffffee4 FG |
841 | if (notable_ty) { |
842 | wrapper.innerHTML = "<div class=\"content\">" + | |
843 | window.NOTABLE_TRAITS[notable_ty] + "</div>"; | |
844 | } else if (e.getAttribute("title") !== undefined) { | |
845 | const titleContent = document.createElement("div"); | |
846 | titleContent.className = "content"; | |
847 | titleContent.appendChild(document.createTextNode(e.getAttribute("title"))); | |
848 | wrapper.appendChild(titleContent); | |
849 | } | |
850 | wrapper.className = "tooltip popover"; | |
487cf647 FG |
851 | const focusCatcher = document.createElement("div"); |
852 | focusCatcher.setAttribute("tabindex", "0"); | |
9ffffee4 | 853 | focusCatcher.onfocus = hideTooltip; |
487cf647 FG |
854 | wrapper.appendChild(focusCatcher); |
855 | const pos = e.getBoundingClientRect(); | |
856 | // 5px overlap so that the mouse can easily travel from place to place | |
857 | wrapper.style.top = (pos.top + window.scrollY + pos.height) + "px"; | |
858 | wrapper.style.left = 0; | |
859 | wrapper.style.right = "auto"; | |
860 | wrapper.style.visibility = "hidden"; | |
861 | const body = document.getElementsByTagName("body")[0]; | |
862 | body.appendChild(wrapper); | |
863 | const wrapperPos = wrapper.getBoundingClientRect(); | |
864 | // offset so that the arrow points at the center of the "(i)" | |
865 | const finalPos = pos.left + window.scrollX - wrapperPos.width + 24; | |
866 | if (finalPos > 0) { | |
867 | wrapper.style.left = finalPos + "px"; | |
868 | } else { | |
869 | wrapper.style.setProperty( | |
870 | "--popover-arrow-offset", | |
871 | (wrapperPos.right - pos.right + 4) + "px" | |
872 | ); | |
873 | } | |
874 | wrapper.style.visibility = ""; | |
9ffffee4 FG |
875 | window.CURRENT_TOOLTIP_ELEMENT = wrapper; |
876 | window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE = e; | |
487cf647 FG |
877 | wrapper.onpointerleave = function(ev) { |
878 | // If this is a synthetic touch event, ignore it. A click event will be along shortly. | |
879 | if (ev.pointerType !== "mouse") { | |
880 | return; | |
881 | } | |
9ffffee4 FG |
882 | if (!e.TOOLTIP_FORCE_VISIBLE && !elemIsInParent(event.relatedTarget, e)) { |
883 | hideTooltip(true); | |
487cf647 FG |
884 | } |
885 | }; | |
886 | } | |
887 | ||
9ffffee4 FG |
888 | function tooltipBlurHandler(event) { |
889 | if (window.CURRENT_TOOLTIP_ELEMENT && | |
890 | !elemIsInParent(document.activeElement, window.CURRENT_TOOLTIP_ELEMENT) && | |
891 | !elemIsInParent(event.relatedTarget, window.CURRENT_TOOLTIP_ELEMENT) && | |
892 | !elemIsInParent(document.activeElement, window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE) && | |
893 | !elemIsInParent(event.relatedTarget, window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE) | |
487cf647 FG |
894 | ) { |
895 | // Work around a difference in the focus behaviour between Firefox, Chrome, and Safari. | |
9ffffee4 | 896 | // When I click the button on an already-opened tooltip popover, Safari |
487cf647 FG |
897 | // hides the popover and then immediately shows it again, while everyone else hides it |
898 | // and it stays hidden. | |
899 | // | |
900 | // To work around this, make sure the click finishes being dispatched before | |
9ffffee4 | 901 | // hiding the popover. Since `hideTooltip()` is idempotent, this makes Safari behave |
487cf647 | 902 | // consistently with the other two. |
9ffffee4 | 903 | setTimeout(() => hideTooltip(false), 0); |
487cf647 FG |
904 | } |
905 | } | |
906 | ||
9ffffee4 FG |
907 | function hideTooltip(focus) { |
908 | if (window.CURRENT_TOOLTIP_ELEMENT) { | |
909 | if (window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE) { | |
487cf647 | 910 | if (focus) { |
9ffffee4 | 911 | window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.focus(); |
487cf647 | 912 | } |
9ffffee4 | 913 | window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE = false; |
487cf647 FG |
914 | } |
915 | const body = document.getElementsByTagName("body")[0]; | |
9ffffee4 FG |
916 | body.removeChild(window.CURRENT_TOOLTIP_ELEMENT); |
917 | window.CURRENT_TOOLTIP_ELEMENT = null; | |
487cf647 FG |
918 | } |
919 | } | |
920 | ||
9ffffee4 | 921 | onEachLazy(document.getElementsByClassName("tooltip"), e => { |
3dfed10e | 922 | e.onclick = function() { |
9ffffee4 FG |
923 | this.TOOLTIP_FORCE_VISIBLE = this.TOOLTIP_FORCE_VISIBLE ? false : true; |
924 | if (window.CURRENT_TOOLTIP_ELEMENT && !this.TOOLTIP_FORCE_VISIBLE) { | |
925 | hideTooltip(true); | |
487cf647 | 926 | } else { |
9ffffee4 FG |
927 | showTooltip(this); |
928 | window.CURRENT_TOOLTIP_ELEMENT.setAttribute("tabindex", "0"); | |
929 | window.CURRENT_TOOLTIP_ELEMENT.focus(); | |
930 | window.CURRENT_TOOLTIP_ELEMENT.onblur = tooltipBlurHandler; | |
487cf647 FG |
931 | } |
932 | return false; | |
933 | }; | |
934 | e.onpointerenter = function(ev) { | |
935 | // If this is a synthetic touch event, ignore it. A click event will be along shortly. | |
936 | if (ev.pointerType !== "mouse") { | |
937 | return; | |
938 | } | |
9ffffee4 | 939 | showTooltip(this); |
487cf647 FG |
940 | }; |
941 | e.onpointerleave = function(ev) { | |
942 | // If this is a synthetic touch event, ignore it. A click event will be along shortly. | |
943 | if (ev.pointerType !== "mouse") { | |
944 | return; | |
945 | } | |
9ffffee4 FG |
946 | if (!this.TOOLTIP_FORCE_VISIBLE && |
947 | !elemIsInParent(ev.relatedTarget, window.CURRENT_TOOLTIP_ELEMENT)) { | |
948 | hideTooltip(true); | |
487cf647 | 949 | } |
3dfed10e XL |
950 | }; |
951 | }); | |
952 | ||
04454e1e | 953 | const sidebar_menu_toggle = document.getElementsByClassName("sidebar-menu-toggle")[0]; |
5099ac24 | 954 | if (sidebar_menu_toggle) { |
04454e1e FG |
955 | sidebar_menu_toggle.addEventListener("click", () => { |
956 | const sidebar = document.getElementsByClassName("sidebar")[0]; | |
5099ac24 | 957 | if (!hasClass(sidebar, "shown")) { |
f2b60f7d | 958 | showSidebar(); |
ff7c6d11 | 959 | } else { |
f2b60f7d | 960 | hideSidebar(); |
ff7c6d11 | 961 | } |
5099ac24 | 962 | }); |
ff7c6d11 XL |
963 | } |
964 | ||
064997fb FG |
965 | function helpBlurHandler(event) { |
966 | blurHandler(event, getHelpButton(), window.hidePopoverMenus); | |
967 | } | |
17df50a5 | 968 | |
064997fb | 969 | function buildHelpMenu() { |
04454e1e | 970 | const book_info = document.createElement("span"); |
c295e0f8 | 971 | book_info.className = "top"; |
29967ef6 XL |
972 | book_info.innerHTML = "You can find more information in \ |
973 | <a href=\"https://doc.rust-lang.org/rustdoc/\">the rustdoc book</a>."; | |
974 | ||
04454e1e | 975 | const shortcuts = [ |
e74abb32 XL |
976 | ["?", "Show this help dialog"], |
977 | ["S", "Focus the search field"], | |
978 | ["↑", "Move up in search results"], | |
979 | ["↓", "Move down in search results"], | |
17df50a5 | 980 | ["← / →", "Switch result tab (when results focused)"], |
e74abb32 XL |
981 | ["⏎", "Go to active search result"], |
982 | ["+", "Expand all sections"], | |
983 | ["-", "Collapse all sections"], | |
04454e1e FG |
984 | ].map(x => "<dt>" + |
985 | x[0].split(" ") | |
923072b8 | 986 | .map((y, index) => ((index & 1) === 0 ? "<kbd>" + y + "</kbd>" : " " + y + " ")) |
04454e1e FG |
987 | .join("") + "</dt><dd>" + x[1] + "</dd>").join(""); |
988 | const div_shortcuts = document.createElement("div"); | |
e74abb32 XL |
989 | addClass(div_shortcuts, "shortcuts"); |
990 | div_shortcuts.innerHTML = "<h2>Keyboard Shortcuts</h2><dl>" + shortcuts + "</dl></div>"; | |
991 | ||
04454e1e | 992 | const infos = [ |
e74abb32 | 993 | "Prefix searches with a type followed by a colon (e.g., <code>fn:</code>) to \ |
1b1a35ee XL |
994 | restrict the search to a given item kind.", |
995 | "Accepted kinds are: <code>fn</code>, <code>mod</code>, <code>struct</code>, \ | |
e74abb32 XL |
996 | <code>enum</code>, <code>trait</code>, <code>type</code>, <code>macro</code>, \ |
997 | and <code>const</code>.", | |
dfeec247 | 998 | "Search functions by type signature (e.g., <code>vec -> usize</code> or \ |
064997fb | 999 | <code>-> vec</code>)", |
e74abb32 XL |
1000 | "Search multiple things at once by splitting your query with comma (e.g., \ |
1001 | <code>str,u8</code> or <code>String,struct:Vec,test</code>)", | |
1002 | "You can look for items with an exact name by putting double quotes around \ | |
1003 | your request: <code>\"string\"</code>", | |
1004 | "Look for items inside another one by searching for a path: <code>vec::Vec</code>", | |
04454e1e FG |
1005 | ].map(x => "<p>" + x + "</p>").join(""); |
1006 | const div_infos = document.createElement("div"); | |
e74abb32 XL |
1007 | addClass(div_infos, "infos"); |
1008 | div_infos.innerHTML = "<h2>Search Tricks</h2>" + infos; | |
1009 | ||
04454e1e | 1010 | const rustdoc_version = document.createElement("span"); |
c295e0f8 | 1011 | rustdoc_version.className = "bottom"; |
04454e1e | 1012 | const rustdoc_version_code = document.createElement("code"); |
a2a8927a | 1013 | rustdoc_version_code.innerText = "rustdoc " + getVar("rustdoc-version"); |
c295e0f8 XL |
1014 | rustdoc_version.appendChild(rustdoc_version_code); |
1015 | ||
064997fb | 1016 | const container = document.createElement("div"); |
2b03887a FG |
1017 | if (!isHelpPage) { |
1018 | container.className = "popover"; | |
1019 | } | |
1020 | container.id = "help"; | |
064997fb FG |
1021 | container.style.display = "none"; |
1022 | ||
1023 | const side_by_side = document.createElement("div"); | |
1024 | side_by_side.className = "side-by-side"; | |
1025 | side_by_side.appendChild(div_shortcuts); | |
1026 | side_by_side.appendChild(div_infos); | |
1027 | ||
1028 | container.appendChild(book_info); | |
1029 | container.appendChild(side_by_side); | |
c295e0f8 XL |
1030 | container.appendChild(rustdoc_version); |
1031 | ||
2b03887a FG |
1032 | if (isHelpPage) { |
1033 | const help_section = document.createElement("section"); | |
1034 | help_section.appendChild(container); | |
1035 | document.getElementById("main-content").appendChild(help_section); | |
1036 | container.style.display = "block"; | |
1037 | } else { | |
1038 | const help_button = getHelpButton(); | |
1039 | help_button.appendChild(container); | |
1040 | ||
1041 | container.onblur = helpBlurHandler; | |
2b03887a FG |
1042 | help_button.onblur = helpBlurHandler; |
1043 | help_button.children[0].onblur = helpBlurHandler; | |
1044 | } | |
064997fb FG |
1045 | |
1046 | return container; | |
1047 | } | |
1048 | ||
487cf647 | 1049 | /** |
9ffffee4 | 1050 | * Hide popover menus, clickable tooltips, and the sidebar (if applicable). |
487cf647 | 1051 | * |
9ffffee4 | 1052 | * Pass "true" to reset focus for tooltip popovers. |
487cf647 FG |
1053 | */ |
1054 | window.hideAllModals = function(switchFocus) { | |
1055 | hideSidebar(); | |
1056 | window.hidePopoverMenus(); | |
9ffffee4 | 1057 | hideTooltip(switchFocus); |
487cf647 FG |
1058 | }; |
1059 | ||
064997fb FG |
1060 | /** |
1061 | * Hide all the popover menus. | |
1062 | */ | |
1063 | window.hidePopoverMenus = function() { | |
2b03887a | 1064 | onEachLazy(document.querySelectorAll(".search-form .popover"), elem => { |
064997fb FG |
1065 | elem.style.display = "none"; |
1066 | }); | |
17df50a5 | 1067 | }; |
e74abb32 | 1068 | |
064997fb FG |
1069 | /** |
1070 | * Returns the help menu element (not the button). | |
1071 | * | |
1072 | * @param {boolean} buildNeeded - If this argument is `false`, the help menu element won't be | |
1073 | * built if it doesn't exist. | |
1074 | * | |
1075 | * @return {HTMLElement} | |
1076 | */ | |
1077 | function getHelpMenu(buildNeeded) { | |
1078 | let menu = getHelpButton().querySelector(".popover"); | |
1079 | if (!menu && buildNeeded) { | |
1080 | menu = buildHelpMenu(); | |
1081 | } | |
1082 | return menu; | |
1083 | } | |
1084 | ||
1085 | /** | |
1086 | * Show the help popup menu. | |
1087 | */ | |
1088 | function showHelp() { | |
9c376795 FG |
1089 | // Prevent `blur` events from being dispatched as a result of closing |
1090 | // other modals. | |
1091 | getHelpButton().querySelector("a").focus(); | |
064997fb FG |
1092 | const menu = getHelpMenu(true); |
1093 | if (menu.style.display === "none") { | |
487cf647 | 1094 | window.hideAllModals(); |
064997fb FG |
1095 | menu.style.display = ""; |
1096 | } | |
1097 | } | |
1098 | ||
2b03887a FG |
1099 | if (isHelpPage) { |
1100 | showHelp(); | |
1101 | document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click", event => { | |
1102 | // Already on the help page, make help button a no-op. | |
1103 | const target = event.target; | |
1104 | if (target.tagName !== "A" || | |
1105 | target.parentElement.id !== HELP_BUTTON_ID || | |
1106 | event.ctrlKey || | |
1107 | event.altKey || | |
1108 | event.metaKey) { | |
1109 | return; | |
1110 | } | |
1111 | event.preventDefault(); | |
1112 | }); | |
1113 | } else { | |
1114 | document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click", event => { | |
1115 | // By default, have help button open docs in a popover. | |
1116 | // If user clicks with a moderator, though, use default browser behavior, | |
1117 | // probably opening in a new window or tab. | |
1118 | const target = event.target; | |
1119 | if (target.tagName !== "A" || | |
1120 | target.parentElement.id !== HELP_BUTTON_ID || | |
1121 | event.ctrlKey || | |
1122 | event.altKey || | |
1123 | event.metaKey) { | |
1124 | return; | |
1125 | } | |
1126 | event.preventDefault(); | |
1127 | const menu = getHelpMenu(true); | |
1128 | const shouldShowHelp = menu.style.display === "none"; | |
1129 | if (shouldShowHelp) { | |
1130 | showHelp(); | |
1131 | } else { | |
1132 | window.hidePopoverMenus(); | |
1133 | } | |
1134 | }); | |
1135 | } | |
064997fb | 1136 | |
923072b8 FG |
1137 | setMobileTopbar(); |
1138 | addSidebarItems(); | |
1139 | addSidebarCrates(); | |
cdc7bbd5 | 1140 | onHashChange(null); |
17df50a5 | 1141 | window.addEventListener("hashchange", onHashChange); |
cdc7bbd5 XL |
1142 | searchState.setup(); |
1143 | }()); | |
6a06907d | 1144 | |
923072b8 | 1145 | (function() { |
04454e1e | 1146 | let reset_button_timeout = null; |
17df50a5 | 1147 | |
9ffffee4 FG |
1148 | const but = document.getElementById("copy-path"); |
1149 | if (!but) { | |
1150 | return; | |
1151 | } | |
1152 | but.onclick = () => { | |
04454e1e FG |
1153 | const parent = but.parentElement; |
1154 | const path = []; | |
17df50a5 | 1155 | |
04454e1e FG |
1156 | onEach(parent.childNodes, child => { |
1157 | if (child.tagName === "A") { | |
17df50a5 XL |
1158 | path.push(child.textContent); |
1159 | } | |
1160 | }); | |
6a06907d | 1161 | |
04454e1e FG |
1162 | const el = document.createElement("textarea"); |
1163 | el.value = path.join("::"); | |
1164 | el.setAttribute("readonly", ""); | |
17df50a5 | 1165 | // To not make it appear on the screen. |
04454e1e FG |
1166 | el.style.position = "absolute"; |
1167 | el.style.left = "-9999px"; | |
17df50a5 XL |
1168 | |
1169 | document.body.appendChild(el); | |
1170 | el.select(); | |
04454e1e | 1171 | document.execCommand("copy"); |
17df50a5 XL |
1172 | document.body.removeChild(el); |
1173 | ||
1174 | // There is always one children, but multiple childNodes. | |
04454e1e | 1175 | but.children[0].style.display = "none"; |
17df50a5 | 1176 | |
04454e1e | 1177 | let tmp; |
17df50a5 | 1178 | if (but.childNodes.length < 2) { |
04454e1e | 1179 | tmp = document.createTextNode("✓"); |
17df50a5 XL |
1180 | but.appendChild(tmp); |
1181 | } else { | |
04454e1e | 1182 | onEachLazy(but.childNodes, e => { |
17df50a5 XL |
1183 | if (e.nodeType === Node.TEXT_NODE) { |
1184 | tmp = e; | |
1185 | return true; | |
1186 | } | |
1187 | }); | |
04454e1e | 1188 | tmp.textContent = "✓"; |
cdc7bbd5 | 1189 | } |
6a06907d | 1190 | |
17df50a5 XL |
1191 | if (reset_button_timeout !== null) { |
1192 | window.clearTimeout(reset_button_timeout); | |
1193 | } | |
6a06907d | 1194 | |
17df50a5 | 1195 | function reset_button() { |
04454e1e | 1196 | tmp.textContent = ""; |
17df50a5 XL |
1197 | reset_button_timeout = null; |
1198 | but.children[0].style.display = ""; | |
1199 | } | |
6a06907d | 1200 | |
17df50a5 XL |
1201 | reset_button_timeout = window.setTimeout(reset_button, 1000); |
1202 | }; | |
1203 | }()); |