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