///|
using @bobzhang/html_parser {
type HtmlError,
type ParsedHtml,
type Node,
type NodeKind,
parse_fragment,
parse,
}
///|
/// Convert parsed html from @bobzhang/html_parser to the MoonBit Html DSL
fn convert_parsed_html(nodes : ParsedHtml) -> @h.Html {
return convert_node(nodes.root)
}
///|
/// Convert raw html string to the MoonBit Html DSL.
///
/// Inputs that contain an explicit ``, ``, or `` start tag
/// are parsed as a full document, which preserves those structural tags.
/// Everything else is parsed as a fragment.
fn convert_to_dsl(str : String) -> @h.Html raise HtmlError {
if has_document_tags(str) {
str |> parse() |> convert_parsed_html()
} else {
str |> parse_fragment() |> convert_parsed_html()
}
}
///|
/// Check whether the input contains an explicit ``, ``, or
/// `` start tag. Tag names are matched case-insensitively; comments
/// and raw-text bodies (script/style/textarea) are ignored because the
/// tokenizer already treats them as character data.
fn has_document_tags(str : String) -> Bool {
for token in @bobzhang/html_parser.tokenize(str).tokens {
match token {
@bobzhang/html_parser.HtmlToken::Tag(t) =>
if t.kind == @bobzhang/html_parser.TagKind::StartTag &&
(t.name == "html" || t.name == "head" || t.name == "body") {
return true
}
_ => ()
}
}
false
}
///|
/// Convert raw html string to the MoonBit Html DSL
pub fn convert_html(str : String) -> String raise HtmlError {
let code = str |> minify_html() |> convert_to_dsl() |> print_html_dsl()
"let html_code = \{code}" |> @fmt.format()
}
///|
/// Convert a single @bobzhang/html_parser node to the MoonBit Html DSL.
///
/// Document and fragment roots become `@h.fragment`, comments become
/// `@h.comment`, and doctypes become `@h.doctype`.
fn convert_node(node : Node) -> @h.Html {
match node.kind() {
NodeKind::Document | NodeKind::Fragment =>
@h.fragment(node.children().map(convert_node))
NodeKind::Element => convert_element(node)
NodeKind::Text => @h.text(node.data())
NodeKind::Comment => @h.comment(node.data())
NodeKind::Doctype => convert_doctype(node)
}
}
///|
/// Convert a doctype node to a DSL doctype declaration.
fn convert_doctype(node : Node) -> @h.Html {
match (node.public_id, node.system_id) {
(Some(public_id), Some(system_id)) =>
@h.doctype(name=node.data(), public_id~, system_id~)
(Some(public_id), None) => @h.doctype(name=node.data(), public_id~)
(None, Some(system_id)) => @h.doctype(name=node.data(), system_id~)
(None, None) => @h.doctype(name=node.data())
}
}
///|
/// Convert an element node: tag name, attributes, and recursively converted
/// children.
///
/// SVG elements go through the `@svg` builders; `