translation links
rename with _ translation links css for translation links
This commit is contained in:
parent
e7ba6fac85
commit
c021940331
|
@ -12,7 +12,7 @@ build = "build.rs"
|
||||||
include = ["data"]
|
include = ["data"]
|
||||||
exclude = [
|
exclude = [
|
||||||
"book-example/*",
|
"book-example/*",
|
||||||
"data/html-template/_stylus",
|
"data/_html-template/_stylus",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ exclude = [
|
||||||
clap = "2.19.2"
|
clap = "2.19.2"
|
||||||
handlebars = { version = "0.23.0", features = ["serde_type"] }
|
handlebars = { version = "0.23.0", features = ["serde_type"] }
|
||||||
serde = "0.8"
|
serde = "0.8"
|
||||||
|
serde_derive = "0.8"
|
||||||
serde_json = "0.8"
|
serde_json = "0.8"
|
||||||
pulldown-cmark = "0.0.8"
|
pulldown-cmark = "0.0.8"
|
||||||
regex = "0.1"
|
regex = "0.1"
|
||||||
|
|
2
build.rs
2
build.rs
|
@ -24,7 +24,7 @@ fn main() {
|
||||||
// Compile stylus stylesheet to css
|
// Compile stylus stylesheet to css
|
||||||
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
|
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||||
|
|
||||||
let template_dir = Path::new(&manifest_dir).join("data/html-template/");
|
let template_dir = Path::new(&manifest_dir).join("data/_html-template/");
|
||||||
let stylus_dir = template_dir.join("_stylus/book.styl");
|
let stylus_dir = template_dir.join("_stylus/book.styl");
|
||||||
|
|
||||||
if !Command::new("stylus")
|
if !Command::new("stylus")
|
||||||
|
|
|
@ -53,6 +53,10 @@
|
||||||
<i id="theme-toggle" class="fa fa-paint-brush"></i>
|
<i id="theme-toggle" class="fa fa-paint-brush"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="translations">
|
||||||
|
{{#translations}}{{/translations}}
|
||||||
|
</div>
|
||||||
|
|
||||||
<h1 class="menu-title">{{ title }}</h1>
|
<h1 class="menu-title">{{ title }}</h1>
|
||||||
|
|
||||||
<div class="right-buttons">
|
<div class="right-buttons">
|
||||||
|
@ -60,6 +64,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div id="content" class="content">
|
<div id="content" class="content">
|
||||||
{{{ content }}}
|
{{{ content }}}
|
||||||
</div>
|
</div>
|
|
@ -17,18 +17,13 @@
|
||||||
.right-buttons { float: right }
|
.right-buttons { float: right }
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-title {
|
h1.menu-title {
|
||||||
display: inline-block
|
float: left
|
||||||
|
margin: 0 0 0 2em
|
||||||
|
padding: 0
|
||||||
font-weight: 200
|
font-weight: 200
|
||||||
font-size: 20px
|
font-size: 20px
|
||||||
line-height: 50px
|
line-height: 50px
|
||||||
position: absolute
|
|
||||||
top: 0
|
|
||||||
left: 0
|
|
||||||
right: 0
|
|
||||||
bottom: 0
|
|
||||||
text-align: center
|
|
||||||
margin: 0
|
|
||||||
|
|
||||||
opacity: 0
|
opacity: 0
|
||||||
transition: opacity 0.5s ease-in-out
|
transition: opacity 0.5s ease-in-out
|
|
@ -53,3 +53,21 @@
|
||||||
|
|
||||||
img { max-width: 100%; }
|
img { max-width: 100%; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.translations {
|
||||||
|
float: left
|
||||||
|
height: 50px
|
||||||
|
ul {
|
||||||
|
margin: 10px 0 0 0
|
||||||
|
display: inline-block
|
||||||
|
list-style-type: none
|
||||||
|
li {
|
||||||
|
display: inline-block
|
||||||
|
padding: 0.2em 0.5em
|
||||||
|
a {
|
||||||
|
text-decoration: none
|
||||||
|
&:hover { text-decoration: underline; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,7 +37,7 @@ table thead td {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
.sidebar {
|
.sidebar {
|
||||||
position: fixed;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
@ -99,6 +99,7 @@ table thead td {
|
||||||
}
|
}
|
||||||
.page-wrapper {
|
.page-wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
overflow-y: auto;
|
||||||
left: 315px;
|
left: 315px;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -133,14 +134,7 @@ table thead td {
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
padding-right: 15px;
|
padding-right: 15px;
|
||||||
}
|
overflow-y: auto;
|
||||||
@media only screen and (max-width: 400px) {
|
|
||||||
.page {
|
|
||||||
/* Only prevent horizontal scrolling on screens with less than 100px for the content
|
|
||||||
A better way would be to somehow prevent horizontal scrolling all the time, but this causes scrolling problems on iOS Safari.
|
|
||||||
Also, would be better to only enable horizontal scrolling when it is needed (content does not fit on page) but I have no idea how to do that. */
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.content {
|
.content {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
@ -157,6 +151,25 @@ table thead td {
|
||||||
.content img {
|
.content img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
div.translations {
|
||||||
|
float: left;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
div.translations ul {
|
||||||
|
margin: 10px 0 0 0;
|
||||||
|
display: inline-block;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
div.translations ul li {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.2em 0.5em;
|
||||||
|
}
|
||||||
|
div.translations ul li a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
div.translations ul li a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
.menu-bar {
|
.menu-bar {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
|
@ -181,18 +194,13 @@ table thead td {
|
||||||
.menu-bar .right-buttons {
|
.menu-bar .right-buttons {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
.menu-title {
|
h1.menu-title {
|
||||||
display: inline-block;
|
float: left;
|
||||||
|
margin: 0 0 0 2em;
|
||||||
|
padding: 0;
|
||||||
font-weight: 200;
|
font-weight: 200;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
line-height: 50px;
|
line-height: 50px;
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
text-align: center;
|
|
||||||
margin: 0;
|
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
|
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
|
||||||
filter: alpha(opacity=0);
|
filter: alpha(opacity=0);
|
||||||
|
@ -211,7 +219,7 @@ table thead td {
|
||||||
font-size: 2.5em;
|
font-size: 2.5em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
position: fixed;
|
position: absolute;
|
||||||
top: 50px /* Height of menu-bar */;
|
top: 50px /* Height of menu-bar */;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -250,13 +258,7 @@ table thead td {
|
||||||
.nav-chapters:hover {
|
.nav-chapters:hover {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
.sidebar-hidden .previous {
|
.previous {
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
.sidebar-visible .nav-chapters .previous {
|
|
||||||
left: 300px;
|
|
||||||
}
|
|
||||||
.sidebar-visible .mobile-nav-chapters .previous {
|
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
.next {
|
.next {
|
||||||
|
@ -266,7 +268,6 @@ table thead td {
|
||||||
position: relative;
|
position: relative;
|
||||||
left: 10px;
|
left: 10px;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
-webkit-border-radius: 4px;
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 0.7em;
|
font-size: 0.7em;
|
||||||
}
|
}
|
||||||
|
@ -303,7 +304,6 @@ table thead td {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-bottom: 50px;
|
margin-bottom: 50px;
|
||||||
-webkit-border-radius: 5px;
|
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
.next {
|
.next {
|
||||||
|
@ -406,7 +406,6 @@ table thead td {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
padding: 0.1em 0.3em;
|
padding: 0.1em 0.3em;
|
||||||
-webkit-border-radius: 3px;
|
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
.light pre {
|
.light pre {
|
||||||
|
@ -521,7 +520,6 @@ table thead td {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
padding: 0.1em 0.3em;
|
padding: 0.1em 0.3em;
|
||||||
-webkit-border-radius: 3px;
|
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
.coal pre {
|
.coal pre {
|
||||||
|
@ -636,7 +634,6 @@ table thead td {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
padding: 0.1em 0.3em;
|
padding: 0.1em 0.3em;
|
||||||
-webkit-border-radius: 3px;
|
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
.navy pre {
|
.navy pre {
|
||||||
|
@ -751,7 +748,6 @@ table thead td {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
padding: 0.1em 0.3em;
|
padding: 0.1em 0.3em;
|
||||||
-webkit-border-radius: 3px;
|
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
.rust pre {
|
.rust pre {
|
||||||
|
@ -794,7 +790,6 @@ table thead td {
|
||||||
}
|
}
|
||||||
code {
|
code {
|
||||||
background-color: #666;
|
background-color: #666;
|
||||||
-webkit-border-radius: 5px;
|
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
/* Force background to be printed in Chrome */
|
/* Force background to be printed in Chrome */
|
||||||
-webkit-print-color-adjust: exact;
|
-webkit-print-color-adjust: exact;
|
Before Width: | Height: | Size: 348 KiB After Width: | Height: | Size: 348 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
@ -1,116 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html lang="{{ language }}">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>{{ title }}</title>
|
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
|
||||||
<meta name="description" content="{{ description }}">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
|
|
||||||
<base href="{{ path_to_root }}">
|
|
||||||
|
|
||||||
<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 rel="shortcut icon" href="{{ favicon }}">
|
|
||||||
|
|
||||||
<!-- Font Awesome -->
|
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="highlight.css">
|
|
||||||
<link rel="stylesheet" href="tomorrow-night.css">
|
|
||||||
|
|
||||||
<!-- MathJax -->
|
|
||||||
<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
|
||||||
|
|
||||||
<!-- Fetch JQuery from CDN but have a local fallback -->
|
|
||||||
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
|
|
||||||
<script>
|
|
||||||
if (typeof jQuery == 'undefined') {
|
|
||||||
document.write(unescape("%3Cscript src='jquery.js'%3E%3C/script%3E"));
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body class="light">
|
|
||||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
var theme = localStorage.getItem('theme');
|
|
||||||
if (theme == null) { theme = 'light'; }
|
|
||||||
$('body').removeClass().addClass(theme);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Hide / unhide sidebar before it is displayed -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
var sidebar = localStorage.getItem('sidebar');
|
|
||||||
if (sidebar === "hidden") { $("html").addClass("sidebar-hidden") }
|
|
||||||
else if (sidebar === "visible") { $("html").addClass("sidebar-visible") }
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div id="sidebar" class="sidebar">
|
|
||||||
{{#toc}}{{/toc}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="page-wrapper" class="page-wrapper">
|
|
||||||
|
|
||||||
<div class="page">
|
|
||||||
<div id="menu-bar" class="menu-bar">
|
|
||||||
<div class="left-buttons">
|
|
||||||
<i id="sidebar-toggle" class="fa fa-bars"></i>
|
|
||||||
<i id="theme-toggle" class="fa fa-paint-brush"></i>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h1 class="menu-title">{{ title }}</h1>
|
|
||||||
|
|
||||||
<div class="right-buttons">
|
|
||||||
<i id="print-button" class="fa fa-print" title="Print this book"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="content" class="content">
|
|
||||||
{{{ content }}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Mobile navigation buttons -->
|
|
||||||
{{#previous}}
|
|
||||||
<a href="{{link}}" class="mobile-nav-chapters previous">
|
|
||||||
<i class="fa fa-angle-left"></i>
|
|
||||||
</a>
|
|
||||||
{{/previous}}
|
|
||||||
|
|
||||||
{{#next}}
|
|
||||||
<a href="{{link}}" class="mobile-nav-chapters next">
|
|
||||||
<i class="fa fa-angle-right"></i>
|
|
||||||
</a>
|
|
||||||
{{/next}}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#previous}}
|
|
||||||
<a href="{{link}}" class="nav-chapters previous" title="You can navigate through the chapters using the arrow keys">
|
|
||||||
<i class="fa fa-angle-left"></i>
|
|
||||||
</a>
|
|
||||||
{{/previous}}
|
|
||||||
|
|
||||||
{{#next}}
|
|
||||||
<a href="{{link}}" class="nav-chapters next" title="You can navigate through the chapters using the arrow keys">
|
|
||||||
<i class="fa fa-angle-right"></i>
|
|
||||||
</a>
|
|
||||||
{{/next}}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Local fallback for Font Awesome -->
|
|
||||||
<script>
|
|
||||||
if ($(".fa").css("font-family") !== "FontAwesome") {
|
|
||||||
$('<link rel="stylesheet" type="text/css" href="_FontAwesome/css/font-awesome.css">').prependTo('head');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Livereload script (if served using the cli tool) -->
|
|
||||||
{{{livereload}}}
|
|
||||||
|
|
||||||
<script src="highlight.js"></script>
|
|
||||||
<script src="book.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -53,6 +53,9 @@ pub struct Chapter {
|
||||||
/// chapter's path to index.html.
|
/// chapter's path to index.html.
|
||||||
pub dest_path: Option<PathBuf>,
|
pub dest_path: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Links to the corresponding translations.
|
||||||
|
pub translation_links: Option<Vec<TranslationLink>>,
|
||||||
|
|
||||||
/// The author of the chapter, or the book.
|
/// The author of the chapter, or the book.
|
||||||
pub authors: Option<Vec<Author>>,
|
pub authors: Option<Vec<Author>>,
|
||||||
|
|
||||||
|
@ -73,6 +76,7 @@ impl Default for Chapter {
|
||||||
title: "Untitled".to_string(),
|
title: "Untitled".to_string(),
|
||||||
path: PathBuf::from("src".to_string()).join("untitled.md"),
|
path: PathBuf::from("src".to_string()).join("untitled.md"),
|
||||||
dest_path: None,
|
dest_path: None,
|
||||||
|
translation_links: None,
|
||||||
authors: None,
|
authors: None,
|
||||||
translators: None,
|
translators: None,
|
||||||
description: None,
|
description: None,
|
||||||
|
@ -153,6 +157,17 @@ impl Chapter {
|
||||||
self.css_class = Some(a.to_string());
|
self.css_class = Some(a.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(a) = data.get("translation_links") {
|
||||||
|
if let Some(b) = a.as_slice() {
|
||||||
|
let links: Vec<TranslationLink> = b.iter()
|
||||||
|
.filter_map(|x| x.as_table())
|
||||||
|
.map(|x| TranslationLink::from(x.to_owned()))
|
||||||
|
.collect::<Vec<TranslationLink>>();
|
||||||
|
|
||||||
|
self.translation_links = Some(links);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Author name as a hash key.
|
// Author name as a hash key.
|
||||||
if let Some(a) = data.get("author") {
|
if let Some(a) = data.get("author") {
|
||||||
if let Some(b) = a.as_str() {
|
if let Some(b) = a.as_str() {
|
||||||
|
@ -213,3 +228,40 @@ impl Chapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct TranslationLink {
|
||||||
|
pub code: String,
|
||||||
|
pub link: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TranslationLink {
|
||||||
|
fn default() -> TranslationLink {
|
||||||
|
TranslationLink {
|
||||||
|
code: "".to_string(),
|
||||||
|
link: "".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TranslationLink {
|
||||||
|
pub fn new(code: String, link: String) -> TranslationLink {
|
||||||
|
TranslationLink {
|
||||||
|
code: code,
|
||||||
|
link: link,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<toml::Table> for TranslationLink {
|
||||||
|
fn from(data: toml::Table) -> TranslationLink {
|
||||||
|
let mut link = TranslationLink::default();
|
||||||
|
if let Some(x) = data.get("code") {
|
||||||
|
link.code = x.to_string().replace("\"", "");
|
||||||
|
}
|
||||||
|
if let Some(x) = data.get("link") {
|
||||||
|
link.link = x.to_string().replace("\"", "");
|
||||||
|
}
|
||||||
|
link
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,9 @@ pub mod chapter;
|
||||||
|
|
||||||
pub use self::book::Book;
|
pub use self::book::Book;
|
||||||
use renderer::{Renderer, HtmlHandlebars};
|
use renderer::{Renderer, HtmlHandlebars};
|
||||||
|
|
||||||
|
use self::chapter::TranslationLink;
|
||||||
|
use self::toc::{TocItem, TocContent};
|
||||||
use utils;
|
use utils;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
@ -32,7 +35,7 @@ pub struct MDBook {
|
||||||
/// A book doesn't necessarily has to have the template files. When not
|
/// A book doesn't necessarily has to have the template files. When not
|
||||||
/// found in the book's folder, the embedded static assets will be used.
|
/// found in the book's folder, the embedded static assets will be used.
|
||||||
///
|
///
|
||||||
/// Html Handlebars: `project_root` + `assets/html-template`.
|
/// Html Handlebars: `project_root` + `assets/_html-template`.
|
||||||
template_dir: PathBuf,
|
template_dir: PathBuf,
|
||||||
|
|
||||||
/// Output base for all books, relative to `project_root`. Defaults to
|
/// Output base for all books, relative to `project_root`. Defaults to
|
||||||
|
@ -367,6 +370,69 @@ impl MDBook {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn link_translations(&mut self) -> &mut MDBook {
|
||||||
|
for (key, book) in self.translations.clone() {
|
||||||
|
let mut newbook: Book = book.clone();
|
||||||
|
|
||||||
|
newbook.toc = book.toc.iter()
|
||||||
|
.map(|item| {
|
||||||
|
match *item {
|
||||||
|
TocItem::Numbered(ref i) =>
|
||||||
|
TocItem::Numbered(self.set_translation_links(i)),
|
||||||
|
TocItem::Unnumbered(ref i) =>
|
||||||
|
TocItem::Unnumbered(self.set_translation_links(i)),
|
||||||
|
TocItem::Unlisted(ref i) =>
|
||||||
|
TocItem::Unlisted(self.set_translation_links(i)),
|
||||||
|
TocItem::Spacer =>
|
||||||
|
TocItem::Spacer,
|
||||||
|
}
|
||||||
|
}).collect::<Vec<TocItem>>();
|
||||||
|
|
||||||
|
self.translations.remove(&key);
|
||||||
|
self.translations.insert(key, newbook);
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// prepare a Vec of default links to point to the index.html of each translation
|
||||||
|
fn translation_index_links(&mut self) -> Vec<TranslationLink> {
|
||||||
|
let mut default_links: Vec<TranslationLink> = vec![];
|
||||||
|
|
||||||
|
let mut keys = self.translations.keys()
|
||||||
|
.map(|x| x.to_string())
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
keys.sort();
|
||||||
|
|
||||||
|
for key in keys {
|
||||||
|
let book = self.translations.get(&key).unwrap();
|
||||||
|
|
||||||
|
let z = self.get_dest_base();
|
||||||
|
let a = book.config.dest.strip_prefix(&z).unwrap();
|
||||||
|
let b = a.join("index.html");
|
||||||
|
let c = b.to_str().unwrap();
|
||||||
|
let link = TranslationLink::new(key, c.to_string());
|
||||||
|
default_links.push(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
default_links
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_translation_links(&mut self, content: &TocContent) -> TocContent {
|
||||||
|
let default_links = self.translation_index_links();
|
||||||
|
|
||||||
|
let mut newcontent: TocContent = content.clone();
|
||||||
|
|
||||||
|
match newcontent.chapter.translation_links {
|
||||||
|
Some(_) => {},
|
||||||
|
None => {
|
||||||
|
newcontent.chapter.translation_links = Some(default_links);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newcontent
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_project_root(&self) -> &Path {
|
pub fn get_project_root(&self) -> &Path {
|
||||||
&self.project_root
|
&self.project_root
|
||||||
}
|
}
|
||||||
|
@ -414,7 +480,7 @@ impl MDBook {
|
||||||
self.render_intent = intent;
|
self.render_intent = intent;
|
||||||
match self.render_intent {
|
match self.render_intent {
|
||||||
RenderIntent::HtmlHandlebars => {
|
RenderIntent::HtmlHandlebars => {
|
||||||
self.set_template_dir(&PathBuf::from("assets").join("html-template"));
|
self.set_template_dir(&PathBuf::from("assets").join("_html-template"));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
|
|
|
@ -69,6 +69,11 @@
|
||||||
//!
|
//!
|
||||||
//! Make sure to take a look at it.
|
//! Make sure to take a look at it.
|
||||||
|
|
||||||
|
#![feature(proc_macro)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
|
||||||
extern crate includedir;
|
extern crate includedir;
|
||||||
extern crate phf;
|
extern crate phf;
|
||||||
|
|
||||||
|
|
|
@ -36,9 +36,14 @@ impl Renderer for HtmlHandlebars {
|
||||||
|
|
||||||
book_project.read_config();
|
book_project.read_config();
|
||||||
book_project.parse_books();
|
book_project.parse_books();
|
||||||
|
book_project.link_translations();
|
||||||
|
|
||||||
// Clean output directory
|
// Clean output directory
|
||||||
try!(utils::fs::remove_dir_content(&book_project.get_dest_base()));
|
|
||||||
|
// FIXME don't remove dotfiles such as .git/ folder. It's a common
|
||||||
|
// practice to track gh-pages in a versioned output folder.
|
||||||
|
|
||||||
|
//try!(utils::fs::remove_dir_content(&book_project.get_dest_base()));
|
||||||
|
|
||||||
try!(self.render(&book_project));
|
try!(self.render(&book_project));
|
||||||
|
|
||||||
|
@ -116,9 +121,9 @@ impl Renderer for HtmlHandlebars {
|
||||||
&book_project.get_dest_base());
|
&book_project.get_dest_base());
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
try!(utils::fs::copy_data("data/html-template/**/*",
|
try!(utils::fs::copy_data("data/_html-template/**/*",
|
||||||
"data/html-template/",
|
"data/_html-template/",
|
||||||
vec!["data/html-template/_*"],
|
vec!["data/_html-template/_*"],
|
||||||
&book_project.get_dest_base()));
|
&book_project.get_dest_base()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +138,7 @@ impl Renderer for HtmlHandlebars {
|
||||||
let s = if tmpl_path.exists() {
|
let s = if tmpl_path.exists() {
|
||||||
try!(utils::fs::file_to_string(&tmpl_path))
|
try!(utils::fs::file_to_string(&tmpl_path))
|
||||||
} else {
|
} else {
|
||||||
try!(utils::fs::get_data_file("data/html-template/_layouts/page.hbs"))
|
try!(utils::fs::get_data_file("data/_html-template/_layouts/page.hbs"))
|
||||||
};
|
};
|
||||||
|
|
||||||
// Register template
|
// Register template
|
||||||
|
@ -145,6 +150,7 @@ impl Renderer for HtmlHandlebars {
|
||||||
handlebars.register_helper("toc", Box::new(helpers::toc::RenderToc));
|
handlebars.register_helper("toc", Box::new(helpers::toc::RenderToc));
|
||||||
handlebars.register_helper("previous", Box::new(helpers::navigation::previous));
|
handlebars.register_helper("previous", Box::new(helpers::navigation::previous));
|
||||||
handlebars.register_helper("next", Box::new(helpers::navigation::next));
|
handlebars.register_helper("next", Box::new(helpers::navigation::next));
|
||||||
|
handlebars.register_helper("translations", Box::new(helpers::translations::TranslationsHelper));
|
||||||
|
|
||||||
// Check if book's dest directory exists
|
// Check if book's dest directory exists
|
||||||
|
|
||||||
|
@ -332,6 +338,10 @@ fn make_data(book: &Book,
|
||||||
|
|
||||||
data.insert("path_to_root".to_owned(), utils::fs::path_to_root(&path).to_json());
|
data.insert("path_to_root".to_owned(), utils::fs::path_to_root(&path).to_json());
|
||||||
|
|
||||||
|
if let Some(ref links) = chapter.translation_links {
|
||||||
|
data.insert("translation_links".to_owned(), links.to_json());
|
||||||
|
}
|
||||||
|
|
||||||
let chapters = try!(items_to_chapters(&book.toc, &book));
|
let chapters = try!(items_to_chapters(&book.toc, &book));
|
||||||
|
|
||||||
data.insert("chapters".to_owned(), chapters.to_json());
|
data.insert("chapters".to_owned(), chapters.to_json());
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
pub mod navigation;
|
pub mod navigation;
|
||||||
pub mod toc;
|
pub mod toc;
|
||||||
pub mod playpen;
|
pub mod playpen;
|
||||||
|
pub mod translations;
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
use std::path::Path;
|
||||||
|
use std::collections::{VecDeque, BTreeMap};
|
||||||
|
|
||||||
|
use serde_json;
|
||||||
|
use handlebars::{Handlebars, HelperDef, RenderError, RenderContext, Helper, Context};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct TranslationsHelper;
|
||||||
|
|
||||||
|
impl HelperDef for TranslationsHelper {
|
||||||
|
fn call(&self, c: &Context, _h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
|
||||||
|
|
||||||
|
let translation_links = c.navigate(rc.get_path(), &VecDeque::new(), "translation_links");
|
||||||
|
let decoded: Vec<BTreeMap<String, String>> = serde_json::from_str(&translation_links.to_string()).unwrap();
|
||||||
|
|
||||||
|
if decoded.len() == 0 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
try!(rc.writer.write("<ul class=\"translations\">".as_bytes()));
|
||||||
|
|
||||||
|
for item in decoded {
|
||||||
|
let empty = "".to_string();
|
||||||
|
let link = item.get("link").unwrap_or(&empty);
|
||||||
|
let code = item.get("code").unwrap_or(&empty);
|
||||||
|
let text = format!("<li><a href=\"{}\">{}</a></li>", link, code);
|
||||||
|
try!(rc.writer.write(text.as_bytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
try!(rc.writer.write("</ul>".as_bytes()));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,13 @@
|
||||||
|
+++
|
||||||
|
[[translation_links]]
|
||||||
|
code = "fr"
|
||||||
|
link = "fr/terrier.html"
|
||||||
|
|
||||||
|
[[translation_links]]
|
||||||
|
code = "hu"
|
||||||
|
link = "hu/nyuszi.html"
|
||||||
|
+++
|
||||||
|
|
||||||
# Down The Rabbit-Hole
|
# Down The Rabbit-Hole
|
||||||
|
|
||||||
![Rabbit](images/Rabbit.png)
|
![Rabbit](images/Rabbit.png)
|
||||||
|
|
|
@ -9,7 +9,7 @@ use utils;
|
||||||
#[test]
|
#[test]
|
||||||
fn it_copies_data_file() {
|
fn it_copies_data_file() {
|
||||||
let dest_path = Path::new("the place was dark").join("and dusty and half-lost").join("book.css");
|
let dest_path = Path::new("the place was dark").join("and dusty and half-lost").join("book.css");
|
||||||
utils::fs::copy_data_file("data/html-template/css/books.css", &dest_path);
|
utils::fs::copy_data_file("data/_html-template/css/books.css", &dest_path);
|
||||||
|
|
||||||
let mut file = match File::open(&dest_path) {
|
let mut file = match File::open(&dest_path) {
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
|
@ -32,9 +32,9 @@ fn it_copies_data_file() {
|
||||||
fn it_copies_data_by_pattern() {
|
fn it_copies_data_by_pattern() {
|
||||||
let dest_base = Path::new("in tangles of old alleys").join("near the quays");
|
let dest_base = Path::new("in tangles of old alleys").join("near the quays");
|
||||||
|
|
||||||
if let Err(e) = utils::fs::copy_data("data/html-template/**/*",
|
if let Err(e) = utils::fs::copy_data("data/_html-template/**/*",
|
||||||
"data/html-template/",
|
"data/_html-template/",
|
||||||
vec!["data/html-template/_*"],
|
vec!["data/_html-template/_*"],
|
||||||
&dest_base) {
|
&dest_base) {
|
||||||
println!("Error: {:#?}", e);
|
println!("Error: {:#?}", e);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -50,4 +50,20 @@ fn it_renders_multilanguage_book() {
|
||||||
|
|
||||||
assert!(proj.get_dest_base().join("images").join("Queen.jpg").exists());
|
assert!(proj.get_dest_base().join("images").join("Queen.jpg").exists());
|
||||||
|
|
||||||
|
// Test if translation links given in the TOML header were rendered
|
||||||
|
|
||||||
|
book_path = proj.translations.get("en").unwrap().config.get_dest();
|
||||||
|
chapter_path = book_path.join("rabbit-hole.html");
|
||||||
|
s = utils::fs::file_to_string(&chapter_path).unwrap();
|
||||||
|
assert!(s.contains("<a href=\"hu/nyuszi.html\">hu</a>"));
|
||||||
|
assert!(s.contains("<a href=\"fr/terrier.html\">fr</a>"));
|
||||||
|
|
||||||
|
// Test if default translation links are set
|
||||||
|
|
||||||
|
book_path = proj.translations.get("hu").unwrap().config.get_dest();
|
||||||
|
chapter_path = book_path.join("tarka-farka.html");
|
||||||
|
s = utils::fs::file_to_string(&chapter_path).unwrap();
|
||||||
|
assert!(s.contains("<a href=\"en/index.html\">en</a>"));
|
||||||
|
assert!(s.contains("<a href=\"hu/index.html\">hu</a>"));
|
||||||
|
assert!(s.contains("<a href=\"fr/index.html\">fr</a>"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,17 +61,17 @@ pub fn copy_data_file(src_path: &str, dest_path: &Path) -> Result<(), Box<Error>
|
||||||
/// `include_base` will be removed from the source path. This way the path
|
/// `include_base` will be removed from the source path. This way the path
|
||||||
/// relative to the `dest_path` can be controlled.
|
/// relative to the `dest_path` can be controlled.
|
||||||
///
|
///
|
||||||
/// The following will copy all files under "data/html-template/", excluding
|
/// The following will copy all files under "data/_html-template/", excluding
|
||||||
/// folders that start with "_", take the "data/html-template/" part off the
|
/// folders that start with "_", take the "data/_html-template/" part off the
|
||||||
/// source path, and write the entries to "assets" folder.
|
/// source path, and write the entries to "assets" folder.
|
||||||
///
|
///
|
||||||
/// I.e. "data/html-template/css/book.css" will be written to
|
/// I.e. "data/_html-template/css/book.css" will be written to
|
||||||
/// "assets/css/book.css".
|
/// "assets/css/book.css".
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// utils::fs::copy_data("data/html-template/**/*",
|
/// utils::fs::copy_data("data/_html-template/**/*",
|
||||||
/// "data/html-template/",
|
/// "data/_html-template/",
|
||||||
/// vec!["data/html-template/_*"],
|
/// vec!["data/_html-template/_*"],
|
||||||
/// &Path::new("assets"));
|
/// &Path::new("assets"));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn copy_data(include_glob: &str,
|
pub fn copy_data(include_glob: &str,
|
||||||
|
|
Loading…
Reference in New Issue