Use relative links and translate internal references (#603)
* Relative links for 0.1.8 * Compat for IE11 search
This commit is contained in:
parent
01656b610f
commit
bdb37ec117
|
@ -22,7 +22,7 @@ configuration files, etc.
|
||||||
- The `book` directory is where your book is rendered. All the output is ready to be uploaded
|
- The `book` directory is where your book is rendered. All the output is ready to be uploaded
|
||||||
to a server to be seen by your audience.
|
to a server to be seen by your audience.
|
||||||
|
|
||||||
- The `SUMMARY.md` file is the most important file, it's the skeleton of your book and is discussed in more detail in another [chapter](format/summary.html).
|
- The `SUMMARY.md` file is the most important file, it's the skeleton of your book and is discussed in more detail in another [chapter](../format/summary.md)
|
||||||
|
|
||||||
#### Tip & Trick: Hidden Feature
|
#### Tip & Trick: Hidden Feature
|
||||||
When a `SUMMARY.md` file already exists, the `init` command will first parse it and generate the missing files according to the paths used in the `SUMMARY.md`. This allows you to think and create the whole structure of your book and then let mdBook generate it for you.
|
When a `SUMMARY.md` file already exists, the `init` command will first parse it and generate the missing files according to the paths used in the `SUMMARY.md`. This allows you to think and create the whole structure of your book and then let mdBook generate it for you.
|
||||||
|
|
|
@ -11,8 +11,8 @@ The *For Developers* chapters are here to show you the more advanced usage of
|
||||||
|
|
||||||
The two main ways a developer can hook into the book's build process is via,
|
The two main ways a developer can hook into the book's build process is via,
|
||||||
|
|
||||||
- [Preprocessors](for_developers/preprocessors.html)
|
- [Preprocessors](preprocessors.md)
|
||||||
- [Alternate Backends](for_developers/backends.html)
|
- [Alternate Backends](backends.md)
|
||||||
|
|
||||||
|
|
||||||
## The Build Process
|
## The Build Process
|
||||||
|
|
|
@ -42,10 +42,6 @@ impl HtmlHandlebars {
|
||||||
.to_str()
|
.to_str()
|
||||||
.chain_err(|| "Could not convert path to str")?;
|
.chain_err(|| "Could not convert path to str")?;
|
||||||
let filepath = Path::new(&ch.path).with_extension("html");
|
let filepath = Path::new(&ch.path).with_extension("html");
|
||||||
let filepathstr = filepath
|
|
||||||
.to_str()
|
|
||||||
.chain_err(|| "Could not convert HTML path to str")?;
|
|
||||||
let filepathstr = utils::fs::normalize_path(filepathstr);
|
|
||||||
|
|
||||||
// "print.html" is used for the print page.
|
// "print.html" is used for the print page.
|
||||||
if ch.path == Path::new("print.md") {
|
if ch.path == Path::new("print.md") {
|
||||||
|
@ -75,10 +71,10 @@ impl HtmlHandlebars {
|
||||||
debug!("Render template");
|
debug!("Render template");
|
||||||
let rendered = ctx.handlebars.render("index", &ctx.data)?;
|
let rendered = ctx.handlebars.render("index", &ctx.data)?;
|
||||||
|
|
||||||
let rendered = self.post_process(rendered, &filepathstr, &ctx.html_config.playpen);
|
let rendered = self.post_process(rendered, &ctx.html_config.playpen);
|
||||||
|
|
||||||
// Write to file
|
// Write to file
|
||||||
debug!("Creating {} ✓", filepathstr);
|
debug!("Creating {} ✓", filepath.display());
|
||||||
utils::fs::write_file(&ctx.destination, &filepath, &rendered.into_bytes())?;
|
utils::fs::write_file(&ctx.destination, &filepath, &rendered.into_bytes())?;
|
||||||
|
|
||||||
if ctx.is_index {
|
if ctx.is_index {
|
||||||
|
@ -120,9 +116,8 @@ impl HtmlHandlebars {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "cargo-clippy", allow(let_and_return))]
|
#[cfg_attr(feature = "cargo-clippy", allow(let_and_return))]
|
||||||
fn post_process(&self, rendered: String, filepath: &str, playpen_config: &Playpen) -> String {
|
fn post_process(&self, rendered: String, playpen_config: &Playpen) -> String {
|
||||||
let rendered = build_header_links(&rendered, filepath);
|
let rendered = build_header_links(&rendered);
|
||||||
let rendered = fix_anchor_links(&rendered, filepath);
|
|
||||||
let rendered = fix_code_blocks(&rendered);
|
let rendered = fix_code_blocks(&rendered);
|
||||||
let rendered = add_playpen_pre(&rendered, playpen_config);
|
let rendered = add_playpen_pre(&rendered, playpen_config);
|
||||||
|
|
||||||
|
@ -360,7 +355,7 @@ impl Renderer for HtmlHandlebars {
|
||||||
debug!("Render template");
|
debug!("Render template");
|
||||||
let rendered = handlebars.render("index", &data)?;
|
let rendered = handlebars.render("index", &data)?;
|
||||||
|
|
||||||
let rendered = self.post_process(rendered, "print.html", &html_config.playpen);
|
let rendered = self.post_process(rendered, &html_config.playpen);
|
||||||
|
|
||||||
utils::fs::write_file(&destination, "print.html", &rendered.into_bytes())?;
|
utils::fs::write_file(&destination, "print.html", &rendered.into_bytes())?;
|
||||||
debug!("Creating print.html ✓");
|
debug!("Creating print.html ✓");
|
||||||
|
@ -497,7 +492,7 @@ fn make_data(
|
||||||
|
|
||||||
/// Goes through the rendered HTML, making sure all header tags are wrapped in
|
/// Goes through the rendered HTML, making sure all header tags are wrapped in
|
||||||
/// an anchor so people can link to sections directly.
|
/// an anchor so people can link to sections directly.
|
||||||
fn build_header_links(html: &str, filepath: &str) -> String {
|
fn build_header_links(html: &str) -> String {
|
||||||
let regex = Regex::new(r"<h(\d)>(.*?)</h\d>").unwrap();
|
let regex = Regex::new(r"<h(\d)>(.*?)</h\d>").unwrap();
|
||||||
let mut id_counter = HashMap::new();
|
let mut id_counter = HashMap::new();
|
||||||
|
|
||||||
|
@ -507,7 +502,7 @@ fn build_header_links(html: &str, filepath: &str) -> String {
|
||||||
.parse()
|
.parse()
|
||||||
.expect("Regex should ensure we only ever get numbers here");
|
.expect("Regex should ensure we only ever get numbers here");
|
||||||
|
|
||||||
wrap_header_with_link(level, &caps[2], &mut id_counter, filepath)
|
wrap_header_with_link(level, &caps[2], &mut id_counter)
|
||||||
})
|
})
|
||||||
.into_owned()
|
.into_owned()
|
||||||
}
|
}
|
||||||
|
@ -517,8 +512,7 @@ fn build_header_links(html: &str, filepath: &str) -> String {
|
||||||
fn wrap_header_with_link(
|
fn wrap_header_with_link(
|
||||||
level: usize,
|
level: usize,
|
||||||
content: &str,
|
content: &str,
|
||||||
id_counter: &mut HashMap<String, usize>,
|
id_counter: &mut HashMap<String, usize>
|
||||||
filepath: &str,
|
|
||||||
) -> String {
|
) -> String {
|
||||||
let raw_id = utils::id_from_content(content);
|
let raw_id = utils::id_from_content(content);
|
||||||
|
|
||||||
|
@ -532,35 +526,13 @@ fn wrap_header_with_link(
|
||||||
*id_count += 1;
|
*id_count += 1;
|
||||||
|
|
||||||
format!(
|
format!(
|
||||||
r##"<a class="header" href="{filepath}#{id}" id="{id}"><h{level}>{text}</h{level}></a>"##,
|
r##"<a class="header" href="#{id}" id="{id}"><h{level}>{text}</h{level}></a>"##,
|
||||||
level = level,
|
level = level,
|
||||||
id = id,
|
id = id,
|
||||||
text = content,
|
text = content
|
||||||
filepath = filepath
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// anchors to the same page (href="#anchor") do not work because of
|
|
||||||
// <base href="../"> pointing to the root folder. This function *fixes*
|
|
||||||
// that in a very inelegant way
|
|
||||||
fn fix_anchor_links(html: &str, filepath: &str) -> String {
|
|
||||||
let regex = Regex::new(r##"<a([^>]+)href="#([^"]+)"([^>]*)>"##).unwrap();
|
|
||||||
regex
|
|
||||||
.replace_all(html, |caps: &Captures| {
|
|
||||||
let before = &caps[1];
|
|
||||||
let anchor = &caps[2];
|
|
||||||
let after = &caps[3];
|
|
||||||
|
|
||||||
format!(
|
|
||||||
"<a{before}href=\"{filepath}#{anchor}\"{after}>",
|
|
||||||
before = before,
|
|
||||||
filepath = filepath,
|
|
||||||
anchor = anchor,
|
|
||||||
after = after
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.into_owned()
|
|
||||||
}
|
|
||||||
|
|
||||||
// The rust book uses annotations for rustdoc to test code snippets,
|
// The rust book uses annotations for rustdoc to test code snippets,
|
||||||
// like the following:
|
// like the following:
|
||||||
|
@ -660,37 +632,32 @@ mod tests {
|
||||||
let inputs = vec![
|
let inputs = vec![
|
||||||
(
|
(
|
||||||
"blah blah <h1>Foo</h1>",
|
"blah blah <h1>Foo</h1>",
|
||||||
r##"blah blah <a class="header" href="./some_chapter/some_section.html#foo" id="foo"><h1>Foo</h1></a>"##,
|
r##"blah blah <a class="header" href="#foo" id="foo"><h1>Foo</h1></a>"##,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"<h1>Foo</h1>",
|
"<h1>Foo</h1>",
|
||||||
r##"<a class="header" href="./some_chapter/some_section.html#foo" id="foo"><h1>Foo</h1></a>"##,
|
r##"<a class="header" href="#foo" id="foo"><h1>Foo</h1></a>"##,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"<h3>Foo^bar</h3>",
|
"<h3>Foo^bar</h3>",
|
||||||
r##"<a class="header" href="./some_chapter/some_section.html#foobar" id="foobar"><h3>Foo^bar</h3></a>"##,
|
r##"<a class="header" href="#foobar" id="foobar"><h3>Foo^bar</h3></a>"##,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"<h4></h4>",
|
"<h4></h4>",
|
||||||
r##"<a class="header" href="./some_chapter/some_section.html#" id=""><h4></h4></a>"##,
|
r##"<a class="header" href="#" id=""><h4></h4></a>"##,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"<h4><em>Hï</em></h4>",
|
"<h4><em>Hï</em></h4>",
|
||||||
r##"<a class="header" href="./some_chapter/some_section.html#hï" id="hï"><h4><em>Hï</em></h4></a>"##,
|
r##"<a class="header" href="#hï" id="hï"><h4><em>Hï</em></h4></a>"##,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"<h1>Foo</h1><h3>Foo</h3>",
|
"<h1>Foo</h1><h3>Foo</h3>",
|
||||||
r##"<a class="header" href="./some_chapter/some_section.html#foo" id="foo"><h1>Foo</h1></a><a class="header" href="./some_chapter/some_section.html#foo-1" id="foo-1"><h3>Foo</h3></a>"##,
|
r##"<a class="header" href="#foo" id="foo"><h1>Foo</h1></a><a class="header" href="#foo-1" id="foo-1"><h3>Foo</h3></a>"##,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (src, should_be) in inputs {
|
for (src, should_be) in inputs {
|
||||||
let filepath = "./some_chapter/some_section.html";
|
let got = build_header_links(&src);
|
||||||
let got = build_header_links(&src, filepath);
|
|
||||||
assert_eq!(got, should_be);
|
|
||||||
|
|
||||||
// This is redundant for most cases
|
|
||||||
let got = fix_anchor_links(&got, filepath);
|
|
||||||
assert_eq!(got, should_be);
|
assert_eq!(got, should_be);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ use std::collections::BTreeMap;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use handlebars::{Context, Handlebars, Helper, RenderContext, RenderError, Renderable};
|
use handlebars::{Context, Handlebars, Helper, RenderContext, RenderError, Renderable};
|
||||||
|
|
||||||
|
use utils;
|
||||||
|
|
||||||
type StringMap = BTreeMap<String, String>;
|
type StringMap = BTreeMap<String, String>;
|
||||||
|
|
||||||
/// Target for `find_chapter`.
|
/// Target for `find_chapter`.
|
||||||
|
@ -87,6 +89,13 @@ fn render(
|
||||||
trace!("Creating BTreeMap to inject in context");
|
trace!("Creating BTreeMap to inject in context");
|
||||||
|
|
||||||
let mut context = BTreeMap::new();
|
let mut context = BTreeMap::new();
|
||||||
|
let base_path = rc.evaluate_absolute("path", false)?
|
||||||
|
.as_str()
|
||||||
|
.ok_or_else(|| RenderError::new("Type error for `path`, string expected"))?
|
||||||
|
.replace("\"", "");
|
||||||
|
|
||||||
|
context.insert("path_to_root".to_owned(),
|
||||||
|
json!(utils::fs::path_to_root(&base_path)));
|
||||||
|
|
||||||
chapter
|
chapter
|
||||||
.get("name")
|
.get("name")
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use utils;
|
||||||
|
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use handlebars::{Handlebars, Helper, HelperDef, RenderContext, RenderError};
|
use handlebars::{Handlebars, Helper, HelperDef, RenderContext, RenderError};
|
||||||
use pulldown_cmark::{html, Event, Parser, Tag};
|
use pulldown_cmark::{html, Event, Parser, Tag};
|
||||||
|
@ -77,6 +79,7 @@ impl HelperDef for RenderToc {
|
||||||
.replace("\\", "/");
|
.replace("\\", "/");
|
||||||
|
|
||||||
// Add link
|
// Add link
|
||||||
|
rc.writer.write_all(&utils::fs::path_to_root(¤t).as_bytes())?;
|
||||||
rc.writer.write_all(tmp.as_bytes())?;
|
rc.writer.write_all(tmp.as_bytes())?;
|
||||||
rc.writer.write_all(b"\"")?;
|
rc.writer.write_all(b"\"")?;
|
||||||
|
|
||||||
|
|
|
@ -293,9 +293,9 @@ function playpen_text(playpen) {
|
||||||
var themePopup = document.getElementById('theme-list');
|
var themePopup = document.getElementById('theme-list');
|
||||||
var themeColorMetaTag = document.querySelector('meta[name="theme-color"]');
|
var themeColorMetaTag = document.querySelector('meta[name="theme-color"]');
|
||||||
var stylesheets = {
|
var stylesheets = {
|
||||||
ayuHighlight: document.querySelector("[href='ayu-highlight.css']"),
|
ayuHighlight: document.querySelector("[href$='ayu-highlight.css']"),
|
||||||
tomorrowNight: document.querySelector("[href='tomorrow-night.css']"),
|
tomorrowNight: document.querySelector("[href$='tomorrow-night.css']"),
|
||||||
highlight: document.querySelector("[href='highlight.css']"),
|
highlight: document.querySelector("[href$='highlight.css']"),
|
||||||
};
|
};
|
||||||
|
|
||||||
function showThemes() {
|
function showThemes() {
|
||||||
|
|
|
@ -9,20 +9,18 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="theme-color" content="#ffffff" />
|
<meta name="theme-color" content="#ffffff" />
|
||||||
|
|
||||||
<base href="{{ path_to_root }}">
|
<link rel="stylesheet" href="{{ path_to_root }}book.css">
|
||||||
|
|
||||||
<link rel="stylesheet" href="book.css">
|
|
||||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
|
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
|
||||||
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
|
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
|
||||||
|
|
||||||
<link rel="shortcut icon" href="{{ favicon }}">
|
<link rel="shortcut icon" href="{{ favicon }}">
|
||||||
|
|
||||||
<!-- Font Awesome -->
|
<!-- Font Awesome -->
|
||||||
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
|
<link rel="stylesheet" href="{{ path_to_root }}FontAwesome/css/font-awesome.css">
|
||||||
|
|
||||||
<link rel="stylesheet" href="highlight.css">
|
<link rel="stylesheet" href="{{ path_to_root }}highlight.css">
|
||||||
<link rel="stylesheet" href="tomorrow-night.css">
|
<link rel="stylesheet" href="{{ path_to_root }}tomorrow-night.css">
|
||||||
<link rel="stylesheet" href="ayu-highlight.css">
|
<link rel="stylesheet" href="{{ path_to_root }}ayu-highlight.css">
|
||||||
|
|
||||||
<!-- Custom theme stylesheets -->
|
<!-- Custom theme stylesheets -->
|
||||||
{{#each additional_css}}
|
{{#each additional_css}}
|
||||||
|
@ -107,7 +105,7 @@
|
||||||
<h1 class="menu-title">{{ book_title }}</h1>
|
<h1 class="menu-title">{{ book_title }}</h1>
|
||||||
|
|
||||||
<div class="right-buttons">
|
<div class="right-buttons">
|
||||||
<a href="print.html" title="Print this book" aria-label="Print this book">
|
<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
|
||||||
<i id="print-button" class="fa fa-print"></i>
|
<i id="print-button" class="fa fa-print"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -144,13 +142,13 @@
|
||||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
<!-- Mobile navigation buttons -->
|
<!-- Mobile navigation buttons -->
|
||||||
{{#previous}}
|
{{#previous}}
|
||||||
<a rel="prev" href="{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
{{/previous}}
|
{{/previous}}
|
||||||
|
|
||||||
{{#next}}
|
{{#next}}
|
||||||
<a rel="next" href="{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a rel="next" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
{{/next}}
|
{{/next}}
|
||||||
|
@ -162,13 +160,13 @@
|
||||||
|
|
||||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
{{#previous}}
|
{{#previous}}
|
||||||
<a href="{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
{{/previous}}
|
{{/previous}}
|
||||||
|
|
||||||
{{#next}}
|
{{#next}}
|
||||||
<a href="{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
{{/next}}
|
{{/next}}
|
||||||
|
@ -213,29 +211,32 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if playpen_js}}
|
{{#if playpen_js}}
|
||||||
<script src="ace.js" type="text/javascript" charset="utf-8"></script>
|
<script src="{{ path_to_root }}ace.js" type="text/javascript" charset="utf-8"></script>
|
||||||
<script src="editor.js" type="text/javascript" charset="utf-8"></script>
|
<script src="{{ path_to_root }}editor.js" type="text/javascript" charset="utf-8"></script>
|
||||||
<script src="mode-rust.js" type="text/javascript" charset="utf-8"></script>
|
<script src="{{ path_to_root }}mode-rust.js" type="text/javascript" charset="utf-8"></script>
|
||||||
<script src="theme-dawn.js" type="text/javascript" charset="utf-8"></script>
|
<script src="{{ path_to_root }}theme-dawn.js" type="text/javascript" charset="utf-8"></script>
|
||||||
<script src="theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
|
<script src="{{ path_to_root }}theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if search_enabled}}
|
{{#if search_enabled}}
|
||||||
<script src="searchindex.js" type="text/javascript" charset="utf-8"></script>
|
<script src="{{ path_to_root }}searchindex.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script>
|
||||||
|
var path_to_root = "{{path_to_root}}";
|
||||||
|
</script>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if search_js}}
|
{{#if search_js}}
|
||||||
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
<script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
|
<script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
|
<script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
<script src="{{ path_to_root }}clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
|
<script src="{{ path_to_root }}highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||||
<script src="book.js" type="text/javascript" charset="utf-8"></script>
|
<script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
<!-- Custom JS scripts -->
|
<!-- Custom JS scripts -->
|
||||||
{{#each additional_js}}
|
{{#each additional_js}}
|
||||||
<script type="text/javascript" src="{{this}}"></script>
|
<script type="text/javascript" src="{{ path_to_root }}{{this}}"></script>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
||||||
{{#if is_print}}
|
{{#if is_print}}
|
||||||
|
|
|
@ -10,6 +10,13 @@ window.search = window.search || {};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//IE 11 Compatibility from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith
|
||||||
|
if (!String.prototype.startsWith) {
|
||||||
|
String.prototype.startsWith = function(search, pos) {
|
||||||
|
return this.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
var search_wrap = document.getElementById('search-wrapper'),
|
var search_wrap = document.getElementById('search-wrapper'),
|
||||||
searchbar = document.getElementById('searchbar'),
|
searchbar = document.getElementById('searchbar'),
|
||||||
searchbar_outer = document.getElementById('searchbar-outer'),
|
searchbar_outer = document.getElementById('searchbar-outer'),
|
||||||
|
@ -137,7 +144,7 @@ window.search = window.search || {};
|
||||||
url.push("");
|
url.push("");
|
||||||
}
|
}
|
||||||
|
|
||||||
return '<a href="' + url[0] + '?' + URL_MARK_PARAM + '=' + searchterms + '#' + url[1]
|
return '<a href="' + path_to_root + url[0] + '?' + URL_MARK_PARAM + '=' + searchterms + '#' + url[1]
|
||||||
+ '" aria-details="teaser_' + teaser_count + '">' + result.doc.breadcrumbs + '</a>'
|
+ '" aria-details="teaser_' + teaser_count + '">' + result.doc.breadcrumbs + '</a>'
|
||||||
+ '<span class="teaser" id="teaser_' + teaser_count + '" aria-label="Search Result Teaser">'
|
+ '<span class="teaser" id="teaser_' + teaser_count + '" aria-label="Search Result Teaser">'
|
||||||
+ teaser + '</span>';
|
+ teaser + '</span>';
|
||||||
|
|
|
@ -68,6 +68,35 @@ pub fn id_from_content(content: &str) -> String {
|
||||||
normalize_id(trimmed)
|
normalize_id(trimmed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn adjust_links(event: Event) -> Event {
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref HTTP_LINK: Regex = Regex::new("^https?://").unwrap();
|
||||||
|
static ref MD_LINK: Regex = Regex::new("(?P<link>.*).md(?P<anchor>#.*)?").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
match event {
|
||||||
|
Event::Start(Tag::Link(dest, title)) => {
|
||||||
|
if !HTTP_LINK.is_match(&dest) {
|
||||||
|
if let Some(caps) = MD_LINK.captures(&dest) {
|
||||||
|
|
||||||
|
let mut html_link = [&caps["link"], ".html"].concat();
|
||||||
|
|
||||||
|
if let Some(anchor) = caps.name("anchor") {
|
||||||
|
html_link.push_str(anchor.as_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Event::Start(Tag::Link(Cow::from(html_link), title))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Event::Start(Tag::Link(dest, title))
|
||||||
|
},
|
||||||
|
_ => event
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// Wrapper around the pulldown-cmark parser for rendering markdown to HTML.
|
/// Wrapper around the pulldown-cmark parser for rendering markdown to HTML.
|
||||||
pub fn render_markdown(text: &str, curly_quotes: bool) -> String {
|
pub fn render_markdown(text: &str, curly_quotes: bool) -> String {
|
||||||
let mut s = String::with_capacity(text.len() * 3 / 2);
|
let mut s = String::with_capacity(text.len() * 3 / 2);
|
||||||
|
@ -79,6 +108,7 @@ pub fn render_markdown(text: &str, curly_quotes: bool) -> String {
|
||||||
let p = Parser::new_ext(text, opts);
|
let p = Parser::new_ext(text, opts);
|
||||||
let mut converter = EventQuoteConverter::new(curly_quotes);
|
let mut converter = EventQuoteConverter::new(curly_quotes);
|
||||||
let events = p.map(clean_codeblock_headers)
|
let events = p.map(clean_codeblock_headers)
|
||||||
|
.map(adjust_links)
|
||||||
.map(|event| converter.convert(event));
|
.map(|event| converter.convert(event));
|
||||||
|
|
||||||
html::push_html(&mut s, events);
|
html::push_html(&mut s, events);
|
||||||
|
@ -177,6 +207,17 @@ mod tests {
|
||||||
mod render_markdown {
|
mod render_markdown {
|
||||||
use super::super::render_markdown;
|
use super::super::render_markdown;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn preserves_external_links() {
|
||||||
|
assert_eq!(render_markdown("[example](https://www.rust-lang.org/)", false), "<p><a href=\"https://www.rust-lang.org/\">example</a></p>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_can_adjust_markdown_links() {
|
||||||
|
assert_eq!(render_markdown("[example](example.md)", false), "<p><a href=\"example.html\">example</a></p>\n");
|
||||||
|
assert_eq!(render_markdown("[example_anchor](example.md#anchor)", false), "<p><a href=\"example.html#anchor\">example_anchor</a></p>\n");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_can_keep_quotes_straight() {
|
fn it_can_keep_quotes_straight() {
|
||||||
assert_eq!(render_markdown("'one'", false), "<p>'one'</p>\n");
|
assert_eq!(render_markdown("'one'", false), "<p>'one'</p>\n");
|
||||||
|
|
|
@ -83,12 +83,11 @@ fn check_correct_cross_links_in_nested_dir() {
|
||||||
|
|
||||||
let first = temp.path().join("book").join("first");
|
let first = temp.path().join("book").join("first");
|
||||||
let links = vec![
|
let links = vec![
|
||||||
r#"<base href="../">"#,
|
r#"href="../intro.html""#,
|
||||||
r#"href="intro.html""#,
|
r#"href="../first/index.html""#,
|
||||||
r#"href="first/index.html""#,
|
r#"href="../first/nested.html""#,
|
||||||
r#"href="first/nested.html""#,
|
r#"href="../second.html""#,
|
||||||
r#"href="second.html""#,
|
r#"href="../conclusion.html""#,
|
||||||
r#"href="conclusion.html""#,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
let files_in_nested_dir = vec!["index.html", "nested.html"];
|
let files_in_nested_dir = vec!["index.html", "nested.html"];
|
||||||
|
@ -100,14 +99,14 @@ fn check_correct_cross_links_in_nested_dir() {
|
||||||
assert_contains_strings(
|
assert_contains_strings(
|
||||||
first.join("index.html"),
|
first.join("index.html"),
|
||||||
&[
|
&[
|
||||||
r##"href="first/index.html#some-section" id="some-section""##,
|
r##"href="#some-section" id="some-section""##,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_contains_strings(
|
assert_contains_strings(
|
||||||
first.join("nested.html"),
|
first.join("nested.html"),
|
||||||
&[
|
&[
|
||||||
r##"href="first/nested.html#some-section" id="some-section""##,
|
r##"href="#some-section" id="some-section""##,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -367,8 +366,8 @@ fn by_default_mdbook_use_index_preprocessor_to_convert_readme_to_index() {
|
||||||
.join("first")
|
.join("first")
|
||||||
.join("index.html");
|
.join("index.html");
|
||||||
let expected_strings = vec![
|
let expected_strings = vec![
|
||||||
r#"href="first/index.html""#,
|
r#"href="../first/index.html""#,
|
||||||
r#"href="second/index.html""#,
|
r#"href="../second/index.html""#,
|
||||||
"First README",
|
"First README",
|
||||||
];
|
];
|
||||||
assert_contains_strings(&first_index, &expected_strings);
|
assert_contains_strings(&first_index, &expected_strings);
|
||||||
|
|
Loading…
Reference in New Issue