/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use crate::elements::*;
use crate::format::html::html_writer::HTMLWriter;
use crate::format::style::{get_code_theme_for_theme, get_css_for_theme};
use crate::format::PlaceholderTemplate;
use crate::references::glossary::{GlossaryDisplay, GlossaryReference};
use crate::references::templates::{Template, TemplateVariable};
use asciimath_rs::format::mathml::ToMathML;
use htmlescape::encode_attribute;
use minify::html::minify;
use std::io;
use syntect::html::highlighted_html_for_string;
const MATHJAX_URL: &str = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js";
pub trait ToHtml {
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()>;
}
impl ToHtml for Element {
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
match self {
Element::Block(block) => block.to_html(writer),
Element::Inline(inline) => inline.to_html(writer),
Element::Line(line) => line.to_html(writer),
}
}
}
impl ToHtml for Line {
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
match self {
Line::Text(text) => text.to_html(writer),
Line::Ruler(ruler) => ruler.to_html(writer),
Line::RefLink(anchor) => anchor.to_html(writer),
Line::Centered(centered) => centered.to_html(writer),
Line::Anchor(a) => a.to_html(writer),
_ => Ok(()),
}
}
}
impl ToHtml for Inline {
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
match self {
Inline::Url(url) => url.to_html(writer),
Inline::Monospace(mono) => mono.to_html(writer),
Inline::Striked(striked) => striked.to_html(writer),
Inline::Plain(plain) => plain.to_html(writer),
Inline::Italic(italic) => italic.to_html(writer),
Inline::Underlined(under) => under.to_html(writer),
Inline::Bold(bold) => bold.to_html(writer),
Inline::Image(img) => img.to_html(writer),
Inline::Placeholder(placeholder) => placeholder.read().unwrap().to_html(writer),
Inline::Superscript(superscript) => superscript.to_html(writer),
Inline::Checkbox(checkbox) => checkbox.to_html(writer),
Inline::Emoji(emoji) => emoji.to_html(writer),
Inline::Colored(colored) => colored.to_html(writer),
Inline::BibReference(bibref) => bibref.read().unwrap().to_html(writer),
Inline::TemplateVar(var) => var.read().unwrap().to_html(writer),
Inline::Math(m) => m.to_html(writer),
Inline::LineBreak => writer.write("
".to_string()),
Inline::CharacterCode(code) => code.to_html(writer),
Inline::GlossaryReference(gloss) => gloss.lock().to_html(writer),
Inline::Arrow(a) => a.to_html(writer),
Inline::Anchor(a) => a.to_html(writer),
}
}
}
impl ToHtml for Block {
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
match self {
Block::Paragraph(para) => para.to_html(writer),
Block::List(list) => list.to_html(writer),
Block::Table(table) => table.to_html(writer),
Block::CodeBlock(code) => code.to_html(writer),
Block::Quote(quote) => quote.to_html(writer),
Block::Section(section) => section.to_html(writer),
Block::Import(import) => import.to_html(writer),
Block::Placeholder(placeholder) => placeholder.read().unwrap().to_html(writer),
Block::MathBlock(m) => m.to_html(writer),
_ => Ok(()),
}
}
}
impl ToHtml for MetadataValue {
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
match self {
MetadataValue::String(string) => writer.write_escaped(string.clone()),
MetadataValue::Integer(num) => writer.write(num.to_string()),
MetadataValue::Placeholder(ph) => ph.read().unwrap().to_html(writer),
MetadataValue::Bool(b) => writer.write(b.to_string()),
MetadataValue::Float(f) => writer.write(f.to_string()),
MetadataValue::Template(t) => t.to_html(writer),
}
}
}
impl ToHtml for Document {
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
let path = if let Some(path) = &self.path {
format!("path=\"{}\"", encode_attribute(path.as_str()))
} else {
"".to_string()
};
if self.is_root {
let metadata = self.config.lock().metadata.clone();
let style = minify(get_css_for_theme(writer.get_theme()).as_str());
writer.write("".to_string())?;
writer.write("
".to_string())?; cell.text.to_html(writer)?; writer.write(" | ".to_string())?; } writer.write("
---|
0 {
writer.write(" lang=\"".to_string())?;
writer.write_attribute(self.language.clone())?;
writer.write("\">".to_string())?;
let (theme, syntax_set) = get_code_theme_for_theme(writer.get_theme());
if let Some(syntax) = syntax_set.find_syntax_by_token(self.language.as_str()) {
writer.write(highlighted_html_for_string(
self.code.as_str(),
&syntax_set,
syntax,
&theme,
))?;
} else {
writer.write("".to_string())?;
writer.write_escaped(self.code.clone())?;
writer.write("
".to_string())?;
}
} else {
writer.write(">".to_string())?;
writer.write_escaped(self.code.clone())?;
writer.write("
".to_string())?;
}
writer.write("
".to_string())?; for line in &self.text { line.to_html(writer)?; writer.write("
".to_string())?; } if let Some(meta) = self.metadata.clone() { writer.write(" ".to_string())?; } writer.write("
".to_string())?;
writer.write_escaped(self.value.clone())?;
writer.write("
".to_string())
}
}
impl ToHtml for Url {
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
writer.write("".to_string())?;
if let Some(description) = self.description.clone() {
for desc in description {
desc.to_html(writer)?;
}
} else {
writer.write_escaped(self.url.clone())?;
}
writer.write("".to_string())
}
}
impl ToHtml for PlainText {
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
writer.write_escaped(self.value.clone())
}
}
impl ToHtml for Placeholder {
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
if let Some(value) = &self.value {
value.to_html(writer)
} else {
log::debug!("Unknown placeholder [[{}]]", self.name.clone());
writer.write_escaped(format!("[[{}]]", self.name.clone()))?;
writer.write("".to_string())
}
}
}
impl ToHtml for RefLink {
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
writer.write("".to_string())?;
self.description.to_html(writer)?;
writer.write("".to_string())
}
}
impl ToHtml for InlineMetadata {
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
if let Some(MetadataValue::String(format)) = self.data.get("display") {
let mut template = PlaceholderTemplate::new(format.clone());
self.data
.iter()
.for_each(|(k, v)| template.add_replacement(k, &v.to_string()));
writer.write(template.render())?;
} else {
for (k, v) in &self.data {
writer.write_escaped(format!("{}={},", k, v.to_string()))?;
}
}
Ok(())
}
}
impl ToHtml for Centered {
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
writer.write("