Skip to content

Commit bdb37ec

Browse files
cetra3Michael-F-Bryan
authored andcommitted
Use relative links and translate internal references (#603)
* Relative links for 0.1.8 * Compat for IE11 search
1 parent 01656b6 commit bdb37ec

File tree

10 files changed

+120
-93
lines changed

10 files changed

+120
-93
lines changed

book-example/src/cli/init.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ configuration files, etc.
2222
- The `book` directory is where your book is rendered. All the output is ready to be uploaded
2323
to a server to be seen by your audience.
2424

25-
- 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).
25+
- 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)
2626

2727
#### Tip & Trick: Hidden Feature
2828
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.

book-example/src/for_developers/README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ The *For Developers* chapters are here to show you the more advanced usage of
1111

1212
The two main ways a developer can hook into the book's build process is via,
1313

14-
- [Preprocessors](for_developers/preprocessors.html)
15-
- [Alternate Backends](for_developers/backends.html)
14+
- [Preprocessors](preprocessors.md)
15+
- [Alternate Backends](backends.md)
1616

1717

1818
## The Build Process

src/renderer/html_handlebars/hbs_renderer.rs

+17-50
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,6 @@ impl HtmlHandlebars {
4242
.to_str()
4343
.chain_err(|| "Could not convert path to str")?;
4444
let filepath = Path::new(&ch.path).with_extension("html");
45-
let filepathstr = filepath
46-
.to_str()
47-
.chain_err(|| "Could not convert HTML path to str")?;
48-
let filepathstr = utils::fs::normalize_path(filepathstr);
4945

5046
// "print.html" is used for the print page.
5147
if ch.path == Path::new("print.md") {
@@ -75,10 +71,10 @@ impl HtmlHandlebars {
7571
debug!("Render template");
7672
let rendered = ctx.handlebars.render("index", &ctx.data)?;
7773

78-
let rendered = self.post_process(rendered, &filepathstr, &ctx.html_config.playpen);
74+
let rendered = self.post_process(rendered, &ctx.html_config.playpen);
7975

8076
// Write to file
81-
debug!("Creating {} ✓", filepathstr);
77+
debug!("Creating {} ✓", filepath.display());
8278
utils::fs::write_file(&ctx.destination, &filepath, &rendered.into_bytes())?;
8379

8480
if ctx.is_index {
@@ -120,9 +116,8 @@ impl HtmlHandlebars {
120116
}
121117

122118
#[cfg_attr(feature = "cargo-clippy", allow(let_and_return))]
123-
fn post_process(&self, rendered: String, filepath: &str, playpen_config: &Playpen) -> String {
124-
let rendered = build_header_links(&rendered, filepath);
125-
let rendered = fix_anchor_links(&rendered, filepath);
119+
fn post_process(&self, rendered: String, playpen_config: &Playpen) -> String {
120+
let rendered = build_header_links(&rendered);
126121
let rendered = fix_code_blocks(&rendered);
127122
let rendered = add_playpen_pre(&rendered, playpen_config);
128123

@@ -360,7 +355,7 @@ impl Renderer for HtmlHandlebars {
360355
debug!("Render template");
361356
let rendered = handlebars.render("index", &data)?;
362357

363-
let rendered = self.post_process(rendered, "print.html", &html_config.playpen);
358+
let rendered = self.post_process(rendered, &html_config.playpen);
364359

365360
utils::fs::write_file(&destination, "print.html", &rendered.into_bytes())?;
366361
debug!("Creating print.html ✓");
@@ -497,7 +492,7 @@ fn make_data(
497492

498493
/// Goes through the rendered HTML, making sure all header tags are wrapped in
499494
/// an anchor so people can link to sections directly.
500-
fn build_header_links(html: &str, filepath: &str) -> String {
495+
fn build_header_links(html: &str) -> String {
501496
let regex = Regex::new(r"<h(\d)>(.*?)</h\d>").unwrap();
502497
let mut id_counter = HashMap::new();
503498

@@ -507,7 +502,7 @@ fn build_header_links(html: &str, filepath: &str) -> String {
507502
.parse()
508503
.expect("Regex should ensure we only ever get numbers here");
509504

510-
wrap_header_with_link(level, &caps[2], &mut id_counter, filepath)
505+
wrap_header_with_link(level, &caps[2], &mut id_counter)
511506
})
512507
.into_owned()
513508
}
@@ -517,8 +512,7 @@ fn build_header_links(html: &str, filepath: &str) -> String {
517512
fn wrap_header_with_link(
518513
level: usize,
519514
content: &str,
520-
id_counter: &mut HashMap<String, usize>,
521-
filepath: &str,
515+
id_counter: &mut HashMap<String, usize>
522516
) -> String {
523517
let raw_id = utils::id_from_content(content);
524518

@@ -532,35 +526,13 @@ fn wrap_header_with_link(
532526
*id_count += 1;
533527

534528
format!(
535-
r##"<a class="header" href="{filepath}#{id}" id="{id}"><h{level}>{text}</h{level}></a>"##,
529+
r##"<a class="header" href="#{id}" id="{id}"><h{level}>{text}</h{level}></a>"##,
536530
level = level,
537531
id = id,
538-
text = content,
539-
filepath = filepath
532+
text = content
540533
)
541534
}
542535

543-
// anchors to the same page (href="#anchor") do not work because of
544-
// <base href="../"> pointing to the root folder. This function *fixes*
545-
// that in a very inelegant way
546-
fn fix_anchor_links(html: &str, filepath: &str) -> String {
547-
let regex = Regex::new(r##"<a([^>]+)href="#([^"]+)"([^>]*)>"##).unwrap();
548-
regex
549-
.replace_all(html, |caps: &Captures| {
550-
let before = &caps[1];
551-
let anchor = &caps[2];
552-
let after = &caps[3];
553-
554-
format!(
555-
"<a{before}href=\"{filepath}#{anchor}\"{after}>",
556-
before = before,
557-
filepath = filepath,
558-
anchor = anchor,
559-
after = after
560-
)
561-
})
562-
.into_owned()
563-
}
564536

565537
// The rust book uses annotations for rustdoc to test code snippets,
566538
// like the following:
@@ -660,37 +632,32 @@ mod tests {
660632
let inputs = vec![
661633
(
662634
"blah blah <h1>Foo</h1>",
663-
r##"blah blah <a class="header" href="./some_chapter/some_section.html#foo" id="foo"><h1>Foo</h1></a>"##,
635+
r##"blah blah <a class="header" href="#foo" id="foo"><h1>Foo</h1></a>"##,
664636
),
665637
(
666638
"<h1>Foo</h1>",
667-
r##"<a class="header" href="./some_chapter/some_section.html#foo" id="foo"><h1>Foo</h1></a>"##,
639+
r##"<a class="header" href="#foo" id="foo"><h1>Foo</h1></a>"##,
668640
),
669641
(
670642
"<h3>Foo^bar</h3>",
671-
r##"<a class="header" href="./some_chapter/some_section.html#foobar" id="foobar"><h3>Foo^bar</h3></a>"##,
643+
r##"<a class="header" href="#foobar" id="foobar"><h3>Foo^bar</h3></a>"##,
672644
),
673645
(
674646
"<h4></h4>",
675-
r##"<a class="header" href="./some_chapter/some_section.html#" id=""><h4></h4></a>"##,
647+
r##"<a class="header" href="#" id=""><h4></h4></a>"##,
676648
),
677649
(
678650
"<h4><em>Hï</em></h4>",
679-
r##"<a class="header" href="./some_chapter/some_section.html#hï" id="hï"><h4><em>Hï</em></h4></a>"##,
651+
r##"<a class="header" href="#hï" id="hï"><h4><em>Hï</em></h4></a>"##,
680652
),
681653
(
682654
"<h1>Foo</h1><h3>Foo</h3>",
683-
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>"##,
655+
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>"##,
684656
),
685657
];
686658

687659
for (src, should_be) in inputs {
688-
let filepath = "./some_chapter/some_section.html";
689-
let got = build_header_links(&src, filepath);
690-
assert_eq!(got, should_be);
691-
692-
// This is redundant for most cases
693-
let got = fix_anchor_links(&got, filepath);
660+
let got = build_header_links(&src);
694661
assert_eq!(got, should_be);
695662
}
696663
}

src/renderer/html_handlebars/helpers/navigation.rs

+9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use std::collections::BTreeMap;
44
use serde_json;
55
use handlebars::{Context, Handlebars, Helper, RenderContext, RenderError, Renderable};
66

7+
use utils;
8+
79
type StringMap = BTreeMap<String, String>;
810

911
/// Target for `find_chapter`.
@@ -87,6 +89,13 @@ fn render(
8789
trace!("Creating BTreeMap to inject in context");
8890

8991
let mut context = BTreeMap::new();
92+
let base_path = rc.evaluate_absolute("path", false)?
93+
.as_str()
94+
.ok_or_else(|| RenderError::new("Type error for `path`, string expected"))?
95+
.replace("\"", "");
96+
97+
context.insert("path_to_root".to_owned(),
98+
json!(utils::fs::path_to_root(&base_path)));
9099

91100
chapter
92101
.get("name")

src/renderer/html_handlebars/helpers/toc.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use std::path::Path;
22
use std::collections::BTreeMap;
33

4+
use utils;
5+
46
use serde_json;
57
use handlebars::{Handlebars, Helper, HelperDef, RenderContext, RenderError};
68
use pulldown_cmark::{html, Event, Parser, Tag};
@@ -77,6 +79,7 @@ impl HelperDef for RenderToc {
7779
.replace("\\", "/");
7880

7981
// Add link
82+
rc.writer.write_all(&utils::fs::path_to_root(&current).as_bytes())?;
8083
rc.writer.write_all(tmp.as_bytes())?;
8184
rc.writer.write_all(b"\"")?;
8285

src/theme/book.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -293,9 +293,9 @@ function playpen_text(playpen) {
293293
var themePopup = document.getElementById('theme-list');
294294
var themeColorMetaTag = document.querySelector('meta[name="theme-color"]');
295295
var stylesheets = {
296-
ayuHighlight: document.querySelector("[href='ayu-highlight.css']"),
297-
tomorrowNight: document.querySelector("[href='tomorrow-night.css']"),
298-
highlight: document.querySelector("[href='highlight.css']"),
296+
ayuHighlight: document.querySelector("[href$='ayu-highlight.css']"),
297+
tomorrowNight: document.querySelector("[href$='tomorrow-night.css']"),
298+
highlight: document.querySelector("[href$='highlight.css']"),
299299
};
300300

301301
function showThemes() {

src/theme/index.hbs

+26-25
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,18 @@
99
<meta name="viewport" content="width=device-width, initial-scale=1">
1010
<meta name="theme-color" content="#ffffff" />
1111

12-
<base href="{{ path_to_root }}">
13-
14-
<link rel="stylesheet" href="book.css">
12+
<link rel="stylesheet" href="{{ path_to_root }}book.css">
1513
<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">
1614
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
1715

1816
<link rel="shortcut icon" href="{{ favicon }}">
1917

2018
<!-- Font Awesome -->
21-
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
19+
<link rel="stylesheet" href="{{ path_to_root }}FontAwesome/css/font-awesome.css">
2220

23-
<link rel="stylesheet" href="highlight.css">
24-
<link rel="stylesheet" href="tomorrow-night.css">
25-
<link rel="stylesheet" href="ayu-highlight.css">
21+
<link rel="stylesheet" href="{{ path_to_root }}highlight.css">
22+
<link rel="stylesheet" href="{{ path_to_root }}tomorrow-night.css">
23+
<link rel="stylesheet" href="{{ path_to_root }}ayu-highlight.css">
2624

2725
<!-- Custom theme stylesheets -->
2826
{{#each additional_css}}
@@ -107,7 +105,7 @@
107105
<h1 class="menu-title">{{ book_title }}</h1>
108106

109107
<div class="right-buttons">
110-
<a href="print.html" title="Print this book" aria-label="Print this book">
108+
<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
111109
<i id="print-button" class="fa fa-print"></i>
112110
</a>
113111
</div>
@@ -144,13 +142,13 @@
144142
<nav class="nav-wrapper" aria-label="Page navigation">
145143
<!-- Mobile navigation buttons -->
146144
{{#previous}}
147-
<a rel="prev" href="{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
145+
<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
148146
<i class="fa fa-angle-left"></i>
149147
</a>
150148
{{/previous}}
151149

152150
{{#next}}
153-
<a rel="next" href="{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
151+
<a rel="next" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
154152
<i class="fa fa-angle-right"></i>
155153
</a>
156154
{{/next}}
@@ -162,13 +160,13 @@
162160

163161
<nav class="nav-wide-wrapper" aria-label="Page navigation">
164162
{{#previous}}
165-
<a href="{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
163+
<a href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
166164
<i class="fa fa-angle-left"></i>
167165
</a>
168166
{{/previous}}
169167

170168
{{#next}}
171-
<a href="{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
169+
<a href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
172170
<i class="fa fa-angle-right"></i>
173171
</a>
174172
{{/next}}
@@ -213,29 +211,32 @@
213211
{{/if}}
214212

215213
{{#if playpen_js}}
216-
<script src="ace.js" type="text/javascript" charset="utf-8"></script>
217-
<script src="editor.js" type="text/javascript" charset="utf-8"></script>
218-
<script src="mode-rust.js" type="text/javascript" charset="utf-8"></script>
219-
<script src="theme-dawn.js" type="text/javascript" charset="utf-8"></script>
220-
<script src="theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
214+
<script src="{{ path_to_root }}ace.js" type="text/javascript" charset="utf-8"></script>
215+
<script src="{{ path_to_root }}editor.js" type="text/javascript" charset="utf-8"></script>
216+
<script src="{{ path_to_root }}mode-rust.js" type="text/javascript" charset="utf-8"></script>
217+
<script src="{{ path_to_root }}theme-dawn.js" type="text/javascript" charset="utf-8"></script>
218+
<script src="{{ path_to_root }}theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
221219
{{/if}}
222220

223221
{{#if search_enabled}}
224-
<script src="searchindex.js" type="text/javascript" charset="utf-8"></script>
222+
<script src="{{ path_to_root }}searchindex.js" type="text/javascript" charset="utf-8"></script>
223+
<script>
224+
var path_to_root = "{{path_to_root}}";
225+
</script>
225226
{{/if}}
226227
{{#if search_js}}
227-
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
228-
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
229-
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
228+
<script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
229+
<script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script>
230+
<script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script>
230231
{{/if}}
231232

232-
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
233-
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
234-
<script src="book.js" type="text/javascript" charset="utf-8"></script>
233+
<script src="{{ path_to_root }}clipboard.min.js" type="text/javascript" charset="utf-8"></script>
234+
<script src="{{ path_to_root }}highlight.js" type="text/javascript" charset="utf-8"></script>
235+
<script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
235236

236237
<!-- Custom JS scripts -->
237238
{{#each additional_js}}
238-
<script type="text/javascript" src="{{this}}"></script>
239+
<script type="text/javascript" src="{{ path_to_root }}{{this}}"></script>
239240
{{/each}}
240241

241242
{{#if is_print}}

src/theme/searcher/searcher.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,14 @@ window.search = window.search || {};
99
if (!Mark || !elasticlunr) {
1010
return;
1111
}
12-
12+
13+
//IE 11 Compatibility from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith
14+
if (!String.prototype.startsWith) {
15+
String.prototype.startsWith = function(search, pos) {
16+
return this.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search;
17+
};
18+
}
19+
1320
var search_wrap = document.getElementById('search-wrapper'),
1421
searchbar = document.getElementById('searchbar'),
1522
searchbar_outer = document.getElementById('searchbar-outer'),
@@ -137,7 +144,7 @@ window.search = window.search || {};
137144
url.push("");
138145
}
139146

140-
return '<a href="' + url[0] + '?' + URL_MARK_PARAM + '=' + searchterms + '#' + url[1]
147+
return '<a href="' + path_to_root + url[0] + '?' + URL_MARK_PARAM + '=' + searchterms + '#' + url[1]
141148
+ '" aria-details="teaser_' + teaser_count + '">' + result.doc.breadcrumbs + '</a>'
142149
+ '<span class="teaser" id="teaser_' + teaser_count + '" aria-label="Search Result Teaser">'
143150
+ teaser + '</span>';

0 commit comments

Comments
 (0)