pub fn render<T: fmt::Display, S: fmt::Display>(
dst: &mut dyn io::Write, layout: &Layout, page: &Page, sidebar: &S, t: &T,
- css_file_extension: bool, themes: &[PathBuf])
+ css_file_extension: bool, themes: &[PathBuf], extra_scripts: &[&str])
-> io::Result<()>
{
write!(dst,
</script>\
<script src=\"{root_path}aliases.js\"></script>\
<script src=\"{root_path}main{suffix}.js\"></script>\
+ {extra_scripts}\
<script defer src=\"{root_path}search-index.js\"></script>\
</body>\
</html>",
page.resource_suffix))
.collect::<String>(),
suffix=page.resource_suffix,
+ extra_scripts=extra_scripts.iter().map(|e| {
+ format!("<script src=\"{root_path}{extra_script}\"></script>",
+ root_path=page.root_path,
+ extra_script=e)
+ }).collect::<String>(),
)
}
write_minify(cx.dst.join(&format!("settings{}.js", cx.shared.resource_suffix)),
static_files::SETTINGS_JS,
options.enable_minification)?;
+ write_minify(cx.dst.join(&format!("source-script{}.js", cx.shared.resource_suffix)),
+ include_str!("static/source-script.js"),
+ options.enable_minification)?;
{
let mut data = format!("var resourcesSuffix = \"{}\";\n",
}
}
+ use std::ffi::OsString;
+
+ #[derive(Debug)]
+ struct Hierarchy {
+ elem: OsString,
+ children: FxHashMap<OsString, Hierarchy>,
+ elems: FxHashSet<OsString>,
+ }
+
+ impl Hierarchy {
+ fn new(elem: OsString) -> Hierarchy {
+ Hierarchy {
+ elem,
+ children: FxHashMap::default(),
+ elems: FxHashSet::default(),
+ }
+ }
+
+ fn to_string(&self) -> String {
+ let mut subs: Vec<&Hierarchy> = self.children.iter().map(|(_, v)| v).collect();
+ subs.sort_unstable_by(|a, b| a.elem.cmp(&b.elem));
+ let mut files = self.elems.iter()
+ .map(|s| format!("\"{}\"",
+ s.to_str()
+ .expect("invalid osstring conversion")))
+ .collect::<Vec<_>>();
+ files.sort_unstable_by(|a, b| a.cmp(b));
+ // FIXME(imperio): we could avoid to generate "dirs" and "files" if they're empty.
+ format!("{{\"name\":\"{name}\",\"dirs\":[{subs}],\"files\":[{files}]}}",
+ name=self.elem.to_str().expect("invalid osstring conversion"),
+ subs=subs.iter().map(|s| s.to_string()).collect::<Vec<_>>().join(","),
+ files=files.join(","))
+ }
+ }
+
+ use std::path::Component;
+
+ let mut hierarchy = Hierarchy::new(OsString::new());
+ for source in cx.shared.local_sources.iter()
+ .filter_map(|p| p.0.strip_prefix(&cx.shared.src_root)
+ .ok()) {
+ let mut h = &mut hierarchy;
+ let mut elems = source.components()
+ .filter_map(|s| {
+ match s {
+ Component::Normal(s) => Some(s.to_owned()),
+ _ => None,
+ }
+ })
+ .peekable();
+ loop {
+ let cur_elem = elems.next().expect("empty file path");
+ if elems.peek().is_none() {
+ h.elems.insert(cur_elem);
+ break;
+ } else {
+ let e = cur_elem.clone();
+ h.children.entry(cur_elem.clone()).or_insert_with(|| Hierarchy::new(e));
+ h = h.children.get_mut(&cur_elem).expect("not found child");
+ }
+ }
+ }
+
+ let dst = cx.dst.join("source-files.js");
+ let (mut all_sources, _krates) = try_err!(collect(&dst, &krate.name, "sourcesIndex"), &dst);
+ all_sources.push(format!("sourcesIndex['{}'] = {};", &krate.name, hierarchy.to_string()));
+ all_sources.sort();
+ let mut w = try_err!(File::create(&dst), &dst);
+ try_err!(writeln!(&mut w, "var N = null;var sourcesIndex = {{}};\n{}", all_sources.join("\n")),
+ &dst);
+
// Update the search index
let dst = cx.dst.join("search-index.js");
let (mut all_indexes, mut krates) = try_err!(collect(&dst, &krate.name, "searchIndex"), &dst);
all_indexes.push(search_index);
+
// Sort the indexes by crate so the file will be generated identically even
// with rustdoc running in parallel.
all_indexes.sort();
try_err!(layout::render(&mut w, &cx.shared.layout,
&page, &(""), &content,
cx.shared.css_file_extension.is_some(),
- &cx.shared.themes), &dst);
+ &cx.shared.themes, &[]), &dst);
try_err!(w.flush(), &dst);
}
}
layout::render(&mut w, &self.scx.layout,
&page, &(""), &Source(contents),
self.scx.css_file_extension.is_some(),
- &self.scx.themes)?;
+ &self.scx.themes, &["source-files.js", "source-script.js"])?;
w.flush()?;
self.scx.local_sources.insert(p.clone(), href);
Ok(())
try_err!(layout::render(&mut w, &self.shared.layout,
&page, &sidebar, &all,
self.shared.css_file_extension.is_some(),
- &self.shared.themes),
+ &self.shared.themes, &[]),
&final_file);
// Generating settings page.
try_err!(layout::render(&mut w, &layout,
&page, &sidebar, &settings,
self.shared.css_file_extension.is_some(),
- &themes),
+ &themes, &[]),
&settings_file);
Ok(())
&Sidebar{ cx: self, item: it },
&Item{ cx: self, item: it },
self.shared.css_file_extension.is_some(),
- &self.shared.themes)?;
+ &self.shared.themes, &[])?;
} else {
let mut url = self.root_path();
if let Some(&(ref names, ty)) = cache().paths.get(&it.def_id) {
/*jslint browser: true, es5: true */
/*globals $: true, rootPath: true */
+if (!String.prototype.startsWith) {
+ String.prototype.startsWith = function(searchString, position) {
+ position = position || 0;
+ return this.indexOf(searchString, position) === position;
+ };
+}
+if (!String.prototype.endsWith) {
+ String.prototype.endsWith = function(suffix, length) {
+ var l = length || this.length;
+ return this.indexOf(suffix, l - suffix.length) !== -1;
+ };
+}
+
(function() {
"use strict";
var titleBeforeSearch = document.title;
- if (!String.prototype.startsWith) {
- String.prototype.startsWith = function(searchString, position) {
- position = position || 0;
- return this.indexOf(searchString, position) === position;
- };
- }
- if (!String.prototype.endsWith) {
- String.prototype.endsWith = function(suffix, length) {
- var l = length || this.length;
- return this.indexOf(suffix, l - suffix.length) !== -1;
- };
- }
-
function getPageId() {
var id = document.location.href.split('#')[1];
if (id) {
return null;
}
- function hasClass(elem, className) {
- if (elem && className && elem.className) {
- var elemClass = elem.className;
- var start = elemClass.indexOf(className);
- if (start === -1) {
- return false;
- } else if (elemClass.length === className.length) {
- return true;
- } else {
- if (start > 0 && elemClass[start - 1] !== ' ') {
- return false;
- }
- var end = start + className.length;
- return !(end < elemClass.length && elemClass[end] !== ' ');
- }
- }
- return false;
- }
-
- function addClass(elem, className) {
- if (elem && className && !hasClass(elem, className)) {
- if (elem.className && elem.className.length > 0) {
- elem.className += ' ' + className;
- } else {
- elem.className = className;
- }
- }
- }
-
- function removeClass(elem, className) {
- if (elem && className && elem.className) {
- elem.className = (" " + elem.className + " ").replace(" " + className + " ", " ")
- .trim();
- }
- }
-
- function isHidden(elem) {
- return (elem.offsetParent === null)
- }
-
function showSidebar() {
var elems = document.getElementsByClassName("sidebar-elems")[0];
if (elems) {
h1, h2, h3, h4,
.sidebar, a.source, .search-input, .content table :not(code)>a,
-.collapse-toggle, div.item-list .out-of-band {
+.collapse-toggle, div.item-list .out-of-band,
+#source-sidebar, #sidebar-toggle {
font-family: "Fira Sans", sans-serif;
}
padding-right: 10px;
}
.content .search-results td:first-child a:after {
- clear: both;
- content: "";
- display: block;
+ clear: both;
+ content: "";
+ display: block;
}
.content .search-results td:first-child a span {
float: left;
.non-exhaustive {
margin-bottom: 1em;
}
+
+#sidebar-toggle {
+ position: fixed;
+ top: 30px;
+ right: 300px;
+ z-index: 10;
+ padding: 3px;
+ border-top-left-radius: 3px;
+ border-bottom-left-radius: 3px;
+ cursor: pointer;
+ font-weight: bold;
+ transition: right 1s;
+ font-size: 1.2em;
+}
+#source-sidebar {
+ position: fixed;
+ top: 0;
+ bottom: 0;
+ right: 0;
+ width: 300px;
+ z-index: 1;
+ overflow: auto;
+ transition: right 1s;
+}
+#source-sidebar > .title {
+ font-size: 1.5em;
+ text-align: center;
+ border-bottom: 1px solid;
+ margin-bottom: 6px;
+}
+
+div.children {
+ padding-left: 27px;
+ display: none;
+}
+div.files > a {
+ display: block;
+ padding: 0 3px;
+}
+div.files > a:hover, div.name:hover {
+ background-color: #a14b4b;
+}
+div.name.expand + .children {
+ display: block;
+}
+div.name::before {
+ content: "\25B6";
+ display: inline-block;
+ padding-right: 4px;
+ font-size: 0.7em;
+}
+div.name.expand::before {
+ transform: rotate(90deg);
+}
--- /dev/null
+/*!
+ * Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+ * file at the top-level directory of this distribution and at
+ * http://rust-lang.org/COPYRIGHT.
+ *
+ * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+ * http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+ * <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+ * option. This file may not be copied, modified, or distributed
+ * except according to those terms.
+ */
+
+function getCurrentFilePath() {
+ var parts = window.location.pathname.split("/");
+ var rootPathParts = window.rootPath.split("/");
+
+ for (var i = 0; i < rootPathParts.length; ++i) {
+ if (rootPathParts[i] === "..") {
+ parts.pop();
+ }
+ }
+ var file = window.location.pathname.substring(parts.join("/").length);
+ if (file.startsWith("/")) {
+ file = file.substring(1);
+ }
+ return file.substring(0, file.length - 5);
+}
+
+function createDirEntry(elem, parent, fullPath, currentFile, hasFoundFile) {
+ var name = document.createElement("div");
+ name.className = "name";
+
+ fullPath += elem["name"] + "/";
+
+ name.onclick = function() {
+ if (hasClass(this, "expand")) {
+ removeClass(this, "expand");
+ } else {
+ addClass(this, "expand");
+ }
+ };
+ name.innerText = elem["name"];
+
+ var children = document.createElement("div");
+ children.className = "children";
+ var folders = document.createElement("div");
+ folders.className = "folders";
+ for (var i = 0; i < elem.dirs.length; ++i) {
+ if (createDirEntry(elem.dirs[i], folders, fullPath, currentFile,
+ hasFoundFile) === true) {
+ addClass(name, "expand");
+ hasFoundFile = true;
+ }
+ }
+ children.appendChild(folders);
+
+ var files = document.createElement("div");
+ files.className = "files";
+ for (i = 0; i < elem.files.length; ++i) {
+ var file = document.createElement("a");
+ file.innerText = elem.files[i];
+ file.href = window.rootPath + "src/" + fullPath + elem.files[i] + ".html";
+ if (hasFoundFile === false &&
+ currentFile === fullPath + elem.files[i]) {
+ file.className = "selected";
+ addClass(name, "expand");
+ hasFoundFile = true;
+ }
+ files.appendChild(file);
+ }
+ search.fullPath = fullPath;
+ children.appendChild(files);
+ parent.appendChild(name);
+ parent.appendChild(children);
+ return hasFoundFile === true && search.currentFile !== null;
+}
+
+function toggleSidebar() {
+ var sidebar = document.getElementById("source-sidebar");
+ var child = this.children[0].children[0];
+ if (child.innerText === "<") {
+ sidebar.style.right = "";
+ this.style.right = "";
+ child.innerText = ">";
+ updateLocalStorage("rustdoc-source-sidebar-hidden", "false");
+ } else {
+ sidebar.style.right = "-300px";
+ this.style.right = "0";
+ child.innerText = "<";
+ updateLocalStorage("rustdoc-source-sidebar-hidden", "true");
+ }
+}
+
+function createSidebarToggle() {
+ var sidebarToggle = document.createElement("div");
+ sidebarToggle.id = "sidebar-toggle";
+ sidebarToggle.onclick = toggleSidebar;
+
+ var inner1 = document.createElement("div");
+ inner1.style.position = "relative";
+
+ var inner2 = document.createElement("div");
+ inner2.style.marginTop = "-2px";
+ if (getCurrentValue("rustdoc-source-sidebar-hidden") === "true") {
+ inner2.innerText = "<";
+ sidebarToggle.style.right = "0";
+ } else {
+ inner2.innerText = ">";
+ }
+
+ inner1.appendChild(inner2);
+ sidebarToggle.appendChild(inner1);
+ return sidebarToggle;
+}
+
+function createSourceSidebar() {
+ if (window.rootPath.endsWith("/") === false) {
+ window.rootPath += "/";
+ }
+ var main = document.getElementById("main");
+
+ var sidebarToggle = createSidebarToggle();
+ main.insertBefore(sidebarToggle, main.firstChild);
+
+ var sidebar = document.createElement("div");
+ sidebar.id = "source-sidebar";
+ if (getCurrentValue("rustdoc-source-sidebar-hidden") === "true") {
+ sidebar.style.right = "-300px";
+ }
+
+ var currentFile = getCurrentFilePath();
+ var hasFoundFile = false;
+
+ var title = document.createElement("div");
+ title.className = "title";
+ title.innerText = "Files";
+ sidebar.appendChild(title);
+ Object.keys(sourcesIndex).forEach(function(key) {
+ sourcesIndex[key].name = key;
+ hasFoundFile = createDirEntry(sourcesIndex[key], sidebar, "",
+ currentFile, hasFoundFile);
+ });
+
+ main.insertBefore(sidebar, main.firstChild);
+}
+
+createSourceSidebar();
var savedHref = [];
+function hasClass(elem, className) {
+ if (elem && className && elem.className) {
+ var elemClass = elem.className;
+ var start = elemClass.indexOf(className);
+ if (start === -1) {
+ return false;
+ } else if (elemClass.length === className.length) {
+ return true;
+ } else {
+ if (start > 0 && elemClass[start - 1] !== ' ') {
+ return false;
+ }
+ var end = start + className.length;
+ return !(end < elemClass.length && elemClass[end] !== ' ');
+ }
+ }
+ return false;
+}
+
+function addClass(elem, className) {
+ if (elem && className && !hasClass(elem, className)) {
+ if (elem.className && elem.className.length > 0) {
+ elem.className += ' ' + className;
+ } else {
+ elem.className = className;
+ }
+ }
+}
+
+function removeClass(elem, className) {
+ if (elem && className && elem.className) {
+ elem.className = (" " + elem.className + " ").replace(" " + className + " ", " ")
+ .trim();
+ }
+}
+
+function isHidden(elem) {
+ return (elem.offsetParent === null)
+}
+
function onEach(arr, func, reversed) {
if (arr && arr.length > 0 && func) {
if (reversed !== true) {
.impl-items code {
background-color: rgba(0, 0, 0, 0);
}
+
+#sidebar-toggle {
+ background-color: #565656;
+}
+#sidebar-toggle:hover {
+ background-color: #676767;
+}
+#source-sidebar {
+ background-color: #565656;
+}
+#source-sidebar > .title {
+ border-bottom-color: #ccc;
+}
+div.files > a:hover, div.name:hover {
+ background-color: #444;
+}
+div.files > .selected {
+ background-color: #333;
+}
.impl-items code {
background-color: rgba(0, 0, 0, 0);
}
+
+#sidebar-toggle {
+ background-color: #F1F1F1;
+}
+#sidebar-toggle:hover {
+ background-color: #E0E0E0;
+}
+#source-sidebar {
+ background-color: #F1F1F1;
+}
+#source-sidebar > .title {
+ border-bottom-color: #ccc;
+}
+div.files > a:hover, div.name:hover {
+ background-color: #E0E0E0;
+}
+div.files > .selected {
+ background-color: #fff;
+}