Current section
Files
Jump to
Current section
Files
lib/sneeze.ex
defmodule Sneeze do
alias Sneeze.Internal
@doc ~s"""
Render a data-structure, containing 'elements' to html.
An element is either:
- [tag, attribute_map | body]
- [tag, attribute_map]
- [tag | body]
- [tag]
- [:@__raw_html, html_string]
- [:script, attribute_map, script_text]
- [:script, script_text]
- [:style, attribute_map, style_text]
- [:style, style_text]
- A bare, stringable value (such as a string or number)
- A list of elements
The content of `:__@raw_html`, `:style` and `:script` elements will not be escaped.
All other elements content will be html-escaped.
Examples:
```
render([:p, %{class: "outlined"}, "hello"])
render([:br])
render([[:span, "one"], [:span, %{class: "highlight"}, "two"]])
render([:ul, %{id: "some-list"},
[:li, [:a, %{href: "/"}, "Home"]],
[:li, [:a, %{href: "/about"}, "About"]]])
render([:__@raw_html, "<!DOCTYPE html>"])
render([:script, "console.log(42 < 9);"])
```
"""
def render(data) do
_render(data)
end
defp _render(data) do
case data do
[] ->
""
# list with tag
[tag] when is_atom(tag) ->
if Internal.is_void_tag?(tag) do
Internal.render_void_tag(tag)
else
Internal.render_tag(tag)
end
# script tags
[:script, attributes, script_body] when is_map(attributes) ->
Internal.render_opening_tag(:script, attributes)
<> script_body
<> Internal.render_closing_tag(:script)
[:script, script_body] ->
Internal.render_opening_tag(:script)
<> script_body
<> Internal.render_closing_tag(:script)
# style tags
[:style, attributes, style_body] when is_map(attributes) ->
Internal.render_opening_tag(:style, attributes)
<> style_body
<> Internal.render_closing_tag(:style)
[:style, style_body] ->
Internal.render_opening_tag(:style)
<> style_body
<> Internal.render_closing_tag(:style)
# list with tag and attribute map
[tag, attributes] when is_atom(tag) and is_map(attributes) ->
if Internal.is_void_tag?(tag) do
Internal.render_void_tag(tag, attributes)
else
Internal.render_tag(tag, attributes)
end
[:__@raw_html, html_string] ->
html_string
# list with tag, attribute map and child nodes
[tag, attributes | body] when is_map(attributes) ->
if Internal.is_void_tag?(tag) do
Internal.render_void_tag(tag, attributes)
<> _render(body) # not actually body, next elements
else
Internal.render_opening_tag(tag, attributes)
<> _render_body(body)
<> Internal.render_closing_tag(tag)
end
# list with tag and child nodes
[tag | body] when is_atom(tag) ->
if Internal.is_void_tag?(tag) do
Internal.render_void_tag(tag)
<> _render_body(body) # not actually body, next elements
else
Internal.render_opening_tag(tag)
<> _render_body(body)
<> Internal.render_closing_tag(tag)
end
# list with list of child nodes
[node] when is_list(node) ->
_render node
# list with sub-list as first element
[node | rest] when is_list(node) ->
_render(node) <> _render(rest)
# list with single, stringible member
[node] ->
to_string(node) |> HtmlEntities.encode
# any non-list node
bare_node ->
to_string(bare_node) |> HtmlEntities.encode
end
end
defp _render_body(body_elements) do
body_elements
|> Enum.map(&_render/1)
|> Enum.join("")
end
end