]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/html/static/js/source-script.js
Rollup merge of #103709 - cuviper:netbsd-9, r=pietroalbini
[rust.git] / src / librustdoc / html / static / js / source-script.js
1 // From rust:
2 /* global sourcesIndex */
3
4 // Local js definitions:
5 /* global addClass, getCurrentValue, onEachLazy, removeClass, browserSupportsHistoryApi */
6 /* global updateLocalStorage */
7
8 "use strict";
9
10 (function() {
11
12 const rootPath = document.getElementById("rustdoc-vars").attributes["data-root-path"].value;
13
14 const NAME_OFFSET = 0;
15 const DIRS_OFFSET = 1;
16 const FILES_OFFSET = 2;
17
18 function closeSidebarIfMobile() {
19     if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT) {
20         updateLocalStorage("source-sidebar-show", "false");
21     }
22 }
23
24 function createDirEntry(elem, parent, fullPath, hasFoundFile) {
25     const dirEntry = document.createElement("details");
26     const summary = document.createElement("summary");
27
28     dirEntry.className = "dir-entry";
29
30     fullPath += elem[NAME_OFFSET] + "/";
31
32     summary.innerText = elem[NAME_OFFSET];
33     dirEntry.appendChild(summary);
34
35     const folders = document.createElement("div");
36     folders.className = "folders";
37     if (elem[DIRS_OFFSET]) {
38         for (const dir of elem[DIRS_OFFSET]) {
39             if (createDirEntry(dir, folders, fullPath, false)) {
40                 dirEntry.open = true;
41                 hasFoundFile = true;
42             }
43         }
44     }
45     dirEntry.appendChild(folders);
46
47     const files = document.createElement("div");
48     files.className = "files";
49     if (elem[FILES_OFFSET]) {
50         for (const file_text of elem[FILES_OFFSET]) {
51             const file = document.createElement("a");
52             file.innerText = file_text;
53             file.href = rootPath + "src/" + fullPath + file_text + ".html";
54             file.addEventListener("click", closeSidebarIfMobile);
55             const w = window.location.href.split("#")[0];
56             if (!hasFoundFile && w === file.href) {
57                 file.className = "selected";
58                 dirEntry.open = true;
59                 hasFoundFile = true;
60             }
61             files.appendChild(file);
62         }
63     }
64     dirEntry.appendChild(files);
65     parent.appendChild(dirEntry);
66     return hasFoundFile;
67 }
68
69 function toggleSidebar() {
70     const child = this.parentNode.children[0];
71     if (child.innerText === ">") {
72         window.rustdocMobileScrollLock();
73         addClass(document.documentElement, "source-sidebar-expanded");
74         child.innerText = "<";
75         updateLocalStorage("source-sidebar-show", "true");
76     } else {
77         window.rustdocMobileScrollUnlock();
78         removeClass(document.documentElement, "source-sidebar-expanded");
79         child.innerText = ">";
80         updateLocalStorage("source-sidebar-show", "false");
81     }
82 }
83
84 function createSidebarToggle() {
85     const sidebarToggle = document.createElement("div");
86     sidebarToggle.id = "sidebar-toggle";
87
88     const inner = document.createElement("button");
89
90     if (getCurrentValue("source-sidebar-show") === "true") {
91         inner.innerText = "<";
92     } else {
93         inner.innerText = ">";
94     }
95     inner.onclick = toggleSidebar;
96
97     sidebarToggle.appendChild(inner);
98     return sidebarToggle;
99 }
100
101 // This function is called from "source-files.js", generated in `html/render/write_shared.rs`.
102 // eslint-disable-next-line no-unused-vars
103 function createSourceSidebar() {
104     const container = document.querySelector("nav.sidebar");
105
106     const sidebarToggle = createSidebarToggle();
107     container.insertBefore(sidebarToggle, container.firstChild);
108
109     const sidebar = document.createElement("div");
110     sidebar.id = "source-sidebar";
111
112     let hasFoundFile = false;
113
114     const title = document.createElement("div");
115     title.className = "title";
116     title.innerText = "Files";
117     sidebar.appendChild(title);
118     Object.keys(sourcesIndex).forEach(key => {
119         sourcesIndex[key][NAME_OFFSET] = key;
120         hasFoundFile = createDirEntry(sourcesIndex[key], sidebar, "",
121             hasFoundFile);
122     });
123
124     container.appendChild(sidebar);
125     // Focus on the current file in the source files sidebar.
126     const selected_elem = sidebar.getElementsByClassName("selected")[0];
127     if (typeof selected_elem !== "undefined") {
128         selected_elem.focus();
129     }
130 }
131
132 const lineNumbersRegex = /^#?(\d+)(?:-(\d+))?$/;
133
134 function highlightSourceLines(match) {
135     if (typeof match === "undefined") {
136         match = window.location.hash.match(lineNumbersRegex);
137     }
138     if (!match) {
139         return;
140     }
141     let from = parseInt(match[1], 10);
142     let to = from;
143     if (typeof match[2] !== "undefined") {
144         to = parseInt(match[2], 10);
145     }
146     if (to < from) {
147         const tmp = to;
148         to = from;
149         from = tmp;
150     }
151     let elem = document.getElementById(from);
152     if (!elem) {
153         return;
154     }
155     const x = document.getElementById(from);
156     if (x) {
157         x.scrollIntoView();
158     }
159     onEachLazy(document.getElementsByClassName("src-line-numbers"), e => {
160         onEachLazy(e.getElementsByTagName("a"), i_e => {
161             removeClass(i_e, "line-highlighted");
162         });
163     });
164     for (let i = from; i <= to; ++i) {
165         elem = document.getElementById(i);
166         if (!elem) {
167             break;
168         }
169         addClass(elem, "line-highlighted");
170     }
171 }
172
173 const handleSourceHighlight = (function() {
174     let prev_line_id = 0;
175
176     const set_fragment = name => {
177         const x = window.scrollX,
178             y = window.scrollY;
179         if (browserSupportsHistoryApi()) {
180             history.replaceState(null, null, "#" + name);
181             highlightSourceLines();
182         } else {
183             location.replace("#" + name);
184         }
185         // Prevent jumps when selecting one or many lines
186         window.scrollTo(x, y);
187     };
188
189     return ev => {
190         let cur_line_id = parseInt(ev.target.id, 10);
191         // This event handler is attached to the entire line number column, but it should only
192         // be run if one of the anchors is clicked. It also shouldn't do anything if the anchor
193         // is clicked with a modifier key (to open a new browser tab).
194         if (isNaN(cur_line_id) ||
195             ev.ctrlKey ||
196             ev.altKey ||
197             ev.metaKey) {
198             return;
199         }
200         ev.preventDefault();
201
202         if (ev.shiftKey && prev_line_id) {
203             // Swap selection if needed
204             if (prev_line_id > cur_line_id) {
205                 const tmp = prev_line_id;
206                 prev_line_id = cur_line_id;
207                 cur_line_id = tmp;
208             }
209
210             set_fragment(prev_line_id + "-" + cur_line_id);
211         } else {
212             prev_line_id = cur_line_id;
213
214             set_fragment(cur_line_id);
215         }
216     };
217 }());
218
219 window.addEventListener("hashchange", () => {
220     const match = window.location.hash.match(lineNumbersRegex);
221     if (match) {
222         return highlightSourceLines(match);
223     }
224 });
225
226 onEachLazy(document.getElementsByClassName("src-line-numbers"), el => {
227     el.addEventListener("click", handleSourceHighlight);
228 });
229
230 highlightSourceLines();
231
232 window.createSourceSidebar = createSourceSidebar;
233 })();