defmodule EEx.HTML do @moduledoc """ Conveniences for generating HTML. """ alias __MODULE__.Safe @doc """ Escape the HTML content derived from the given term. The content is returned wrapped in an `EEx.HTML.Safe` struct so it is not reescaped by templates etc. """ # Short circuit escaping the content, if already wrapped as safe. def escape(content = %Safe{}) do content end def escape(term) do data = Safe.to_iodata(term) raw(data) end def raw(content = %Safe{}) do content end def raw(iodata) do %Safe{data: iodata} end @doc ~S""" Escapes the given HTML to string. iex> EEx.HTML.escape_to_binary("foo") "foo" iex> EEx.HTML.escape_to_binary("") "<foo>" iex> EEx.HTML.escape_to_binary("quotes: \" & \'") "quotes: " & '" """ @spec escape_to_binary(String.t()) :: String.t() def escape_to_binary(data) when is_binary(data) do IO.iodata_to_binary(to_iodata(data, 0, data, [])) end @doc ~S""" Escapes the given HTML to iodata. iex> EEx.HTML.escape_to_iodata("foo") "foo" iex> EEx.HTML.escape_to_iodata("") [[[] | "<"], "foo" | ">"] iex> EEx.HTML.escape_to_iodata("quotes: \" & \'") [[[[], "quotes: " | """], " " | "&"], " " | "'"] """ @spec escape_to_iodata(String.t()) :: iodata def escape_to_iodata(data) when is_binary(data) do to_iodata(data, 0, data, []) end escapes = [ {?<, "<"}, {?>, ">"}, {?&, "&"}, {?", """}, {?', "'"} ] for {match, insert} <- escapes do defp to_iodata(<>, skip, original, acc) do to_iodata(rest, skip + 1, original, [acc | unquote(insert)]) end end defp to_iodata(<<_char, rest::bits>>, skip, original, acc) do to_iodata(rest, skip, original, acc, 1) end defp to_iodata(<<>>, _skip, _original, acc) do acc end for {match, insert} <- escapes do defp to_iodata(<>, skip, original, acc, len) do part = binary_part(original, skip, len) to_iodata(rest, skip + len + 1, original, [acc, part | unquote(insert)]) end end defp to_iodata(<<_char, rest::bits>>, skip, original, acc, len) do to_iodata(rest, skip, original, acc, len + 1) end defp to_iodata(<<>>, 0, original, _acc, _len) do original end defp to_iodata(<<>>, skip, original, acc, len) do [acc | binary_part(original, skip, len)] end end