2 /* global sourcesIndex */
4 // Local js definitions:
5 /* global addClass, getCurrentValue, onEachLazy, removeClass, browserSupportsHistoryApi */
6 /* global updateLocalStorage */
12 const rootPath = document.getElementById("rustdoc-vars").attributes["data-root-path"].value;
13 let oldScrollPosition = 0;
15 function closeSidebarIfMobile() {
16 if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT) {
17 updateLocalStorage("source-sidebar-show", "false");
21 function createDirEntry(elem, parent, fullPath, hasFoundFile) {
22 const dirEntry = document.createElement("details");
23 const summary = document.createElement("summary");
25 dirEntry.className = "dir-entry";
27 fullPath += elem["name"] + "/";
29 summary.innerText = elem["name"];
30 dirEntry.appendChild(summary);
32 const folders = document.createElement("div");
33 folders.className = "folders";
35 for (const dir of elem.dirs) {
36 if (createDirEntry(dir, folders, fullPath, hasFoundFile)) {
42 dirEntry.appendChild(folders);
44 const files = document.createElement("div");
45 files.className = "files";
47 for (const file_text of elem.files) {
48 const file = document.createElement("a");
49 file.innerText = file_text;
50 file.href = rootPath + "src/" + fullPath + file_text + ".html";
51 file.addEventListener("click", closeSidebarIfMobile);
52 const w = window.location.href.split("#")[0];
53 if (!hasFoundFile && w === file.href) {
54 file.className = "selected";
58 files.appendChild(file);
61 dirEntry.appendChild(files);
62 parent.appendChild(dirEntry);
66 function toggleSidebar() {
67 const child = this.parentNode.children[0];
68 if (child.innerText === ">") {
69 if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT) {
70 // This is to keep the scroll position on mobile.
71 oldScrollPosition = window.scrollY;
72 document.body.style.position = "fixed";
73 document.body.style.top = `-${oldScrollPosition}px`;
75 addClass(document.documentElement, "source-sidebar-expanded");
76 child.innerText = "<";
77 updateLocalStorage("source-sidebar-show", "true");
79 if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT) {
80 // This is to keep the scroll position on mobile.
81 document.body.style.position = "";
82 document.body.style.top = "";
83 // The scroll position is lost when resetting the style, hence why we store it in
85 window.scrollTo(0, oldScrollPosition);
87 removeClass(document.documentElement, "source-sidebar-expanded");
88 child.innerText = ">";
89 updateLocalStorage("source-sidebar-show", "false");
93 function createSidebarToggle() {
94 const sidebarToggle = document.createElement("div");
95 sidebarToggle.id = "sidebar-toggle";
97 const inner = document.createElement("button");
99 if (getCurrentValue("source-sidebar-show") === "true") {
100 inner.innerText = "<";
102 inner.innerText = ">";
104 inner.onclick = toggleSidebar;
106 sidebarToggle.appendChild(inner);
107 return sidebarToggle;
110 // This function is called from "source-files.js", generated in `html/render/mod.rs`.
111 // eslint-disable-next-line no-unused-vars
112 function createSourceSidebar() {
113 const container = document.querySelector("nav.sidebar");
115 const sidebarToggle = createSidebarToggle();
116 container.insertBefore(sidebarToggle, container.firstChild);
118 const sidebar = document.createElement("div");
119 sidebar.id = "source-sidebar";
121 let hasFoundFile = false;
123 const title = document.createElement("div");
124 title.className = "title";
125 title.innerText = "Files";
126 sidebar.appendChild(title);
127 Object.keys(sourcesIndex).forEach(key => {
128 sourcesIndex[key].name = key;
129 hasFoundFile = createDirEntry(sourcesIndex[key], sidebar, "",
133 container.appendChild(sidebar);
134 // Focus on the current file in the source files sidebar.
135 const selected_elem = sidebar.getElementsByClassName("selected")[0];
136 if (typeof selected_elem !== "undefined") {
137 selected_elem.focus();
141 const lineNumbersRegex = /^#?(\d+)(?:-(\d+))?$/;
143 function highlightSourceLines(match) {
144 if (typeof match === "undefined") {
145 match = window.location.hash.match(lineNumbersRegex);
150 let from = parseInt(match[1], 10);
152 if (typeof match[2] !== "undefined") {
153 to = parseInt(match[2], 10);
160 let elem = document.getElementById(from);
164 const x = document.getElementById(from);
168 onEachLazy(document.getElementsByClassName("line-numbers"), e => {
169 onEachLazy(e.getElementsByTagName("span"), i_e => {
170 removeClass(i_e, "line-highlighted");
173 for (let i = from; i <= to; ++i) {
174 elem = document.getElementById(i);
178 addClass(elem, "line-highlighted");
182 const handleSourceHighlight = (function() {
183 let prev_line_id = 0;
185 const set_fragment = name => {
186 const x = window.scrollX,
188 if (browserSupportsHistoryApi()) {
189 history.replaceState(null, null, "#" + name);
190 highlightSourceLines();
192 location.replace("#" + name);
194 // Prevent jumps when selecting one or many lines
195 window.scrollTo(x, y);
199 let cur_line_id = parseInt(ev.target.id, 10);
200 // It can happen when clicking not on a line number span.
201 if (isNaN(cur_line_id)) {
206 if (ev.shiftKey && prev_line_id) {
207 // Swap selection if needed
208 if (prev_line_id > cur_line_id) {
209 const tmp = prev_line_id;
210 prev_line_id = cur_line_id;
214 set_fragment(prev_line_id + "-" + cur_line_id);
216 prev_line_id = cur_line_id;
218 set_fragment(cur_line_id);
223 window.addEventListener("hashchange", () => {
224 const match = window.location.hash.match(lineNumbersRegex);
226 return highlightSourceLines(match);
230 onEachLazy(document.getElementsByClassName("line-numbers"), el => {
231 el.addEventListener("click", handleSourceHighlight);
234 highlightSourceLines();
236 window.createSourceSidebar = createSourceSidebar;