use reader_for_microxml::*;
use dodrio::{
builder::{text, ElementBuilder},
bumpalo::{self},
Attribute, Listener, Node, RenderContext, RootRender, VdomWeak,
};
use unwrap::unwrap;
#[derive(Clone, Copy)]
pub enum HtmlOrSvg {
Html,
Svg,
}
pub trait HtmlTemplating {
fn replace_with_string(&self, fn_name: &str) -> String;
fn retain_next_node_or_attribute<'a>(&self, fn_name: &str) -> bool;
fn replace_with_nodes<'a>(&self, cx: &mut RenderContext<'a>, fn_name: &str) -> Vec<Node<'a>>;
fn set_event_listener(
&self,
fn_name: String,
) -> Box<dyn Fn(&mut dyn RootRender, VdomWeak, web_sys::Event) + 'static>;
fn render_template<'a>(
&self,
cx: &mut RenderContext<'a>,
html_template: &str,
html_or_svg_parent: HtmlOrSvg,
) -> Result<Node<'a>, String> {
let mut reader_for_microxml = ReaderForMicroXml::new(html_template);
let mut dom_path = Vec::new();
let mut root_element;
let mut html_or_svg_local = html_or_svg_parent;
let bump = cx.bump;
#[allow(clippy::single_match_else, clippy::wildcard_enum_match_arm)]
match reader_for_microxml.next() {
None => {
return Err("Error: no root element".to_owned());
}
Some(result_token) => {
match result_token {
Result::Err(e) => {
return Err(format!("Error: {}", e));
}
Result::Ok(token) => {
match token {
Token::StartElement(name) => {
dom_path.push(name.to_owned());
let name = bumpalo::format!(in bump, "{}",name).into_bump_str();
root_element = ElementBuilder::new(bump, name);
if name == "svg" {
html_or_svg_local = HtmlOrSvg::Svg;
}
if let HtmlOrSvg::Svg = html_or_svg_local {
root_element =
root_element.namespace(Some("http://www.w3.org/2000/svg"));
}
match self.fill_element_builder(
&mut reader_for_microxml,
root_element,
cx,
html_or_svg_local,
&mut dom_path,
) {
Ok(new_root_element) => root_element = new_root_element,
Err(err) => {
return Err(err);
}
}
}
_ => {
return Err("Error: no root element".to_owned());
}
}
}
}
}
}
Ok(root_element.finish())
}
#[allow(clippy::too_many_lines, clippy::type_complexity)]
fn fill_element_builder<'a>(
&self,
reader_for_microxml: &mut ReaderForMicroXml,
mut element: ElementBuilder<
'a,
bumpalo::collections::Vec<'a, Listener<'a>>,
bumpalo::collections::Vec<'a, Attribute<'a>>,
bumpalo::collections::Vec<'a, Node<'a>>,
>,
cx: &mut RenderContext<'a>,
html_or_svg_parent: HtmlOrSvg,
dom_path: &mut Vec<String>,
) -> Result<
ElementBuilder<
'a,
bumpalo::collections::Vec<'a, Listener<'a>>,
bumpalo::collections::Vec<'a, Attribute<'a>>,
bumpalo::collections::Vec<'a, Node<'a>>,
>,
String,
> {
let mut replace_string: Option<String> = None;
let mut replace_vec_nodes: Option<Vec<Node>> = None;
let mut replace_boolean: Option<bool> = None;
let mut html_or_svg_local;
let bump = cx.bump;
loop {
html_or_svg_local = html_or_svg_parent;
match reader_for_microxml.next() {
None => {}
Some(result_token) => {
match result_token {
Result::Err(e) => {
return Err(format!("Error: {}", e));
}
Result::Ok(token) => {
match token {
Token::StartElement(name) => {
dom_path.push(name.to_owned());
let name = bumpalo::format!(in bump, "{}",name).into_bump_str();
let mut child_element = ElementBuilder::new(bump, name);
if name == "svg" {
html_or_svg_local = HtmlOrSvg::Svg;
}
if let HtmlOrSvg::Svg = html_or_svg_local {
child_element = child_element
.namespace(Some("http://www.w3.org/2000/svg"));
}
if name == "foreignObject" {
html_or_svg_local = HtmlOrSvg::Html;
}
child_element = self.fill_element_builder(
reader_for_microxml,
child_element,
cx,
html_or_svg_local,
dom_path,
)?;
if replace_boolean.unwrap_or(true) {
if let Some(repl_vec_nodes) = replace_vec_nodes {
for repl_node in repl_vec_nodes {
element = element.child(repl_node);
}
replace_vec_nodes = None;
} else {
element = element.child(child_element.finish());
}
}
if replace_boolean.is_some() {
replace_boolean = None;
}
}
Token::Attribute(name, value) => {
if name.starts_with("data-wt-") {
let fn_name = value;
if &fn_name[..3] != "wt_" {
return Err(format!(
"{} value does not start with wt_ : {}.",
name, fn_name
));
}
let repl_txt = self.replace_with_string(fn_name);
replace_string = Some(repl_txt);
} else if name.starts_with("data-on-") {
let fn_name = value.to_string();
let event_to_listen = unwrap!(name.get(8..)).to_string();
if !fn_name.is_empty() && &fn_name[..3] != "wl_" {
return Err(format!(
"{} value does not start with wl_ : {}.",
name, fn_name
));
}
let event_to_listen =
bumpalo::format!(in bump, "{}",&event_to_listen)
.into_bump_str();
element = element
.on(event_to_listen, self.set_event_listener(fn_name));
} else {
let name =
bumpalo::format!(in bump, "{}",name).into_bump_str();
let value2;
if let Some(repl) = replace_string {
value2 =
bumpalo::format!(in bump, "{}",decode_5_xml_control_characters(&repl))
.into_bump_str();
replace_string = None;
} else {
value2 =
bumpalo::format!(in bump, "{}",decode_5_xml_control_characters(value))
.into_bump_str();
}
element = element.attr(name, value2);
}
}
Token::TextNode(txt) => {
let txt2;
if let Some(repl) = replace_string {
txt2 =
bumpalo::format!(in bump, "{}",decode_5_xml_control_characters(&repl))
.into_bump_str();
replace_string = None;
} else {
txt2 = bumpalo::format!(in bump, "{}",decode_5_xml_control_characters(txt))
.into_bump_str();
}
element = element.child(text(txt2));
}
Token::Comment(txt) => {
if txt == "end_of_wt" {
} else if txt.starts_with("wt_") {
let repl_txt = self.replace_with_string(txt);
replace_string = Some(repl_txt);
} else if txt.starts_with("wn_") {
let repl_vec_nodes = self.replace_with_nodes(cx, txt);
replace_vec_nodes = Some(repl_vec_nodes);
} else if txt.starts_with("wb_") {
replace_boolean =
Some(self.retain_next_node_or_attribute(txt));
} else {
}
}
Token::EndElement(name) => {
let last_name = unwrap!(dom_path.pop());
if last_name == name || name == "" {
return Ok(element);
} else {
return Err(format!(
"End element not correct: starts <{}> ends </{}>",
last_name, name
));
}
}
}
}
}
}
}
}
}
}
pub fn empty_div<'a>(cx: &mut RenderContext<'a>) -> Node<'a> {
let bump = cx.bump;
ElementBuilder::new(bump, "div").finish()
}
pub fn decode_5_xml_control_characters(input: &str) -> String {
input
.replace(""", "\"")
.replace("'", "'")
.replace("&", "&")
.replace("<", "<")
.replace(">", ">")
}