Current section
Files
Jump to
Current section
Files
lib/core/linker.ex
defmodule Gulib.LinkerUtil do
alias Gulib.HTMLParse
@auto_link_schemas ~w(mailto http https thunder ftp sftp svn)
def auto_link_schemas, do: @auto_link_schemas
@doc """
返回html中链接的个数
"""
def links_amount(html) do
html |> HTMLParse.parse |> HTMLParse.find("a") |> length
end
def append_rest(rest, extend_rest) do
rest |> List.insert_at(-1, extend_rest)
end
@doc """
自动生成链接
## Params
```
# opts
* html: 生成链接相关参数
* target # _blank or nil (nil)
* sanitize:是否过滤标签 true/false 默认为false
* sanitize_options: 过滤标签的参数
* jump_to # 用于跳转的基础url
* jump_fn # 调用的时候可以用此回调
* raw_html # true/false # 默认为true
* html_escape # true/false # 默认为false
* normalize_rest_fn # 格式化链接内容的方法
* normalize_href_fn # 格式化链接链接的方法
* normalize_linker_fn # 格式化链接, 可以灵活的转换链接!
```
"""
def auto_link(html_or_tree, opts \\ []) do
html_opts = opts |> Gulib.get(:html, %{})
html_escape = opts |> Gulib.get(:html_escape, false)
opts =
opts
|> Keyword.take([
:raw_html, :jump_to, :jump_fn,
:normalize_rest_fn, :normalize_href_fn, :html_safe,
:normalize_linker_fn
])
|> Keyword.merge(
html_escape: html_escape,
html: html_opts
)
rebuild_html_tree =
html_or_tree
|> HTMLParse.parse
|> HTMLParse.transform(fn(html_node, extends) ->
impl_transform(html_node, extends, opts)
end)
raw_html = rebuild_html_tree |> HTMLParse.raw_html
cond do
Keyword.get(opts, :html_safe, false) ->
{:safe, raw_html}
Keyword.get(opts, :raw_html, true) ->
raw_html
true ->
raw_html |> HTMLParse.parse
end
end
defp impl_transform_linker({"a", attrs, rest}, extends, opts) do
if is_function(normalize_linker_fn = Gulib.get(opts, :normalize_linker_fn, nil)) do
normalize_linker_fn.(
[
attrs: attrs,
rest: rest,
extends: extends,
opts: opts
]
)
else
{"a", attrs, rest}
end
end
# 对A标签的处理
defp impl_transform({"a", attrs, rest}, extends, opts) do
{"a", impl_normalize_linker_attrs(attrs, opts), impl_normalize_linker_rest(rest, opts)}
|> impl_transform_linker(extends, opts)
end
defp impl_transform(plain_text, extends, opts) when is_binary(plain_text) do
impl_transform_plain_text(plain_text, extends, opts)
end
defp impl_transform(html_tree, _extends, _opts), do: html_tree
defp impl_transform_plain_text(plain_text, extends, opts) do
%{
ancestors: ancestors
} = extends
plain_text =
if Gulib.get(opts, :html_escape, false) do
Gulib.html_escape(plain_text)
else
plain_text
end
if Enum.member?(ancestors, "a") do
plain_text
else
Gulib.Const.scan_url_regex
|> Regex.replace(plain_text, fn(link_url, _) ->
{
"a",
impl_normalize_linker_attrs([{"href", impl_normalize_href(link_url, opts)}], opts),
impl_normalize_linker_rest([link_url], opts)
}
|> impl_transform_linker(extends, opts)
|> HTMLParse.raw_html
end)
end
end
# 对标签属性的处理
# opts:
# * target
defp impl_normalize_linker_attrs(attrs, opts) do
parsed_attrs = HTMLParse.parse_attrs(attrs)
html_opts = opts |> Keyword.get(:html, %{})
target = html_opts |> Map.get(:target, nil)
parsed_attrs =
if Enum.member?(["_blank", "_parent", "_self"], target) do
parsed_attrs |> Keyword.put(:target, target)
else
parsed_attrs |> Keyword.drop([:target])
end
parsed_attrs |> HTMLParse.flatten_attrs
end
# 实现格式化rest
defp impl_normalize_linker_rest(rest, opts) do
normalize_rest_fn = opts |> Keyword.get(:normalize_rest_fn, nil)
if is_function(normalize_rest_fn) do
normalize_rest_fn.(rest)
else
rest
end
end
def normalize_url(url) do
url =
cond do
Gulib.blank?(url) -> ""
String.starts_with?(url, ["/"]) -> url
true ->
header_str = url |> String.trim |> String.split([":"]) |> List.first |> String.downcase
cond do
Enum.member?(auto_link_schemas(), header_str) -> url
Regex.match?(Gulib.Const.match_url_regex, url) -> "http://" <> url
true -> "/#{url}"
end
end
url |> Gulib.html_unescape
end
# 实现格式化href
defp impl_normalize_href(url, opts) do
normalize_href_fn = opts |> Keyword.get(:normalize_href_fn, nil)
if is_function(normalize_href_fn) do
url
|> normalize_href_fn.()
|> normalize_url
else
url = normalize_url(url)
jump_to_base_uri = Keyword.get(opts, :jump_to, nil)
jump_fn = Keyword.get(opts, :jump_fn, nil)
cond do
is_function(jump_fn) ->
jump_fn.(url)
Gulib.present?(jump_to_base_uri) ->
jump_to_base_uri <> URI.encode_www_form(url)
true -> url
end
end
end
end