@@ -24,7 +24,6 @@ defmodule Phoenix.HTML.Tag do
2424
2525 import Phoenix.HTML
2626
27- @ special_attributes [ "data" , "aria" , "class" ]
2827 @ csrf_param "_csrf_token"
2928
3029 @ doc ~S"""
@@ -64,7 +63,7 @@ defmodule Phoenix.HTML.Tag do
6463 def tag ( name ) , do: tag ( name , [ ] )
6564
6665 def tag ( name , attrs ) when is_list ( attrs ) do
67- { :safe , [ ?< , to_string ( name ) , build_attrs ( attrs ) |> Enum . sort ( ) |> tag_attrs ( ) , ?> ] }
66+ { :safe , [ ?< , to_string ( name ) , sorted_attrs ( attrs ) , ?> ] }
6867 end
6968
7069 @ doc ~S"""
@@ -103,96 +102,71 @@ defmodule Phoenix.HTML.Tag do
103102 name = to_string ( name )
104103 { :safe , escaped } = html_escape ( content )
105104
106- { :safe ,
107- [ ?< , name , build_attrs ( attrs ) |> Enum . sort ( ) |> tag_attrs ( ) , ?> , escaped , ?< , ?/ , name , ?> ] }
105+ { :safe , [ ?< , name , sorted_attrs ( attrs ) , ?> , escaped , ?< , ?/ , name , ?> ] }
108106 end
109107
110- @ doc """
108+ @ doc ~S """
111109 Escapes an enumerable of attributes, returning iodata.
112110
113111 Pay attention that, unlike `tag/2` and `content_tag/2`, this
114112 function does not sort the attributes. However if given a map,
115113 note also that the key ordering may change.
116114
117- iex> attributes_escape(title: "the title", id: "the id", selected: true)
118- {:safe,
119- [
120- [32, "title", 61, 34, "the title", 34],
121- [32, "id", 61, 34, "the id", 34],
122- [32, "selected"]
123- ]}
124-
125- iex> attributes_escape(%{data: [phx: [value: [foo: "bar"]]], class: "foo"})
126- {:safe,
127- [
128- [32, "class", 61, 34, "foo", 34],
129- [32, "data-phx-value-foo", 61, 34, "bar", 34]
130- ]}
115+ iex> safe_to_string attributes_escape(title: "the title", id: "the id", selected: true)
116+ " title=\" the title\" id=\" the id\" selected"
117+
118+ iex> safe_to_string attributes_escape(%{data: [phx: [value: [foo: "bar"]]], class: "foo"})
119+ " class=\" foo\" data-phx-value-foo=\" bar\" "
131120
132121 """
122+ def attributes_escape ( attrs ) when is_list ( attrs ) do
123+ { :safe , build_attrs ( attrs ) }
124+ end
125+
133126 def attributes_escape ( attrs ) do
134- { :safe , attrs |> build_attrs ( ) |> Enum . reverse ( ) |> tag_attrs ( ) }
127+ { :safe , attrs |> Enum . to_list ( ) |> build_attrs ( ) }
135128 end
136129
137- defp build_attrs ( [ ] ) , do: [ ]
138- defp build_attrs ( [ _ | _ ] = attrs ) , do: build_attrs ( attrs , [ ] )
139- defp build_attrs ( attrs ) , do: attrs |> Enum . to_list ( ) |> build_attrs ( [ ] )
130+ defp build_attrs ( [ { "data" , v } | t ] ) when is_list ( v ) ,
131+ do: nested_attrs ( v , " data" , t )
140132
141- defp build_attrs ( [ ] , acc ) , do: acc
133+ defp build_attrs ( [ { "aria" , v } | t ] ) when is_list ( v ) ,
134+ do: nested_attrs ( v , " aria" , t )
142135
143- defp build_attrs ( [ { k , v } | t ] , acc ) when k in @ special_attributes do
144- build_attrs ( [ { String . to_atom ( k ) , v } | t ] , acc )
145- end
136+ defp build_attrs ( [ { "class" , v } | t ] ) when is_list ( v ) ,
137+ do: [ " class=\" " , class_value ( v ) , ?" | build_attrs ( t ) ]
146138
147- defp build_attrs ( [ { :data , v } | t ] , acc ) when is_list ( v ) do
148- build_attrs ( t , nested_attrs ( "data" , v , acc ) )
149- end
139+ defp build_attrs ( [ { :data , v } | t ] ) when is_list ( v ) ,
140+ do: nested_attrs ( v , " data" , t )
150141
151- defp build_attrs ( [ { :aria , v } | t ] , acc ) when is_list ( v ) do
152- build_attrs ( t , nested_attrs ( "aria" , v , acc ) )
153- end
142+ defp build_attrs ( [ { :aria , v } | t ] ) when is_list ( v ) ,
143+ do: nested_attrs ( v , " aria" , t )
154144
155- defp build_attrs ( [ { :class , v } | t ] , acc ) when is_list ( v ) do
156- build_attrs ( t , [ { "class" , class_value ( v ) } | acc ] )
157- end
145+ defp build_attrs ( [ { :class , v } | t ] ) when is_list ( v ) ,
146+ do: [ " class=\" " , class_value ( v ) , ?" | build_attrs ( t ) ]
158147
159- defp build_attrs ( [ { k , true } | t ] , acc ) do
160- build_attrs ( t , [ key_escape ( k ) | acc ] )
161- end
148+ defp build_attrs ( [ { k , true } | t ] ) ,
149+ do: [ ?\s , key_escape ( k ) | build_attrs ( t ) ]
162150
163- defp build_attrs ( [ { _ , false } | t ] , acc ) do
164- build_attrs ( t , acc )
165- end
151+ defp build_attrs ( [ { _ , false } | t ] ) ,
152+ do: build_attrs ( t )
166153
167- defp build_attrs ( [ { _ , nil } | t ] , acc ) do
168- build_attrs ( t , acc )
169- end
154+ defp build_attrs ( [ { _ , nil } | t ] ) ,
155+ do: build_attrs ( t )
170156
171- defp build_attrs ( [ { k , v } | t ] , acc ) do
172- build_attrs ( t , [ { key_escape ( k ) , v } | acc ] )
173- end
157+ defp build_attrs ( [ { k , v } | t ] ) ,
158+ do: [ ?\s , key_escape ( k ) , ?= , ?" , attr_escape ( v ) , ?" | build_attrs ( t ) ]
174159
175- defp tag_attrs ( [ ] ) , do: [ ]
160+ defp build_attrs ( [ ] ) , do: [ ]
176161
177- defp tag_attrs ( attrs ) do
178- for a <- attrs do
179- case a do
180- { k , v } -> [ ?\s , k , ?= , ?" , attr_escape ( v ) , ?" ]
181- k -> [ ?\s , k ]
182- end
183- end
184- end
162+ defp nested_attrs ( [ { k , v } | kv ] , attr , t ) when is_list ( v ) ,
163+ do: [ nested_attrs ( v , "#{ attr } -#{ key_escape ( k ) } " , [ ] ) | nested_attrs ( kv , attr , t ) ]
185164
186- defp nested_attrs ( attr , dict , acc ) do
187- Enum . reduce ( dict , acc , fn { k , v } , acc ->
188- attr_name = "#{ attr } -#{ key_escape ( k ) } "
165+ defp nested_attrs ( [ { k , v } | kv ] , attr , t ) ,
166+ do: [ attr , ?- , key_escape ( k ) , ?= , ?" , attr_escape ( v ) , ?" | nested_attrs ( kv , attr , t ) ]
189167
190- case is_list ( v ) do
191- true -> nested_attrs ( attr_name , v , acc )
192- false -> [ { attr_name , v } | acc ]
193- end
194- end )
195- end
168+ defp nested_attrs ( [ ] , _attr , t ) ,
169+ do: build_attrs ( t )
196170
197171 defp class_value ( value ) when is_list ( value ) do
198172 value
@@ -212,6 +186,16 @@ defmodule Phoenix.HTML.Tag do
212186 defp attr_escape ( other ) when is_binary ( other ) , do: Phoenix.HTML.Engine . encode_to_iodata! ( other )
213187 defp attr_escape ( other ) , do: Phoenix.HTML.Safe . to_iodata ( other )
214188
189+ defp sorted_attrs ( attrs ) when is_list ( attrs ) ,
190+ do: attrs |> normalize_attrs ( ) |> Enum . sort ( ) |> build_attrs ( )
191+
192+ defp sorted_attrs ( attrs ) ,
193+ do: attrs |> Enum . to_list ( ) |> sorted_attrs ( )
194+
195+ defp normalize_attrs ( [ { k , v } | tail ] ) , do: [ { k , v } | normalize_attrs ( tail ) ]
196+ defp normalize_attrs ( [ k | tail ] ) , do: [ { k , true } | normalize_attrs ( tail ) ]
197+ defp normalize_attrs ( [ ] ) , do: [ ]
198+
215199 @ doc ~S"""
216200 Generates a form tag.
217201
0 commit comments