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