-
-
Notifications
You must be signed in to change notification settings - Fork 153
/
raw_html.ex
119 lines (96 loc) · 3.1 KB
/
raw_html.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
defmodule Floki.RawHTML do
@moduledoc false
@self_closing_tags [
"area",
"base",
"br",
"col",
"command",
"embed",
"hr",
"img",
"input",
"keygen",
"link",
"meta",
"param",
"source",
"track",
"wbr"
]
@encoder &HtmlEntities.encode/1
def raw_html(html_tree, options) do
encoder =
case Keyword.fetch(options, :encode) do
{:ok, true} -> @encoder
{:ok, false} -> & &1
:error -> default_encoder()
end
build_raw_html(html_tree, "", encoder)
end
defp build_raw_html([], html, _encoder), do: html
defp build_raw_html(string, _html, encoder) when is_binary(string), do: encoder.(string)
defp build_raw_html(tuple, html, encoder) when is_tuple(tuple),
do: build_raw_html([tuple], html, encoder)
defp build_raw_html([string | tail], html, encoder) when is_binary(string) do
build_raw_html(tail, html <> encoder.(string), encoder)
end
defp build_raw_html([{:comment, comment} | tail], html, encoder),
do: build_raw_html(tail, html <> "<!--#{comment}-->", encoder)
defp build_raw_html([{:pi, "xml", attrs} | tail], html, encoder) do
build_raw_html(tail, html <> "<?xml " <> tag_attrs(attrs) <> "?>", encoder)
end
defp build_raw_html([{:doctype, type, public, system} | tail], html, encoder) do
attr =
case {public, system} do
{"", ""} -> ""
{"", system} -> " SYSTEM \"#{system}\""
{public, system} -> " PUBLIC \"#{public}\" \"#{system}\""
end
build_raw_html(tail, html <> "<!DOCTYPE #{type}#{attr}>", encoder)
end
defp build_raw_html([{type, attrs, children} | tail], html, encoder) do
build_raw_html(tail, html <> tag_for(type, attrs, children, encoder), encoder)
end
defp tag_attrs(attr_list) do
attr_list
|> Enum.reduce("", &build_attrs/2)
|> String.trim()
end
defp tag_with_attrs(type, [], children), do: "<#{type}" <> close_open_tag(type, children)
defp tag_with_attrs(type, attrs, children) do
"<#{type} #{tag_attrs(attrs)}" <> close_open_tag(type, children)
end
defp close_open_tag(type, []) when type in @self_closing_tags, do: "/>"
defp close_open_tag(_type, _), do: ">"
defp close_end_tag(type, []) when type in @self_closing_tags, do: ""
defp close_end_tag(type, _), do: "</#{type}>"
defp build_attrs({attr, iodata}, attrs) when is_list(iodata) do
build_attrs({attr, IO.iodata_to_binary(iodata)}, attrs)
end
defp build_attrs({attr, <<value::binary>>}, attrs) do
if String.contains?(value, "\"") do
~s(#{attrs} #{attr}='#{value}')
else
~s(#{attrs} #{attr}="#{value}")
end
end
defp build_attrs(<<attr::binary>>, attrs), do: "#{attrs} #{attr}"
defp tag_for(type, attrs, children, encoder) do
encoder =
case type do
"script" -> & &1
"style" -> & &1
_ -> encoder
end
tag_with_attrs(type, attrs, children) <>
build_raw_html(children, "", encoder) <> close_end_tag(type, children)
end
defp default_encoder do
if Application.get_env(:floki, :encode_raw_html, true) do
@encoder
else
& &1
end
end
end