defmodule HtmlEntities do
@moduledoc """
Decode and encode HTML entities in a string.
## Examples
Decoding:
iex> "Tom & Jerry" |> HtmlEntities.decode
"Tom & Jerry"
iex> "¡Ay, caramba!" |> HtmlEntities.decode
"¡Ay, caramba!"
iex> "ő ő" |> HtmlEntities.decode
"ő ő"
Encoding:
iex> "Tom & Jerry" |> HtmlEntities.encode
"Tom & Jerry"
iex> "<< KAPOW!! >>" |> HtmlEntities.encode
"<< KAPOW!! >>"
"""
@external_resource "lib/html_entities_list.txt"
@doc "Decode HTML entities in a string."
@spec decode(String.t()) :: String.t()
def decode(string) when is_binary(string) do
decode(string, "")
end
defp decode(<<"&", rest::binary>>, acc) do
case decode_entity(rest) do
{character, rest} -> decode(rest, <>)
:error -> decode(rest, <>)
end
end
defp decode(<>, acc) do
decode(rest, <>)
end
defp decode(<<>>, acc) do
acc
end
defp decode_entity(<<"#x", c, rest::binary>>) when c in ?0..?9 or c in ?a..?f or c in ?A..?F do
case Integer.parse(<>, 16) do
{number, ";" <> rest} -> {<>, rest}
_ -> :error
end
rescue
ArgumentError -> :error
end
defp decode_entity(<<"#", rest::binary>>) do
case Integer.parse(rest, 10) do
{number, ";" <> rest} -> {<>, rest}
_ -> :error
end
rescue
ArgumentError -> :error
end
codes = HtmlEntities.Util.load_entities(@external_resource)
for {name, _character, codepoint} <- codes do
defp decode_entity(<>) do
{<>, rest}
end
end
defp decode_entity(_), do: :error
@doc "Encode HTML entities in a string."
@spec encode(String.t()) :: String.t()
def encode(string) when is_binary(string) do
for <>, into: "" do
case x do
?' -> "'"
?" -> """
?& -> "&"
?< -> "<"
?> -> ">"
_ -> <>
end
end
end
end