You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

176 lines
4.4 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. defmodule AutoLinker.Builder do
  2. @moduledoc """
  3. Module for building the auto generated link.
  4. """
  5. @doc """
  6. Create a link.
  7. """
  8. def create_link(url, opts) do
  9. []
  10. |> build_attrs(url, opts, :rel)
  11. |> build_attrs(url, opts, :target)
  12. |> build_attrs(url, opts, :class)
  13. |> build_attrs(url, opts, :scheme)
  14. |> format_url(url, opts)
  15. end
  16. def create_markdown_links(text, opts) do
  17. []
  18. |> build_attrs(text, opts, :rel)
  19. |> build_attrs(text, opts, :target)
  20. |> build_attrs(text, opts, :class)
  21. |> format_markdown(text, opts)
  22. end
  23. defp build_attrs(attrs, _, opts, :rel) do
  24. if rel = Map.get(opts, :rel, "noopener noreferrer"), do: [{:rel, rel} | attrs], else: attrs
  25. end
  26. defp build_attrs(attrs, _, opts, :target) do
  27. if Map.get(opts, :new_window, true), do: [{:target, :_blank} | attrs], else: attrs
  28. end
  29. defp build_attrs(attrs, _, opts, :class) do
  30. if cls = Map.get(opts, :class, "auto-linker"), do: [{:class, cls} | attrs], else: attrs
  31. end
  32. defp build_attrs(attrs, url, _opts, :scheme) do
  33. if String.starts_with?(url, ["http://", "https://"]),
  34. do: [{:href, url} | attrs],
  35. else: [{:href, "http://" <> url} | attrs]
  36. end
  37. defp format_url(attrs, url, opts) do
  38. url =
  39. url
  40. |> strip_prefix(Map.get(opts, :strip_prefix, true))
  41. |> truncate(Map.get(opts, :truncate, false))
  42. attrs = format_attrs(attrs)
  43. "<a #{attrs}>" <> url <> "</a>"
  44. end
  45. defp format_attrs(attrs) do
  46. attrs
  47. |> Enum.map(fn {key, value} -> ~s(#{key}="#{value}") end)
  48. |> Enum.join(" ")
  49. end
  50. defp format_markdown(attrs, text, _opts) do
  51. attrs =
  52. case format_attrs(attrs) do
  53. "" -> ""
  54. attrs -> " " <> attrs
  55. end
  56. Regex.replace(~r/\[(.+?)\]\((.+?)\)/, text, "<a href='\\2'#{attrs}>\\1</a>")
  57. end
  58. defp truncate(url, false), do: url
  59. defp truncate(url, len) when len < 3, do: url
  60. defp truncate(url, len) do
  61. if String.length(url) > len, do: String.slice(url, 0, len - 2) <> "..", else: url
  62. end
  63. defp strip_prefix(url, true) do
  64. url
  65. |> String.replace(~r/^https?:\/\//, "")
  66. |> String.replace(~r/^www\./, "")
  67. end
  68. defp strip_prefix(url, _), do: url
  69. def create_phone_link([], buffer, _) do
  70. buffer
  71. end
  72. def create_phone_link([h | t], buffer, opts) do
  73. create_phone_link(t, format_phone_link(h, buffer, opts), opts)
  74. end
  75. def format_phone_link([h | _], buffer, opts) do
  76. val =
  77. h
  78. |> String.replace(~r/[\.\+\- x\(\)]+/, "")
  79. |> format_phone_link(h, opts)
  80. # val = ~s'<a href="#" class="phone-number" data-phone="#{number}">#{h}</a>'
  81. String.replace(buffer, h, val)
  82. end
  83. def format_phone_link(number, original, opts) do
  84. tag = opts[:tag] || "a"
  85. class = opts[:class] || "phone-number"
  86. data_phone = opts[:data_phone] || "data-phone"
  87. attrs = format_attributes(opts[:attributes] || [])
  88. href = opts[:href] || "#"
  89. ~s'<#{tag} href="#{href}" class="#{class}" #{data_phone}="#{number}"#{attrs}>#{original}</#{
  90. tag
  91. }>'
  92. end
  93. def create_mention_link("@" <> name, _buffer, opts) do
  94. mention_prefix = opts[:mention_prefix]
  95. url = mention_prefix <> name
  96. [href: url]
  97. |> build_attrs(url, opts, :rel)
  98. |> build_attrs(url, opts, :target)
  99. |> build_attrs(url, opts, :class)
  100. |> format_mention(name, opts)
  101. end
  102. def create_hashtag_link("#" <> tag, _buffer, opts) do
  103. hashtag_prefix = opts[:hashtag_prefix]
  104. url = hashtag_prefix <> tag
  105. [href: url]
  106. |> build_attrs(url, opts, :rel)
  107. |> build_attrs(url, opts, :target)
  108. |> build_attrs(url, opts, :class)
  109. |> format_hashtag(tag, opts)
  110. end
  111. def create_email_link(email, opts) do
  112. []
  113. |> build_attrs(email, opts, :class)
  114. |> format_email(email, opts)
  115. end
  116. def create_extra_link(uri, opts) do
  117. []
  118. |> build_attrs(uri, opts, :class)
  119. |> format_extra(uri, opts)
  120. end
  121. def format_mention(attrs, name, _opts) do
  122. attrs = format_attrs(attrs)
  123. "<a #{attrs}>@" <> name <> "</a>"
  124. end
  125. def format_hashtag(attrs, tag, _opts) do
  126. attrs = format_attrs(attrs)
  127. "<a #{attrs}>#" <> tag <> "</a>"
  128. end
  129. def format_email(attrs, email, _opts) do
  130. attrs = format_attrs(attrs)
  131. ~s(<a href="mailto:#{email}" #{attrs}>#{email}</a>)
  132. end
  133. def format_extra(attrs, uri, _opts) do
  134. attrs = format_attributes(attrs)
  135. ~s(<a href="#{uri}"#{attrs}>#{uri}</a>)
  136. end
  137. defp format_attributes(attrs) do
  138. Enum.reduce(attrs, "", fn {name, value}, acc ->
  139. acc <> ~s' #{name}="#{value}"'
  140. end)
  141. end
  142. end