Integrating Ace #247
This commit is contained in:
parent
6601dbdd61
commit
16aa545c5b
|
@ -13,9 +13,9 @@ use errors::*;
|
||||||
|
|
||||||
use config::BookConfig;
|
use config::BookConfig;
|
||||||
use config::tomlconfig::TomlConfig;
|
use config::tomlconfig::TomlConfig;
|
||||||
|
use config::htmlconfig::HtmlConfig;
|
||||||
use config::jsonconfig::JsonConfig;
|
use config::jsonconfig::JsonConfig;
|
||||||
|
|
||||||
|
|
||||||
pub struct MDBook {
|
pub struct MDBook {
|
||||||
config: BookConfig,
|
config: BookConfig,
|
||||||
|
|
||||||
|
@ -496,6 +496,10 @@ impl MDBook {
|
||||||
.get_additional_css()
|
.get_additional_css()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_html_config(&self) -> &HtmlConfig {
|
||||||
|
self.config.get_html_config()
|
||||||
|
}
|
||||||
|
|
||||||
// Construct book
|
// Construct book
|
||||||
fn parse_summary(&mut self) -> Result<()> {
|
fn parse_summary(&mut self) -> Result<()> {
|
||||||
// When append becomes stable, use self.content.append() ...
|
// When append becomes stable, use self.content.append() ...
|
||||||
|
|
|
@ -112,7 +112,7 @@ impl BookConfig {
|
||||||
self.get_mut_html_config()
|
self.get_mut_html_config()
|
||||||
.fill_from_tomlconfig(root, tomlhtmlconfig);
|
.fill_from_tomlconfig(root, tomlhtmlconfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,7 +213,7 @@ impl BookConfig {
|
||||||
self.authors.as_slice()
|
self.authors.as_slice()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_html_config(&mut self, htmlconfig: HtmlConfig) -> &mut Self {
|
pub fn set_html_config(&mut self, htmlconfig: HtmlConfig) -> &mut Self {
|
||||||
self.html_config = htmlconfig;
|
self.html_config = htmlconfig;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::path::{PathBuf, Path};
|
use std::path::{PathBuf, Path};
|
||||||
|
|
||||||
use super::tomlconfig::TomlHtmlConfig;
|
use super::tomlconfig::TomlHtmlConfig;
|
||||||
|
use super::playpenconfig::PlaypenConfig;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct HtmlConfig {
|
pub struct HtmlConfig {
|
||||||
|
@ -11,6 +12,7 @@ pub struct HtmlConfig {
|
||||||
google_analytics: Option<String>,
|
google_analytics: Option<String>,
|
||||||
additional_css: Vec<PathBuf>,
|
additional_css: Vec<PathBuf>,
|
||||||
additional_js: Vec<PathBuf>,
|
additional_js: Vec<PathBuf>,
|
||||||
|
playpen: PlaypenConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HtmlConfig {
|
impl HtmlConfig {
|
||||||
|
@ -27,14 +29,16 @@ impl HtmlConfig {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new<T: Into<PathBuf>>(root: T) -> Self {
|
pub fn new<T: Into<PathBuf>>(root: T) -> Self {
|
||||||
let root = root.into();
|
let root = root.into();
|
||||||
|
let theme = root.join("theme");
|
||||||
HtmlConfig {
|
HtmlConfig {
|
||||||
destination: root.clone().join("book"),
|
destination: root.clone().join("book"),
|
||||||
theme: root.join("theme"),
|
theme: theme.clone(),
|
||||||
curly_quotes: false,
|
curly_quotes: false,
|
||||||
mathjax_support: false,
|
mathjax_support: false,
|
||||||
google_analytics: None,
|
google_analytics: None,
|
||||||
additional_css: Vec::new(),
|
additional_css: Vec::new(),
|
||||||
additional_js: Vec::new(),
|
additional_js: Vec::new(),
|
||||||
|
playpen: PlaypenConfig::new(theme),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +85,10 @@ impl HtmlConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(playpen) = tomlconfig.playpen {
|
||||||
|
self.playpen.fill_from_tomlconfig(&self.theme, playpen);
|
||||||
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,4 +162,12 @@ impl HtmlConfig {
|
||||||
pub fn get_additional_js(&self) -> &[PathBuf] {
|
pub fn get_additional_js(&self) -> &[PathBuf] {
|
||||||
&self.additional_js
|
&self.additional_js
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_playpen_config(&self) -> &PlaypenConfig {
|
||||||
|
&self.playpen
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mut_playpen_config(&mut self) -> &mut PlaypenConfig {
|
||||||
|
&mut self.playpen
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
pub mod bookconfig;
|
pub mod bookconfig;
|
||||||
pub mod htmlconfig;
|
pub mod htmlconfig;
|
||||||
|
pub mod playpenconfig;
|
||||||
pub mod tomlconfig;
|
pub mod tomlconfig;
|
||||||
pub mod jsonconfig;
|
pub mod jsonconfig;
|
||||||
|
|
||||||
// Re-export the config structs
|
// Re-export the config structs
|
||||||
pub use self::bookconfig::BookConfig;
|
pub use self::bookconfig::BookConfig;
|
||||||
pub use self::htmlconfig::HtmlConfig;
|
pub use self::htmlconfig::HtmlConfig;
|
||||||
|
pub use self::playpenconfig::PlaypenConfig;
|
||||||
pub use self::tomlconfig::TomlConfig;
|
pub use self::tomlconfig::TomlConfig;
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
use std::path::{PathBuf, Path};
|
||||||
|
|
||||||
|
use super::tomlconfig::TomlPlaypenConfig;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
pub struct PlaypenConfig {
|
||||||
|
editor: PathBuf,
|
||||||
|
editable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlaypenConfig {
|
||||||
|
/// Creates a new `PlaypenConfig` for playpen configuration.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use std::path::PathBuf;
|
||||||
|
/// # use mdbook::config::PlaypenConfig;
|
||||||
|
/// #
|
||||||
|
/// let editor = PathBuf::from("root/editor");
|
||||||
|
/// let config = PlaypenConfig::new(PathBuf::from("root"));
|
||||||
|
///
|
||||||
|
/// assert_eq!(config.get_editor(), &editor);
|
||||||
|
/// assert_eq!(config.is_editable(), false);
|
||||||
|
/// ```
|
||||||
|
pub fn new<T: Into<PathBuf>>(root: T) -> Self {
|
||||||
|
PlaypenConfig {
|
||||||
|
editor: root.into().join("editor"),
|
||||||
|
editable: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fill_from_tomlconfig<T: Into<PathBuf>>(&mut self, root: T, tomlplaypenconfig: TomlPlaypenConfig) -> &mut Self {
|
||||||
|
let root = root.into();
|
||||||
|
|
||||||
|
if let Some(editor) = tomlplaypenconfig.editor {
|
||||||
|
if editor.is_relative() {
|
||||||
|
self.editor = root.join(editor);
|
||||||
|
} else {
|
||||||
|
self.editor = editor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(editable) = tomlplaypenconfig.editable {
|
||||||
|
self.editable = editable;
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_editable(&self) -> bool {
|
||||||
|
self.editable
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_editor(&self) -> &Path {
|
||||||
|
&self.editor
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_editor<T: Into<PathBuf>>(&mut self, root: T, editor: T) -> &mut Self {
|
||||||
|
let editor = editor.into();
|
||||||
|
|
||||||
|
if editor.is_relative() {
|
||||||
|
self.editor = root.into().join(editor);
|
||||||
|
} else {
|
||||||
|
self.editor = editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,13 @@ pub struct TomlHtmlConfig {
|
||||||
pub mathjax_support: Option<bool>,
|
pub mathjax_support: Option<bool>,
|
||||||
pub additional_css: Option<Vec<PathBuf>>,
|
pub additional_css: Option<Vec<PathBuf>>,
|
||||||
pub additional_js: Option<Vec<PathBuf>>,
|
pub additional_js: Option<Vec<PathBuf>>,
|
||||||
|
pub playpen: Option<TomlPlaypenConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
pub struct TomlPlaypenConfig {
|
||||||
|
pub editor: Option<PathBuf>,
|
||||||
|
pub editable: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a `TomlConfig` from a TOML string
|
/// Returns a `TomlConfig` from a TOML string
|
||||||
|
@ -52,5 +59,3 @@ impl TomlConfig {
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,9 @@ use preprocess;
|
||||||
use renderer::Renderer;
|
use renderer::Renderer;
|
||||||
use book::MDBook;
|
use book::MDBook;
|
||||||
use book::bookitem::{BookItem, Chapter};
|
use book::bookitem::{BookItem, Chapter};
|
||||||
use utils;
|
use config::PlaypenConfig;
|
||||||
use theme::{self, Theme};
|
use {utils, theme};
|
||||||
|
use theme::{Theme, playpen_editor};
|
||||||
use errors::*;
|
use errors::*;
|
||||||
use regex::{Regex, Captures};
|
use regex::{Regex, Captures};
|
||||||
|
|
||||||
|
@ -19,7 +20,6 @@ use handlebars::Handlebars;
|
||||||
|
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct HtmlHandlebars;
|
pub struct HtmlHandlebars;
|
||||||
|
|
||||||
|
@ -69,8 +69,11 @@ impl HtmlHandlebars {
|
||||||
// Render the handlebars template with the data
|
// Render the handlebars template with the data
|
||||||
debug!("[*]: Render template");
|
debug!("[*]: Render template");
|
||||||
let rendered = ctx.handlebars.render("index", &ctx.data)?;
|
let rendered = ctx.handlebars.render("index", &ctx.data)?;
|
||||||
|
|
||||||
let filename = Path::new(&ch.path).with_extension("html");
|
let filename = Path::new(&ch.path).with_extension("html");
|
||||||
let rendered = self.post_process(rendered, filename.file_name().unwrap().to_str().unwrap_or(""));
|
let rendered = self.post_process(rendered,
|
||||||
|
filename.file_name().unwrap().to_str().unwrap_or(""),
|
||||||
|
ctx.book.get_html_config().get_playpen_config());
|
||||||
|
|
||||||
// Write to file
|
// Write to file
|
||||||
info!("[*] Creating {:?} ✓", filename.display());
|
info!("[*] Creating {:?} ✓", filename.display());
|
||||||
|
@ -116,11 +119,11 @@ impl HtmlHandlebars {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_process(&self, rendered: String, filename: &str) -> String {
|
fn post_process(&self, rendered: String, filename: &str, playpen_config: &PlaypenConfig) -> String {
|
||||||
let rendered = build_header_links(&rendered, filename);
|
let rendered = build_header_links(&rendered, filename);
|
||||||
let rendered = fix_anchor_links(&rendered, filename);
|
let rendered = fix_anchor_links(&rendered, filename);
|
||||||
let rendered = fix_code_blocks(&rendered);
|
let rendered = fix_code_blocks(&rendered);
|
||||||
let rendered = add_playpen_pre(&rendered);
|
let rendered = add_playpen_pre(&rendered, playpen_config);
|
||||||
|
|
||||||
rendered
|
rendered
|
||||||
}
|
}
|
||||||
|
@ -171,6 +174,19 @@ impl HtmlHandlebars {
|
||||||
theme::FONT_AWESOME_TTF,
|
theme::FONT_AWESOME_TTF,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let playpen_config = book.get_html_config().get_playpen_config();
|
||||||
|
|
||||||
|
// Ace is a very large dependency, so only load it when requested
|
||||||
|
if playpen_config.is_editable() {
|
||||||
|
// Load the editor
|
||||||
|
let editor = playpen_editor::PlaypenEditor::new(playpen_config.get_editor());
|
||||||
|
book.write_file("editor.js", &editor.js)?;
|
||||||
|
book.write_file("ace.js", &editor.ace_js)?;
|
||||||
|
book.write_file("mode-rust.js", &editor.mode_rust_js)?;
|
||||||
|
book.write_file("theme-dawn.js", &editor.theme_dawn_js)?;
|
||||||
|
book.write_file("theme-tomorrow_night.js", &editor.theme_tomorrow_night_js)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,8 +289,10 @@ 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");
|
|
||||||
|
|
||||||
|
let rendered = self.post_process(rendered, "print.html",
|
||||||
|
book.get_html_config().get_playpen_config());
|
||||||
|
|
||||||
book.write_file(
|
book.write_file(
|
||||||
Path::new("print").with_extension("html"),
|
Path::new("print").with_extension("html"),
|
||||||
&rendered.into_bytes(),
|
&rendered.into_bytes(),
|
||||||
|
@ -354,6 +372,15 @@ fn make_data(book: &MDBook) -> Result<serde_json::Map<String, serde_json::Value>
|
||||||
data.insert("additional_js".to_owned(), json!(js));
|
data.insert("additional_js".to_owned(), json!(js));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if book.get_html_config().get_playpen_config().is_editable() {
|
||||||
|
data.insert("playpens_editable".to_owned(), json!(true));
|
||||||
|
data.insert("editor_js".to_owned(), json!("editor.js"));
|
||||||
|
data.insert("ace_js".to_owned(), json!("ace.js"));
|
||||||
|
data.insert("mode_rust_js".to_owned(), json!("mode-rust.js"));
|
||||||
|
data.insert("theme_dawn_js".to_owned(), json!("theme-dawn.js"));
|
||||||
|
data.insert("theme_tomorrow_night_js".to_owned(), json!("theme-tomorrow_night.js"));
|
||||||
|
}
|
||||||
|
|
||||||
let mut chapters = vec![];
|
let mut chapters = vec![];
|
||||||
|
|
||||||
for item in book.iter() {
|
for item in book.iter() {
|
||||||
|
@ -512,7 +539,7 @@ fn fix_code_blocks(html: &str) -> String {
|
||||||
.into_owned()
|
.into_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_playpen_pre(html: &str) -> String {
|
fn add_playpen_pre(html: &str, playpen_config: &PlaypenConfig) -> String {
|
||||||
let regex = Regex::new(r##"((?s)<code[^>]?class="([^"]+)".*?>(.*?)</code>)"##).unwrap();
|
let regex = Regex::new(r##"((?s)<code[^>]?class="([^"]+)".*?>(.*?)</code>)"##).unwrap();
|
||||||
regex
|
regex
|
||||||
.replace_all(html, |caps: &Captures| {
|
.replace_all(html, |caps: &Captures| {
|
||||||
|
@ -522,22 +549,18 @@ fn add_playpen_pre(html: &str) -> String {
|
||||||
|
|
||||||
if classes.contains("language-rust") && !classes.contains("ignore") {
|
if classes.contains("language-rust") && !classes.contains("ignore") {
|
||||||
// wrap the contents in an external pre block
|
// wrap the contents in an external pre block
|
||||||
|
if playpen_config.is_editable() &&
|
||||||
if text.contains("fn main") || text.contains("quick_main!") {
|
classes.contains("editable") || text.contains("fn main") || text.contains("quick_main!") {
|
||||||
format!("<pre class=\"playpen\">{}</pre>", text)
|
format!("<pre class=\"playpen\">{}</pre>", text)
|
||||||
} else {
|
} else {
|
||||||
// we need to inject our own main
|
// we need to inject our own main
|
||||||
let (attrs, code) = partition_source(code);
|
let (attrs, code) = partition_source(code);
|
||||||
format!(
|
|
||||||
"<pre class=\"playpen\"><code class=\"{}\"># #![allow(unused_variables)]
|
format!("<pre class=\"playpen\"><code class=\"{}\">\n# #![allow(unused_variables)]\n\
|
||||||
{}#fn main() {{
|
{}#fn main() {{\n\
|
||||||
\
|
{}\
|
||||||
{}
|
#}}</code></pre>",
|
||||||
#}}</code></pre>",
|
classes, attrs, code)
|
||||||
classes,
|
|
||||||
attrs,
|
|
||||||
code
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// not language-rust, so no-op
|
// not language-rust, so no-op
|
||||||
|
|
|
@ -936,6 +936,9 @@ table thead td {
|
||||||
/* Force background to be printed in Chrome */
|
/* Force background to be printed in Chrome */
|
||||||
-webkit-print-color-adjust: exact;
|
-webkit-print-color-adjust: exact;
|
||||||
}
|
}
|
||||||
|
pre > .buttons {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
a,
|
a,
|
||||||
a:visited,
|
a:visited,
|
||||||
a:active,
|
a:active,
|
||||||
|
|
|
@ -12,16 +12,25 @@ $( document ).ready(function() {
|
||||||
|
|
||||||
set_theme(theme);
|
set_theme(theme);
|
||||||
|
|
||||||
|
|
||||||
// Syntax highlighting Configuration
|
// Syntax highlighting Configuration
|
||||||
hljs.configure({
|
hljs.configure({
|
||||||
tabReplace: ' ', // 4 spaces
|
tabReplace: ' ', // 4 spaces
|
||||||
languages: [], // Languages used for auto-detection
|
languages: [], // Languages used for auto-detection
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (window.ace) {
|
||||||
|
// language-rust class needs to be removed for editable
|
||||||
|
// blocks or highlightjs will capture events
|
||||||
|
$('code.editable').removeClass('language-rust');
|
||||||
|
|
||||||
$('code').each(function(i, block) {
|
$('code').not('.editable').each(function(i, block) {
|
||||||
hljs.highlightBlock(block);
|
hljs.highlightBlock(block);
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
$('code').each(function(i, block) {
|
||||||
|
hljs.highlightBlock(block);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Adding the hljs class gives code blocks the color css
|
// Adding the hljs class gives code blocks the color css
|
||||||
// even if highlighting doesn't apply
|
// even if highlighting doesn't apply
|
||||||
|
@ -118,18 +127,32 @@ $( document ).ready(function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
function set_theme(theme) {
|
function set_theme(theme) {
|
||||||
|
let ace_theme;
|
||||||
|
|
||||||
if (theme == 'coal' || theme == 'navy') {
|
if (theme == 'coal' || theme == 'navy') {
|
||||||
$("[href='ayu-highlight.css']").prop('disabled', true);
|
$("[href='ayu-highlight.css']").prop('disabled', true);
|
||||||
$("[href='tomorrow-night.css']").prop('disabled', false);
|
$("[href='tomorrow-night.css']").prop('disabled', false);
|
||||||
$("[href='highlight.css']").prop('disabled', true);
|
$("[href='highlight.css']").prop('disabled', true);
|
||||||
|
|
||||||
|
ace_theme = "ace/theme/tomorrow_night";
|
||||||
} else if (theme == 'ayu') {
|
} else if (theme == 'ayu') {
|
||||||
$("[href='ayu-highlight.css']").prop('disabled', false);
|
$("[href='ayu-highlight.css']").prop('disabled', false);
|
||||||
$("[href='tomorrow-night.css']").prop('disabled', true);
|
$("[href='tomorrow-night.css']").prop('disabled', true);
|
||||||
$("[href='highlight.css']").prop('disabled', true);
|
$("[href='highlight.css']").prop('disabled', true);
|
||||||
|
|
||||||
|
ace_theme = "ace/theme/tomorrow_night";
|
||||||
} else {
|
} else {
|
||||||
$("[href='ayu-highlight.css']").prop('disabled', true);
|
$("[href='ayu-highlight.css']").prop('disabled', true);
|
||||||
$("[href='tomorrow-night.css']").prop('disabled', true);
|
$("[href='tomorrow-night.css']").prop('disabled', true);
|
||||||
$("[href='highlight.css']").prop('disabled', false);
|
$("[href='highlight.css']").prop('disabled', false);
|
||||||
|
|
||||||
|
ace_theme = "ace/theme/dawn";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.ace && window.editors) {
|
||||||
|
window.editors.forEach(function(editor) {
|
||||||
|
editor.setTheme(ace_theme);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
store.set('theme', theme);
|
store.set('theme', theme);
|
||||||
|
@ -187,7 +210,6 @@ $( document ).ready(function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Process playpen code blocks
|
// Process playpen code blocks
|
||||||
$(".playpen").each(function(block){
|
$(".playpen").each(function(block){
|
||||||
var pre_block = $(this);
|
var pre_block = $(this);
|
||||||
|
@ -200,18 +222,37 @@ $( document ).ready(function() {
|
||||||
buttons.prepend("<i class=\"fa fa-play play-button\"></i>");
|
buttons.prepend("<i class=\"fa fa-play play-button\"></i>");
|
||||||
buttons.prepend("<i class=\"fa fa-copy clip-button\"><i class=\"tooltiptext\"></i></i>");
|
buttons.prepend("<i class=\"fa fa-copy clip-button\"><i class=\"tooltiptext\"></i></i>");
|
||||||
|
|
||||||
|
let code_block = pre_block.find("code").first();
|
||||||
|
if (window.ace && code_block.hasClass("editable")) {
|
||||||
|
buttons.prepend("<i class=\"fa fa-history reset-button\"></i>");
|
||||||
|
}
|
||||||
|
|
||||||
buttons.find(".play-button").click(function(e){
|
buttons.find(".play-button").click(function(e){
|
||||||
run_rust_code(pre_block);
|
run_rust_code(pre_block);
|
||||||
});
|
});
|
||||||
buttons.find(".clip-button").mouseout(function(e){
|
buttons.find(".clip-button").mouseout(function(e){
|
||||||
hideTooltip(e.currentTarget);
|
hideTooltip(e.currentTarget);
|
||||||
});
|
});
|
||||||
|
buttons.find(".reset-button").click(function() {
|
||||||
|
if (!window.ace) { return; }
|
||||||
|
let editor = window.ace.edit(code_block.get(0));
|
||||||
|
editor.setValue(editor.originalCode);
|
||||||
|
editor.clearSelection();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
var clipboardSnippets = new Clipboard('.clip-button', {
|
var clipboardSnippets = new Clipboard('.clip-button', {
|
||||||
text: function(trigger) {
|
text: function(trigger) {
|
||||||
hideTooltip(trigger);
|
hideTooltip(trigger);
|
||||||
return $(trigger).parents(".playpen").find("code.language-rust.hljs")[0].textContent;
|
let playpen = $(trigger).parents(".playpen");
|
||||||
|
let code_block = playpen.find("code").first();
|
||||||
|
|
||||||
|
if (window.ace && code_block.hasClass("editable")) {
|
||||||
|
let editor = window.ace.edit(code_block.get(0));
|
||||||
|
return editor.getValue();
|
||||||
|
} else {
|
||||||
|
return code_block.get(0).textContent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
clipboardSnippets.on('success', function(e) {
|
clipboardSnippets.on('success', function(e) {
|
||||||
|
@ -221,7 +262,6 @@ $( document ).ready(function() {
|
||||||
clipboardSnippets.on('error', function(e) {
|
clipboardSnippets.on('error', function(e) {
|
||||||
showTooltip(e.trigger, "Clipboard error!");
|
showTooltip(e.trigger, "Clipboard error!");
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function hideTooltip(elem) {
|
function hideTooltip(elem) {
|
||||||
|
@ -260,7 +300,15 @@ function run_rust_code(code_block) {
|
||||||
result_block = code_block.find(".result");
|
result_block = code_block.find(".result");
|
||||||
}
|
}
|
||||||
|
|
||||||
var text = code_block.find(".language-rust").text();
|
let text;
|
||||||
|
|
||||||
|
let inner_code_block = code_block.find("code").first();
|
||||||
|
if (window.ace && inner_code_block.hasClass("editable")) {
|
||||||
|
let editor = window.ace.edit(inner_code_block.get(0));
|
||||||
|
text = editor.getValue();
|
||||||
|
} else {
|
||||||
|
text = inner_code_block.text();
|
||||||
|
}
|
||||||
|
|
||||||
var params = {
|
var params = {
|
||||||
version: "stable",
|
version: "stable",
|
||||||
|
|
|
@ -146,8 +146,15 @@
|
||||||
ga('create', '{{google_analytics}}', 'auto');
|
ga('create', '{{google_analytics}}', 'auto');
|
||||||
ga('send', 'pageview');
|
ga('send', 'pageview');
|
||||||
</script>
|
</script>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if playpens_editable}}
|
||||||
|
<script src="{{ ace_js }}" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="{{ editor_js }}" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="{{ mode_rust_js }}" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="{{ theme_dawn_js }}" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="{{ theme_tomorrow_night_js }}" type="text/javascript" charset="utf-8"></script>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
<script src="highlight.js"></script>
|
<script src="highlight.js"></script>
|
||||||
<script src="book.js"></script>
|
<script src="book.js"></script>
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
pub mod playpen_editor;
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
use errors::*;
|
use errors::*;
|
||||||
|
|
||||||
|
|
||||||
pub static INDEX: &'static [u8] = include_bytes!("index.hbs");
|
pub static INDEX: &'static [u8] = include_bytes!("index.hbs");
|
||||||
pub static CSS: &'static [u8] = include_bytes!("book.css");
|
pub static CSS: &'static [u8] = include_bytes!("book.css");
|
||||||
pub static FAVICON: &'static [u8] = include_bytes!("favicon.png");
|
pub static FAVICON: &'static [u8] = include_bytes!("favicon.png");
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
window.editors = [];
|
||||||
|
(function(editors) {
|
||||||
|
if (typeof(ace) === 'undefined' || !ace) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(".editable").each(function() {
|
||||||
|
let editor = ace.edit(this);
|
||||||
|
editor.setOptions({
|
||||||
|
highlightActiveLine: false,
|
||||||
|
showPrintMargin: false,
|
||||||
|
showLineNumbers: false,
|
||||||
|
showGutter: false,
|
||||||
|
maxLines: Infinity
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.$blockScrolling = Infinity;
|
||||||
|
|
||||||
|
editor.getSession().setMode("ace/mode/rust");
|
||||||
|
|
||||||
|
editor.originalCode = editor.getValue();
|
||||||
|
|
||||||
|
editors.push(editor);
|
||||||
|
});
|
||||||
|
})(window.editors);
|
|
@ -0,0 +1,71 @@
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use theme::load_file_contents;
|
||||||
|
|
||||||
|
pub static JS: &'static [u8] = include_bytes!("editor.js");
|
||||||
|
pub static ACE_JS: &'static [u8] = include_bytes!("ace.js");
|
||||||
|
pub static MODE_RUST_JS: &'static [u8] = include_bytes!("mode-rust.js");
|
||||||
|
pub static THEME_DAWN_JS: &'static [u8] = include_bytes!("theme-dawn.js");
|
||||||
|
pub static THEME_TOMORROW_NIGHT_JS: &'static [u8] = include_bytes!("theme-tomorrow_night.js");
|
||||||
|
|
||||||
|
/// Integration of a JavaScript editor for playpens.
|
||||||
|
/// Uses the Ace editor: https://ace.c9.io/.
|
||||||
|
/// The Ace editor itself, the mode, and the theme files are the
|
||||||
|
/// generated minified no conflict versions.
|
||||||
|
///
|
||||||
|
/// The `PlaypenEditor` struct should be used instead of the static variables because
|
||||||
|
/// the `new()` method
|
||||||
|
/// will look if the user has an editor directory in his source folder and use
|
||||||
|
/// the users editor instead
|
||||||
|
/// of the default.
|
||||||
|
///
|
||||||
|
/// You should exceptionnaly use the static variables only if you need the
|
||||||
|
/// default editor even if the
|
||||||
|
/// user has specified another editor.
|
||||||
|
pub struct PlaypenEditor {
|
||||||
|
pub js: Vec<u8>,
|
||||||
|
pub ace_js: Vec<u8>,
|
||||||
|
pub mode_rust_js: Vec<u8>,
|
||||||
|
pub theme_dawn_js: Vec<u8>,
|
||||||
|
pub theme_tomorrow_night_js: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlaypenEditor {
|
||||||
|
pub fn new(src: &Path) -> Self {
|
||||||
|
let mut editor = PlaypenEditor {
|
||||||
|
js: JS.to_owned(),
|
||||||
|
ace_js: ACE_JS.to_owned(),
|
||||||
|
mode_rust_js: MODE_RUST_JS.to_owned(),
|
||||||
|
theme_dawn_js: THEME_DAWN_JS.to_owned(),
|
||||||
|
theme_tomorrow_night_js: THEME_TOMORROW_NIGHT_JS.to_owned(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if the given path exists
|
||||||
|
if !src.exists() || !src.is_dir() {
|
||||||
|
return editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for individual files if they exist
|
||||||
|
{
|
||||||
|
let files = vec![
|
||||||
|
(src.join("editor.js"), &mut editor.js),
|
||||||
|
(src.join("ace.js"), &mut editor.ace_js),
|
||||||
|
(src.join("mode-rust.js"), &mut editor.mode_rust_js),
|
||||||
|
(src.join("theme-dawn.js"), &mut editor.theme_dawn_js),
|
||||||
|
(src.join("theme-tomorrow_night.js"), &mut editor.theme_tomorrow_night_js),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (filename, dest) in files {
|
||||||
|
if !filename.exists() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = load_file_contents(&filename, dest) {
|
||||||
|
warn!("Couldn't load custom file, {}: {}", filename.display(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
editor
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,6 +30,10 @@
|
||||||
-webkit-print-color-adjust: exact
|
-webkit-print-color-adjust: exact
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pre > .buttons {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
a, a:visited, a:active, a:hover {
|
a, a:visited, a:active, a:hover {
|
||||||
color: #4183c4
|
color: #4183c4
|
||||||
text-decoration: none
|
text-decoration: none
|
||||||
|
|
|
@ -59,7 +59,49 @@ fn from_toml_authors() {
|
||||||
assert_eq!(config.get_authors(), &[String::from("John Doe"), String::from("Jane Doe")]);
|
assert_eq!(config.get_authors(), &[String::from("John Doe"), String::from("Jane Doe")]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that the `output.html.destination` key is correctly parsed in the TOML config
|
// Tests that the default `playpen` config is correct in the TOML config
|
||||||
|
#[test]
|
||||||
|
fn from_toml_playpen_default() {
|
||||||
|
let toml = "";
|
||||||
|
|
||||||
|
let parsed = TomlConfig::from_toml(toml).expect("This should parse");
|
||||||
|
let config = BookConfig::from_tomlconfig("root", parsed);
|
||||||
|
|
||||||
|
let playpenconfig = config.get_html_config().get_playpen_config();
|
||||||
|
|
||||||
|
assert_eq!(playpenconfig.get_editor(), PathBuf::from("root/theme/editor"));
|
||||||
|
assert_eq!(playpenconfig.is_editable(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that the `playpen.editor` key is correctly parsed in the TOML config
|
||||||
|
#[test]
|
||||||
|
fn from_toml_playpen_editor() {
|
||||||
|
let toml = r#"[output.html.playpen]
|
||||||
|
editor = "editordir""#;
|
||||||
|
|
||||||
|
let parsed = TomlConfig::from_toml(toml).expect("This should parse");
|
||||||
|
let config = BookConfig::from_tomlconfig("root", parsed);
|
||||||
|
|
||||||
|
let playpenconfig = config.get_html_config().get_playpen_config();
|
||||||
|
|
||||||
|
assert_eq!(playpenconfig.get_editor(), PathBuf::from("root/theme/editordir"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that the `playpen.editable` key is correctly parsed in the TOML config
|
||||||
|
#[test]
|
||||||
|
fn from_toml_playpen_editable() {
|
||||||
|
let toml = r#"[output.html.playpen]
|
||||||
|
editable = true"#;
|
||||||
|
|
||||||
|
let parsed = TomlConfig::from_toml(toml).expect("This should parse");
|
||||||
|
let config = BookConfig::from_tomlconfig("root", parsed);
|
||||||
|
|
||||||
|
let playpenconfig = config.get_html_config().get_playpen_config();
|
||||||
|
|
||||||
|
assert_eq!(playpenconfig.is_editable(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that the `output.html.destination` key is correcly parsed in the TOML config
|
||||||
#[test]
|
#[test]
|
||||||
fn from_toml_output_html_destination() {
|
fn from_toml_output_html_destination() {
|
||||||
let toml = r#"[output.html]
|
let toml = r#"[output.html]
|
||||||
|
|
Loading…
Reference in New Issue