Packages
phoenix
0.12.0
1.8.9
1.8.8
1.8.7
1.8.6
1.8.5
1.8.4
1.8.3
1.8.2
1.8.1
1.8.0
1.8.0-rc.4
1.8.0-rc.3
1.8.0-rc.2
1.8.0-rc.1
1.8.0-rc.0
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.7.0-rc.3
1.7.0-rc.2
1.7.0-rc.1
1.7.0-rc.0
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.6.0-rc.1
1.6.0-rc.0
1.5.15
1.5.14
1.5.13
1.5.12
1.5.11
1.5.10
1.5.9
1.5.8
1.5.7
1.5.6
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.5.0-rc.0
1.4.18
1.4.17
1.4.16
1.4.15
1.4.14
1.4.13
1.4.12
1.4.11
1.4.10
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.4.0-rc.3
1.4.0-rc.2
1.4.0-rc.1
1.4.0-rc.0
1.3.5
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.3.0-rc.3
1.3.0-rc.2
1.3.0-rc.1
1.3.0-rc.0
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.2.0-rc.1
1.2.0-rc.0
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.17.1
0.17.0
0.16.1
0.16.0
0.15.0
0.14.0
0.13.1
0.13.0
0.12.0
0.11.0
0.10.0
0.9.0
0.8.0
0.7.2
0.7.1
0.7.0
0.6.2
0.6.1
0.6.0
0.5.0
0.4.1
0.4.0
0.3.1
0.3.0
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.0
Productive. Reliable. Fast. A productive web framework that does not compromise speed or maintainability.
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/phoenix/html/tag.ex
defmodule Phoenix.HTML.Tag do
@moduledoc ~S"""
Helpers related to producing HTML tags within templates.
"""
import Phoenix.HTML
import Plug.CSRFProtection, only: [get_csrf_token: 0]
@tag_prefixes [:aria, :data]
@doc ~S"""
Creates an HTML tag with the given name and options.
iex> tag(:br)
{:safe, "<br>"}
iex> tag(:input, type: "text", name: "user_id")
{:safe, "<input name=\"user_id\" type=\"text\">"}
## Boolean values
In case an attribute contains a boolean value, its key
is repeated when it is true, as expected in HTML, or
the attribute is completely removed if it is false:
iex> tag(:audio, autoplay: true)
{:safe, "<audio autoplay=\"autoplay\">"}
iex> tag(:audio, autoplay: false)
{:safe, "<audio>"}
If you want the boolean attribute to be sent as is,
you can explicitly convert it to a string before.
"""
def tag(name), do: tag(name, [])
def tag(name, attrs) when is_list(attrs) do
{:safe, "<#{name}#{build_attrs(name, attrs)}>"}
end
@doc ~S"""
Creates an HTML tag with given name, content, and attributes.
iex> content_tag(:p, "Hello")
{:safe, "<p>Hello</p>"}
iex> content_tag(:p, "<Hello>", class: "test")
{:safe, "<p class=\"test\"><Hello></p>"}
iex> content_tag :p, class: "test" do
...> "Hello"
...> end
{:safe, "<p class=\"test\">Hello</p>"}
"""
def content_tag(name, content) when is_atom(name) do
content_tag(name, content, [])
end
def content_tag(name, attrs, [do: block]) when is_atom(name) and is_list(attrs) do
content_tag(name, block, attrs)
end
def content_tag(name, content, attrs) when is_atom(name) and is_list(attrs) do
tag(name, attrs)
|> safe_concat(content)
|> safe_concat({:safe, "</#{name}>"})
end
defp tag_attrs([]), do: ""
defp tag_attrs(attrs) do
for {k, v} <- attrs, into: "" do
" " <> k <> "=" <> "\"" <> attr_escape(v) <> "\""
end
end
defp attr_escape({:safe, data}),
do: data
defp attr_escape(nil),
do: ""
defp attr_escape(other) when is_binary(other),
do: Phoenix.HTML.Safe.BitString.to_iodata(other)
defp attr_escape(other),
do: Phoenix.HTML.Safe.to_iodata(other)
defp nested_attrs(attr, dict, acc) do
Enum.reduce dict, acc, fn {k,v}, acc ->
attr_name = "#{attr}-#{dasherize(k)}"
case is_list(v) do
true -> nested_attrs(attr_name, v, acc)
false -> [{attr_name, v}|acc]
end
end
end
defp build_attrs(_tag, []), do: ""
defp build_attrs(tag, attrs), do: build_attrs(tag, attrs, [])
defp build_attrs(_tag, [], acc),
do: acc |> Enum.sort |> tag_attrs
defp build_attrs(tag, [{k, v}|t], acc) when k in @tag_prefixes and is_list(v) do
build_attrs(tag, t, nested_attrs(dasherize(k), v, acc))
end
defp build_attrs(tag, [{k, true}|t], acc) do
k = dasherize(k)
build_attrs(tag, t, [{k, k}|acc])
end
defp build_attrs(tag, [{_, false}|t], acc) do
build_attrs(tag, t, acc)
end
defp build_attrs(tag, [{_, nil}|t], acc) do
build_attrs(tag, t, acc)
end
defp build_attrs(tag, [{k, v}|t], acc) do
build_attrs(tag, t, [{dasherize(k), v}|acc])
end
defp dasherize(value) when is_atom(value), do: dasherize(Atom.to_string(value))
defp dasherize(value) when is_binary(value), do: String.replace(value, "_", "-")
@doc ~S"""
Generates a form tag.
This function generates the `<form>` tag without its
closing part. Check `form_tag/3` for generating an
enclosing tag.
## Examples
form_tag("/hello")
<form action="/hello" method="post">
form_tag("/hello", method: :get)
<form action="/hello" method="get">
## Options
* `:method` - the HTTP method. If the method is not "get" nor "post",
an input tag with name `_method` is generated along-side the form tag.
Defaults to "post".
* `:multipart` - when true, sets enctype to "multipart/form-data".
Required when uploading files
* `:csrf_token` - for "post" requests, the form tag will automatically
include an input tag with name `_csrf_token`. When set to false, this
is disabled
* `:enforce_utf8` - when false, does not enforce utf8. Read below
for more information
All other options are passed to the underlying HTML tag.
## Enforce UTF-8
Alhought forms provide the `accept-charset` attribute, which we set
to UTF-8, Internet Explorer 5 up to 8 may ignore the value of this
attribute if the user chooses their browser to do so. This ends up
triggering the browser to send data in a format that is not
understandable by the server.
For this reason, Phoenix automatically includes a "_utf8=✓" parameter
in your forms, to force those browsers to send the data in the proper
encoding. This technique has been seen in the Rails web framework and
reproduced here.
"""
def form_tag(action, opts \\ [])
def form_tag(action, do: block) do
form_tag(action, [], do: block)
end
def form_tag(action, opts) when is_list(opts) do
{:safe, method} = html_escape(Keyword.get(opts, :method, "post"))
{opts, extra} =
case method do
"get" -> {opts, ""}
"post" -> csrf_token_tag(Keyword.put(opts, :method, "post"), "")
_ -> csrf_token_tag(Keyword.put(opts, :method, "post"),
~s'<input name="_method" type="hidden" value="#{method}">')
end
{opts, extra} =
case Keyword.pop(opts, :enforce_utf8, true) do
{false, opts} -> {opts, extra}
{true, opts} -> {Keyword.put_new(opts, :accept_charset, "UTF-8"),
extra <> ~s'<input name="_utf8" type="hidden" value="✓">'}
end
opts =
case Keyword.pop(opts, :multipart, false) do
{false, opts} -> opts
{true, opts} -> Keyword.put(opts, :enctype, "multipart/form-data")
end
safe_concat tag(:form, [action: action] ++ opts), safe(extra)
end
@doc """
Generates a form tag with the given contents.
## Examples
form_tag("/hello", method: "get") do
"Hello"
end
<form action="/hello" method="post">...Hello...</form>
"""
def form_tag(action, options, do: block) do
safe_concat [form_tag(action, options), block, safe("</form>")]
end
defp csrf_token_tag(opts, extra) do
case Keyword.pop(opts, :csrf_token, true) do
{true, opts} ->
{opts, extra <> ~s'<input name="_csrf_token" type="hidden" value="#{get_csrf_token}">'}
{false, opts} ->
{opts, extra}
end
end
end