mirror of
https://github.com/rust-lang/mdBook.git
synced 2025-12-28 11:24:57 -05:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b765023da3 | ||
|
|
d306aed587 | ||
|
|
89a5dbaf9a | ||
|
|
6961247f56 | ||
|
|
07551760c9 | ||
|
|
990daceed5 | ||
|
|
2989096188 | ||
|
|
03c6c44e5b | ||
|
|
31a370d149 | ||
|
|
0bc1030a02 | ||
|
|
43fcd00cd5 | ||
|
|
3d83b784b3 | ||
|
|
5d42738a79 | ||
|
|
1f4dab3e5c | ||
|
|
7181993b43 | ||
|
|
bf9f58e11b | ||
|
|
3ba71c570c | ||
|
|
674e58e747 | ||
|
|
348c5d07c5 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,3 @@
|
||||
Cargo.lock
|
||||
target
|
||||
|
||||
# MacOS temp file
|
||||
|
||||
1380
Cargo.lock
generated
Normal file
1380
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mdbook"
|
||||
version = "0.1.1"
|
||||
version = "0.1.3"
|
||||
authors = ["Mathieu David <mathieudavid@mathieudavid.org>", "Michael-F-Bryan <michaelfbryan@gmail.com>"]
|
||||
description = "create books from markdown files (like Gitbook)"
|
||||
documentation = "http://rust-lang-nursery.github.io/mdBook/index.html"
|
||||
|
||||
@@ -40,7 +40,7 @@ There are multiple ways to install mdBook.
|
||||
path to the binary into your `PATH`.
|
||||
|
||||
2. **From Crates.io**
|
||||
j
|
||||
|
||||
This requires [Rust] and Cargo to be installed. Once you have installed
|
||||
Rust, type the following in the terminal:
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
- [Syntax highlighting](format/theme/syntax-highlighting.md)
|
||||
- [Editor](format/theme/editor.md)
|
||||
- [MathJax Support](format/mathjax.md)
|
||||
- [Rust code specific features](format/rust.md)
|
||||
- [mdBook specific features](format/mdbook.md)
|
||||
- [For Developers](for_developers/index.md)
|
||||
- [Preprocessors](for_developers/preprocessors.md)
|
||||
- [Alternate Backends](for_developers/backends.md)
|
||||
|
||||
@@ -110,7 +110,7 @@ editable = false
|
||||
|
||||
## Environment Variables
|
||||
|
||||
All configuration values van be overridden from the command line by setting the
|
||||
All configuration values can be overridden from the command line by setting the
|
||||
corresponding environment variable. Because many operating systems restrict
|
||||
environment variables to be alphanumeric characters or `_`, the configuration
|
||||
key needs to be formatted slightly differently to the normal `foo.bar.baz` form.
|
||||
|
||||
64
book-example/src/format/mdbook.md
Normal file
64
book-example/src/format/mdbook.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# mdBook-specific markdown
|
||||
|
||||
## Hiding code lines
|
||||
|
||||
There is a feature in mdBook that lets you hide code lines by prepending them with a `#`.
|
||||
|
||||
```bash
|
||||
# fn main() {
|
||||
let x = 5;
|
||||
let y = 6;
|
||||
|
||||
println!("{}", x + y);
|
||||
# }
|
||||
```
|
||||
|
||||
Will render as
|
||||
|
||||
```rust
|
||||
# fn main() {
|
||||
let x = 5;
|
||||
let y = 7;
|
||||
|
||||
println!("{}", x + y);
|
||||
# }
|
||||
```
|
||||
|
||||
## Including files
|
||||
|
||||
With the following syntax, you can include files into your book:
|
||||
|
||||
```hbs
|
||||
\{{#include file.rs}}
|
||||
```
|
||||
|
||||
The path to the file has to be relative from the current source file.
|
||||
|
||||
Usually, this command is used for including code snippets and examples. In this case, oftens one would include a specific part of the file e.g. which only contains the relevant lines for the example. We support four different modes of partial includes:
|
||||
|
||||
```hbs
|
||||
\{{#include file.rs:2}}
|
||||
\{{#include file.rs::10}}
|
||||
\{{#include file.rs:2:}}
|
||||
\{{#include file.rs:2:10}}
|
||||
```
|
||||
|
||||
The first command only includes the second line from file `file.rs`. The second command includes all lines up to line 10, i.e. the lines from 11 till the end of the file are omitted. The third command includes all lines from line 2, i.e. the first line is omitted. The last command includes the excerpt of `file.rs` consisting of lines 2 to 10.
|
||||
|
||||
## Inserting runnable Rust files
|
||||
|
||||
With the following syntax, you can insert runnable Rust files into your book:
|
||||
|
||||
```hbs
|
||||
\{{#playpen file.rs}}
|
||||
```
|
||||
|
||||
The path to the Rust file has to be relative from the current source file.
|
||||
|
||||
When play is clicked, the code snippet will be sent to the [Rust Playpen] to be compiled and run. The result is sent back and displayed directly underneath the code.
|
||||
|
||||
Here is what a rendered code snippet looks like:
|
||||
|
||||
{{#playpen example.rs}}
|
||||
|
||||
[Rust Playpen]: https://play.rust-lang.org/
|
||||
@@ -1,44 +0,0 @@
|
||||
# Rust code specific features
|
||||
|
||||
## Hiding code lines
|
||||
|
||||
There is a feature in mdBook that let's you hide code lines by prepending them with a `#`.
|
||||
|
||||
```bash
|
||||
# fn main() {
|
||||
let x = 5;
|
||||
let y = 6;
|
||||
|
||||
println!("{}", x + y);
|
||||
# }
|
||||
```
|
||||
|
||||
Will render as
|
||||
|
||||
```rust
|
||||
# fn main() {
|
||||
let x = 5;
|
||||
let y = 7;
|
||||
|
||||
println!("{}", x + y);
|
||||
# }
|
||||
```
|
||||
|
||||
|
||||
## Inserting runnable Rust files
|
||||
|
||||
With the following syntax, you can insert runnable Rust files into your book:
|
||||
|
||||
```hbs
|
||||
\{{#playpen file.rs}}
|
||||
```
|
||||
|
||||
The path to the Rust file has to be relative from the current source file.
|
||||
|
||||
When play is clicked, the code snippet will be send to the [Rust Playpen] to be compiled and run. The result is send back and displayed directly underneath the code.
|
||||
|
||||
Here is what a rendered code snippet looks like:
|
||||
|
||||
{{#playpen example.rs}}
|
||||
|
||||
[Rust Playpen]: https://play.rust-lang.org/
|
||||
30
src/bin/clean.rs
Normal file
30
src/bin/clean.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use clap::{App, ArgMatches, SubCommand};
|
||||
use mdbook::MDBook;
|
||||
use mdbook::errors::*;
|
||||
use get_book_dir;
|
||||
|
||||
// Create clap subcommand arguments
|
||||
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
|
||||
SubCommand::with_name("clean")
|
||||
.about("Delete built book")
|
||||
.arg_from_usage(
|
||||
"-d, --dest-dir=[dest-dir] 'The directory of built book{n}(Defaults to ./book when \
|
||||
omitted)'",
|
||||
)
|
||||
}
|
||||
|
||||
// Clean command implementation
|
||||
pub fn execute(args: &ArgMatches) -> ::mdbook::errors::Result<()> {
|
||||
let book_dir = get_book_dir(args);
|
||||
let book = MDBook::load(&book_dir)?;
|
||||
|
||||
let dir_to_remove = match args.value_of("dest-dir") {
|
||||
Some(dest_dir) => PathBuf::from(dest_dir),
|
||||
None => book.root.join(&book.config.build.build_dir),
|
||||
};
|
||||
fs::remove_dir_all(&dir_to_remove).chain_err(|| "Unable to remove the build directory")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -19,6 +19,7 @@ use env_logger::Builder;
|
||||
use mdbook::utils;
|
||||
|
||||
pub mod build;
|
||||
pub mod clean;
|
||||
pub mod init;
|
||||
pub mod test;
|
||||
#[cfg(feature = "serve")]
|
||||
@@ -44,7 +45,8 @@ fn main() {
|
||||
at: https://github.com/rust-lang-nursery/mdBook")
|
||||
.subcommand(init::make_subcommand())
|
||||
.subcommand(build::make_subcommand())
|
||||
.subcommand(test::make_subcommand());
|
||||
.subcommand(test::make_subcommand())
|
||||
.subcommand(clean::make_subcommand());
|
||||
|
||||
#[cfg(feature = "watch")]
|
||||
let app = app.subcommand(watch::make_subcommand());
|
||||
@@ -55,6 +57,7 @@ fn main() {
|
||||
let res = match app.get_matches().subcommand() {
|
||||
("init", Some(sub_matches)) => init::execute(sub_matches),
|
||||
("build", Some(sub_matches)) => build::execute(sub_matches),
|
||||
("clean", Some(sub_matches)) => clean::execute(sub_matches),
|
||||
#[cfg(feature = "watch")]
|
||||
("watch", Some(sub_matches)) => watch::execute(sub_matches),
|
||||
#[cfg(feature = "serve")]
|
||||
|
||||
@@ -10,7 +10,7 @@ use book::{Book, BookItem};
|
||||
|
||||
const ESCAPE_CHAR: char = '\\';
|
||||
|
||||
/// A preprocessor for expanding the `{{# playpen}}` and `{{# include}}`
|
||||
/// A preprocessor for expanding the `{{# playpen}}` and `{{# include}}`
|
||||
/// helpers in a chapter.
|
||||
pub struct LinkPreprocessor;
|
||||
|
||||
@@ -87,8 +87,14 @@ enum LinkType<'a> {
|
||||
fn parse_include_path(path: &str) -> LinkType<'static> {
|
||||
let mut parts = path.split(':');
|
||||
let path = parts.next().unwrap().into();
|
||||
let start = parts.next().and_then(|s| s.parse::<usize>().ok());
|
||||
let end = parts.next().and_then(|s| s.parse::<usize>().ok());
|
||||
// subtract 1 since line numbers usually begin with 1
|
||||
let start = parts
|
||||
.next()
|
||||
.and_then(|s| s.parse::<usize>().ok())
|
||||
.map(|val| val.checked_sub(1).unwrap_or(0));
|
||||
let end = parts.next();
|
||||
let has_end = end.is_some();
|
||||
let end = end.and_then(|s| s.parse::<usize>().ok());
|
||||
match start {
|
||||
Some(start) => match end {
|
||||
Some(end) => LinkType::IncludeRange(
|
||||
@@ -98,7 +104,17 @@ fn parse_include_path(path: &str) -> LinkType<'static> {
|
||||
end: end,
|
||||
},
|
||||
),
|
||||
None => LinkType::IncludeRangeFrom(path, RangeFrom { start: start }),
|
||||
None => if has_end {
|
||||
LinkType::IncludeRangeFrom(path, RangeFrom { start: start })
|
||||
} else {
|
||||
LinkType::IncludeRange(
|
||||
path,
|
||||
Range {
|
||||
start: start,
|
||||
end: start + 1,
|
||||
},
|
||||
)
|
||||
},
|
||||
},
|
||||
None => match end {
|
||||
Some(end) => LinkType::IncludeRangeTo(path, RangeTo { end: end }),
|
||||
@@ -276,13 +292,31 @@ mod tests {
|
||||
Link {
|
||||
start_index: 22,
|
||||
end_index: 48,
|
||||
link: LinkType::IncludeRange(PathBuf::from("file.rs"), 10..20),
|
||||
link: LinkType::IncludeRange(PathBuf::from("file.rs"), 9..20),
|
||||
link_text: "{{#include file.rs:10:20}}",
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_links_with_line_number() {
|
||||
let s = "Some random text with {{#include file.rs:10}}...";
|
||||
let res = find_links(s).collect::<Vec<_>>();
|
||||
println!("\nOUTPUT: {:?}\n", res);
|
||||
assert_eq!(
|
||||
res,
|
||||
vec![
|
||||
Link {
|
||||
start_index: 22,
|
||||
end_index: 45,
|
||||
link: LinkType::IncludeRange(PathBuf::from("file.rs"), 9..10),
|
||||
link_text: "{{#include file.rs:10}}",
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_links_with_from_range() {
|
||||
let s = "Some random text with {{#include file.rs:10:}}...";
|
||||
@@ -294,7 +328,7 @@ mod tests {
|
||||
Link {
|
||||
start_index: 22,
|
||||
end_index: 46,
|
||||
link: LinkType::IncludeRangeFrom(PathBuf::from("file.rs"), 10..),
|
||||
link: LinkType::IncludeRangeFrom(PathBuf::from("file.rs"), 9..),
|
||||
link_text: "{{#include file.rs:10:}}",
|
||||
},
|
||||
]
|
||||
|
||||
@@ -238,12 +238,13 @@ impl HtmlHandlebars {
|
||||
|
||||
/// Copy across any additional CSS and JavaScript files which the book
|
||||
/// has been configured to use.
|
||||
fn copy_additional_css_and_js(&self, html: &HtmlConfig, destination: &Path) -> Result<()> {
|
||||
fn copy_additional_css_and_js(&self, html: &HtmlConfig, root: &Path, destination: &Path) -> Result<()> {
|
||||
let custom_files = html.additional_css.iter().chain(html.additional_js.iter());
|
||||
|
||||
debug!("Copying additional CSS and JS");
|
||||
|
||||
for custom_file in custom_files {
|
||||
let input_location = root.join(custom_file);
|
||||
let output_location = destination.join(custom_file);
|
||||
if let Some(parent) = output_location.parent() {
|
||||
fs::create_dir_all(parent)
|
||||
@@ -251,14 +252,14 @@ impl HtmlHandlebars {
|
||||
}
|
||||
debug!(
|
||||
"Copying {} -> {}",
|
||||
custom_file.display(),
|
||||
input_location.display(),
|
||||
output_location.display()
|
||||
);
|
||||
|
||||
fs::copy(custom_file, &output_location).chain_err(|| {
|
||||
fs::copy(&input_location, &output_location).chain_err(|| {
|
||||
format!(
|
||||
"Unable to copy {} to {}",
|
||||
custom_file.display(),
|
||||
input_location.display(),
|
||||
output_location.display()
|
||||
)
|
||||
})?;
|
||||
@@ -338,7 +339,7 @@ impl Renderer for HtmlHandlebars {
|
||||
debug!("Copy static files");
|
||||
self.copy_static_files(&destination, &theme, &html_config)
|
||||
.chain_err(|| "Unable to copy across static files")?;
|
||||
self.copy_additional_css_and_js(&html_config, &destination)
|
||||
self.copy_additional_css_and_js(&html_config, &ctx.root, &destination)
|
||||
.chain_err(|| "Unable to copy across additional CSS and JS")?;
|
||||
|
||||
// Copy all remaining files
|
||||
@@ -375,11 +376,11 @@ fn make_data(root: &Path, book: &Book, config: &Config, html_config: &HtmlConfig
|
||||
let mut css = Vec::new();
|
||||
for style in &html.additional_css {
|
||||
match style.strip_prefix(root) {
|
||||
Ok(p) => css.push(p.to_str().expect("Could not convert to str")),
|
||||
Ok(p) => {
|
||||
css.push(p.to_str().expect("Could not convert to str"))
|
||||
},
|
||||
Err(_) => {
|
||||
css.push(style.file_name()
|
||||
.expect("File has a file name")
|
||||
.to_str()
|
||||
css.push(style.to_str()
|
||||
.expect("Could not convert to str"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,17 @@ table td {
|
||||
table thead td {
|
||||
font-weight: 700;
|
||||
}
|
||||
:not(.footnote-definition) + .footnote-definition,
|
||||
.footnote-definition + :not(.footnote-definition) {
|
||||
margin-top: 2em;
|
||||
}
|
||||
.footnote-definition {
|
||||
font-size: 0.9em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.footnote-definition p {
|
||||
display: inline;
|
||||
}
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
@@ -60,6 +71,7 @@ table thead td {
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overscroll-behavior-y: contain;
|
||||
-webkit-transition: -webkit-transform 0.5s;
|
||||
-moz-transition: -moz-transform 0.5s;
|
||||
-o-transition: -o-transform 0.5s;
|
||||
@@ -83,16 +95,26 @@ table thead td {
|
||||
}
|
||||
.chapter li a {
|
||||
display: block;
|
||||
padding: 5px 0;
|
||||
padding: 0;
|
||||
text-decoration: none;
|
||||
}
|
||||
@media (-moz-touch-enabled: 1), (pointer: coarse) {
|
||||
.chapter li a {
|
||||
padding: 5px 0;
|
||||
}
|
||||
}
|
||||
.chapter li a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.chapter .spacer {
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
margin: 10px 0px;
|
||||
margin: 5px 0px;
|
||||
}
|
||||
@media (-moz-touch-enabled: 1), (pointer: coarse) {
|
||||
.chapter .spacer {
|
||||
margin: 10px 0;
|
||||
}
|
||||
}
|
||||
.section {
|
||||
list-style: none outside none;
|
||||
@@ -209,6 +231,7 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
|
||||
overflow: hidden;
|
||||
-o-text-overflow: ellipsis;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
}
|
||||
.nav-chapters {
|
||||
font-size: 2.5em;
|
||||
|
||||
@@ -172,7 +172,7 @@ function playpen_text(playpen) {
|
||||
|
||||
var buttons = document.createElement('div');
|
||||
buttons.className = 'buttons';
|
||||
buttons.innerHTML = "<i class=\"fa fa-expand\" title=\"Show hidden lines\"></i>";
|
||||
buttons.innerHTML = "<button class=\"fa fa-expand\" title=\"Show hidden lines\" aria-label=\"Show hidden lines\"></button>";
|
||||
|
||||
// add expand button
|
||||
pre_block.prepend(buttons);
|
||||
@@ -184,6 +184,7 @@ function playpen_text(playpen) {
|
||||
e.target.classList.remove('fa-expand');
|
||||
e.target.classList.add('fa-compress');
|
||||
e.target.title = 'Hide lines';
|
||||
e.target.setAttribute('aria-label', e.target.title);
|
||||
|
||||
Array.from(lines).forEach(function (line) {
|
||||
line.classList.remove('hidden');
|
||||
@@ -195,6 +196,7 @@ function playpen_text(playpen) {
|
||||
e.target.classList.remove('fa-compress');
|
||||
e.target.classList.add('fa-expand');
|
||||
e.target.title = 'Show hidden lines';
|
||||
e.target.setAttribute('aria-label', e.target.title);
|
||||
|
||||
Array.from(lines).forEach(function (line) {
|
||||
line.classList.remove('unhidden');
|
||||
@@ -217,6 +219,7 @@ function playpen_text(playpen) {
|
||||
var clipButton = document.createElement('button');
|
||||
clipButton.className = 'fa fa-copy clip-button';
|
||||
clipButton.title = 'Copy to clipboard';
|
||||
clipButton.setAttribute('aria-label', clipButton.title);
|
||||
clipButton.innerHTML = '<i class=\"tooltiptext\"></i>';
|
||||
|
||||
buttons.prepend(clipButton);
|
||||
@@ -237,11 +240,13 @@ function playpen_text(playpen) {
|
||||
runCodeButton.className = 'fa fa-play play-button';
|
||||
runCodeButton.hidden = true;
|
||||
runCodeButton.title = 'Run this code';
|
||||
runCodeButton.setAttribute('aria-label', runCodeButton.title);
|
||||
|
||||
var copyCodeClipboardButton = document.createElement('button');
|
||||
copyCodeClipboardButton.className = 'fa fa-copy clip-button';
|
||||
copyCodeClipboardButton.innerHTML = '<i class="tooltiptext"></i>';
|
||||
copyCodeClipboardButton.title = 'Copy to clipboard';
|
||||
copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title);
|
||||
|
||||
buttons.prepend(runCodeButton);
|
||||
buttons.prepend(copyCodeClipboardButton);
|
||||
@@ -255,6 +260,7 @@ function playpen_text(playpen) {
|
||||
var undoChangesButton = document.createElement('button');
|
||||
undoChangesButton.className = 'fa fa-history reset-button';
|
||||
undoChangesButton.title = 'Undo changes';
|
||||
undoChangesButton.setAttribute('aria-label', undoChangesButton.title);
|
||||
|
||||
buttons.prepend(undoChangesButton);
|
||||
|
||||
@@ -292,11 +298,13 @@ function playpen_text(playpen) {
|
||||
function showThemes() {
|
||||
themePopup.style.display = 'block';
|
||||
themeToggleButton.setAttribute('aria-expanded', true);
|
||||
themePopup.querySelector("button#" + document.body.className).focus();
|
||||
}
|
||||
|
||||
function hideThemes() {
|
||||
themePopup.style.display = 'none';
|
||||
themeToggleButton.setAttribute('aria-expanded', false);
|
||||
themeToggleButton.focus();
|
||||
}
|
||||
|
||||
function set_theme(theme) {
|
||||
@@ -363,19 +371,43 @@ function playpen_text(playpen) {
|
||||
set_theme(theme);
|
||||
});
|
||||
|
||||
// Hide theme selector popup when clicking outside of it
|
||||
document.addEventListener('click', function (event) {
|
||||
if (themePopup.style.display === 'block' && !themeToggleButton.contains(event.target) && !themePopup.contains(event.target)) {
|
||||
themePopup.addEventListener('focusout', function(e) {
|
||||
if (!themePopup.contains(e.relatedTarget)) {
|
||||
hideThemes();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
|
||||
if (!themePopup.contains(e.target)) { return; }
|
||||
|
||||
switch (e.key) {
|
||||
case 'Escape':
|
||||
e.preventDefault();
|
||||
hideThemes();
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
var li = document.activeElement.parentElement;
|
||||
if (li && li.previousElementSibling) {
|
||||
li.previousElementSibling.querySelector('button').focus();
|
||||
}
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
var li = document.activeElement.parentElement;
|
||||
if (li && li.nextElementSibling) {
|
||||
li.nextElementSibling.querySelector('button').focus();
|
||||
}
|
||||
break;
|
||||
case 'Home':
|
||||
e.preventDefault();
|
||||
themePopup.querySelector('li:first-child button').focus();
|
||||
break;
|
||||
case 'End':
|
||||
e.preventDefault();
|
||||
themePopup.querySelector('li:last-child button').focus();
|
||||
break;
|
||||
}
|
||||
});
|
||||
})();
|
||||
@@ -516,6 +548,14 @@ function playpen_text(playpen) {
|
||||
});
|
||||
})();
|
||||
|
||||
(function scrollToTop () {
|
||||
var menuTitle = document.querySelector('.menu-title');
|
||||
|
||||
menuTitle.addEventListener('click', function () {
|
||||
document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
});
|
||||
})();
|
||||
|
||||
(function autoHideMenu() {
|
||||
var menu = document.getElementById('menu-bar');
|
||||
|
||||
|
||||
@@ -89,25 +89,25 @@
|
||||
<div id="menu-bar" class="menu-bar">
|
||||
<div id="menu-bar-sticky-container">
|
||||
<div class="left-buttons">
|
||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="submenu">
|
||||
<li><button class="theme" id="light">Light <span class="default">(default)</span></button></li>
|
||||
<li><button class="theme" id="rust">Rust</button></li>
|
||||
<li><button class="theme" id="coal">Coal</button></li>
|
||||
<li><button class="theme" id="navy">Navy</button></li>
|
||||
<li><button class="theme" id="ayu">Ayu</button></li>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light <span class="default">(default)</span></button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">{{ book_title }}</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="print.html" title="Print this book">
|
||||
<a href="print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
</div>
|
||||
@@ -131,13 +131,13 @@
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
{{#previous}}
|
||||
<a rel="prev" href="{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
{{/previous}}
|
||||
|
||||
{{#next}}
|
||||
<a rel="next" href="{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next" href="{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
{{/next}}
|
||||
@@ -149,13 +149,13 @@
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
{{#previous}}
|
||||
<a href="{{link}}" class="nav-chapters previous" title="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a href="{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
{{/previous}}
|
||||
|
||||
{{#next}}
|
||||
<a href="{{link}}" class="nav-chapters next" title="Next chapter" aria-keyshortcuts="Right">
|
||||
<a href="{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
{{/next}}
|
||||
|
||||
@@ -48,3 +48,15 @@ table {
|
||||
td { font-weight: 700; }
|
||||
}
|
||||
}
|
||||
|
||||
:not(.footnote-definition) + .footnote-definition,
|
||||
.footnote-definition + :not(.footnote-definition) {
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
.footnote-definition {
|
||||
font-size: 0.9em;
|
||||
margin: 0.5em 0;
|
||||
|
||||
p { display: inline; }
|
||||
}
|
||||
|
||||
@@ -38,4 +38,5 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
|
||||
white-space: nowrap
|
||||
overflow: hidden
|
||||
text-overflow: ellipsis
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
font-size: 0.875em
|
||||
box-sizing: border-box
|
||||
-webkit-overflow-scrolling: touch
|
||||
overscroll-behavior-y: contain;
|
||||
|
||||
// Animation: slide away
|
||||
transition: transform 0.5s
|
||||
@@ -31,16 +32,18 @@
|
||||
|
||||
li a {
|
||||
display: block;
|
||||
padding: 5px 0
|
||||
padding: 0
|
||||
text-decoration: none
|
||||
|
||||
@media (-moz-touch-enabled: 1), (pointer: coarse) { padding: 5px 0; }
|
||||
&:hover { text-decoration: none }
|
||||
}
|
||||
|
||||
.spacer {
|
||||
width: 100%
|
||||
height: 3px
|
||||
margin: 10px 0px
|
||||
margin: 5px 0px
|
||||
@media (-moz-touch-enabled: 1), (pointer: coarse) { margin: 10px 0; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ pub fn take_lines<R: RangeArgument<usize>>(s: &str, range: R) -> String {
|
||||
let start = *range.start().unwrap_or(&0);
|
||||
let mut lines = s.lines().skip(start);
|
||||
match range.end() {
|
||||
Some(&end) => lines.take(end).join("\n"),
|
||||
Some(&end) => lines.take(end.checked_sub(start).unwrap_or(0)).join("\n"),
|
||||
None => lines.join("\n"),
|
||||
}
|
||||
}
|
||||
@@ -62,9 +62,12 @@ mod tests {
|
||||
#[test]
|
||||
fn take_lines_test() {
|
||||
let s = "Lorem\nipsum\ndolor\nsit\namet";
|
||||
assert_eq!(take_lines(s, 0..3), "Lorem\nipsum\ndolor");
|
||||
assert_eq!(take_lines(s, 1..3), "ipsum\ndolor");
|
||||
assert_eq!(take_lines(s, 3..), "sit\namet");
|
||||
assert_eq!(take_lines(s, ..3), "Lorem\nipsum\ndolor");
|
||||
assert_eq!(take_lines(s, ..), s);
|
||||
// corner cases
|
||||
assert_eq!(take_lines(s, 4..3), "");
|
||||
assert_eq!(take_lines(s, ..100), s);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user