defmodule MfmParser.Encoder do alias MfmParser.Parser alias MfmParser.Node import Temple @moduledoc """ An encoder who can turn a tree into HTML. It only works for the MFM specific tags of the form $[name.opts content]. Other parts of MFM (html, Markdown and [KaTeX](https://katex.org/)) are out of scope for this project. It can directly take input from function `MfmParser.Parser.parse`. ## Examples iex> [ ...> %MfmParser.Node.MFM.Twitch{ ...> children: [%MfmParser.Node.Text{props: %{text: "🍮"}}], ...> props: %{speed: "5s"} ...> } ...> ] ...> |> MfmParser.Encoder.to_html() "🍮" iex> MfmParser.Parser.parse("$[twitch.speed=5s 🍮]") |> MfmParser.Encoder.to_html() "🍮" """ def to_html(tree) when is_list(tree) do to_html_styles(tree) end def to_html(input) when is_binary(input) do Parser.parse(input) |> to_html() end defp to_html_styles(tree) do tree |> Enum.reduce("", fn node, html -> html <> case node do %Node.Text{} -> node.props.text %Node.Newline{} -> node.props.text %Node.MFM.Flip{} -> html_child = to_html_styles(node.children) case node.props do %{v: true, h: true} -> temple do span class: "mfm", style: "display: inline-block; transform: scale(-1);" do html_child end end %{v: true} -> "#{html_child}" _ -> "#{html_child}" end %Node.MFM.Font{} -> html_child = to_html_styles(node.children) temple do span class: "mfm", style: "display: inline-block; font-family: #{node.props.font};" do html_child end end %Node.MFM.X{} -> html_child = to_html_styles(node.children) prop_map = %{"200%" => "2", "400%" => "3", "600%" => "4"} temple do span style: "font-size: #{node.props.size}", class: "mfm _mfm_x#{prop_map[node.props.size]}_" do html_child end end %Node.MFM.Blur{} -> html_child = to_html_styles(node.children) temple do span(class: "mfm _mfm_blur_", do: html_child) end %Node.MFM.Jelly{} -> html_child = to_html_styles(node.children) "#{html_child}" %Node.MFM.Tada{} -> html_child = to_html_styles(node.children) temple do span class: "mfm _mfm_tada_", style: "display: inline-block; font-size: 150%; animation: #{node.props.speed} linear 0s infinite normal both running mfm-tada;" do html_child end end %Node.MFM.Jump{} -> html_child = to_html_styles(node.children) "#{html_child}" %Node.MFM.Bounce{} -> html_child = to_html_styles(node.children) "#{html_child}" %Node.MFM.Spin{} -> html_child = to_html_styles(node.children) keyframe_names_map = %{ "x" => "mfm-spinX", "y" => "mfm-spinY", "z" => "mfm-spin" } directions_map = %{ "left" => "reverse" } "#{html_child}" %Node.MFM.Shake{} -> html_child = to_html_styles(node.children) temple do span class: "mfm", style: "display: inline-block; animation: #{node.props.speed} ease 0s infinite normal none running mfm-shake;" do html_child end end %Node.MFM.Twitch{} -> html_child = to_html_styles(node.children) temple do span class: "mfm", style: "display: inline-block; animation: #{node.props.speed} ease 0s infinite normal none running mfm-twitch;" do html_child end end %Node.MFM.Rainbow{} -> html_child = to_html_styles(node.children) "#{html_child}" %Node.MFM.Sparkle{} -> # TODO: This is not how Misskey does it and should be changed to make it work like Misskey. html_child = to_html_styles(node.children) "#{html_child}" %Node.MFM.Rotate{} -> html_child = to_html_styles(node.children) "#{html_child}" %Node.MFM.Undefined{} -> html_child = to_html_styles(node.children) "#{html_child}" _ -> html end end) end end