1module AutoLinkHelper |
|
2 class AutoLinker |
|
3 BLANK = "".html_safe |
|
4 AUTO_LINK_RE = %r{(?:((?:ftp|http|https|mailto):)//|www\.)[^\s<\u00A0"]+}ix |
|
5 AUTO_LINK_CRE = [/<[^>]+$/, /^[^>]*>/, /<a\b.*?>/i, /<\/a>/i] |
|
6 AUTO_EMAIL_LOCAL_RE = /[\w.!#\$%&'*\/=?^`{|}~+-]/ |
|
7 AUTO_EMAIL_RE = /(?<!#{AUTO_EMAIL_LOCAL_RE})[\w.!#\$%+-]\.?#{AUTO_EMAIL_LOCAL_RE}*@[\w-]+(?:\.[\w-]+)+/ |
|
8 BRACKETS = { "]" => "[", ")" => "(", "}" => "{" } |
|
10 attr_reader :template, :text, :options, :block |
|
11 delegate :sanitize, :mail_to, :content_tag, to: :template |
|
13 def self.generate(template, text, options = {}, &block) |
|
14 new(template, text, options, &block).generate
|
|
15 end |
|
|
17 def initialize(template, text, options, &block) |
18 @template = template |
|
19 @text = text |
|
20 @block = block |
|
21 @options = options |
|
22 end |
|
|
24 def generate |
25 return BLANK if text.blank? |
|
27 result = \
|
|
28 case scope |
|
29 when :urls |
|
30 auto_link_urls(sanitized_text)
|
|
31 when :email_addresses |
|
32 auto_link_email_addresses(sanitized_text)
|
|
33 else |
|
34 auto_link_all(sanitized_text)
|
|
35 end |
|
37 sanitize? ? result.html_safe : result
|
|
38 end |
|
40 private
|
|
|
42 def scope |
43 options[:link] || :all |
|
44 end |
|
|
46 def sanitized_text |
47 (sanitize? ? sanitize(text, sanitize_options) : text).to_str
|
|
48 end |
|
|
50 def sanitize? |
51 return @sanitize if defined?(@sanitize) |
|
52 @sanitize = options[:sanitize] != false |
|
53 end |
|
|
55 def sanitize_options |
56 @sanitize_options ||= options[:sanitize_options] || {} |
|
57 end |
|
|
59 def html_options |
60 @html_options ||= options[:html] || {} |
|
61 end |
|
|
63 def auto_linked?(left, right) |
64 (left =~ AUTO_LINK_CRE[0] && right =~ AUTO_LINK_CRE[1]) || |
|
65 (left.rindex(AUTO_LINK_CRE[2]) && $' !~ AUTO_LINK_CRE[3]) |
|
66 end |
|
|
68 def auto_link_all(target) |
69 auto_link_email_addresses(auto_link_urls(target))
|
|
70 end |
|
|
72 def auto_link_urls(text) |
|
73 text.gsub(AUTO_LINK_RE) do |
74 scheme, href = $1, $& |
|
75 punctuation = []
|
|
77 if auto_linked?($`, $') |
|
78 # do not change string; URL is already linked |
|
79 href
|
|
80 else |
|
81 # don't include trailing punctuation character as part of the URL |
|
82 while href.sub!(/[^\p{Word}\/-=&]$/, "") |
|
83 punctuation.push $& |
|
85 if opening = BRACKETS[punctuation.last] and href.scan(opening).size > href.scan(punctuation.last).size |
|
|
86 href << punctuation.pop
|
87 break |
|
88 end |
|
89 end |
|
91 link_text = block ? block.call(href) : href
|
|
92 href = "http://" + href unless scheme |
|
94 if sanitize? |
|
95 link_text = sanitize(link_text)
|
|
96 href = sanitize(href)
|
|
97 end |
|
99 content_tag(:a, link_text, html_options.merge(href: href), sanitize?) + punctuation.reverse.join("") |
|
100 end |
|
101 end |
|
102 end |
|
|
104 def auto_link_email_addresses(text) |
|
105 text.gsub(AUTO_EMAIL_RE) do |
106 text = $& |
|
108 if auto_linked?($`, $') |
|
109 text.html_safe
|
|
110 else |
|
111 display_text = block ? block.call(text) : text
|
|
113 if sanitize? |
|
114 text = sanitize(text)
|
|
115 display_text = sanitize(display_text) unless text == display_text |
|
116 end |
|
118 mail_to text, display_text, html_options
|
|
119 end |
|
120 end |
|
121 end |
|
122 end |
|
|
124 def auto_link(text, options = {}, &block) |
125 AutoLinker.generate(self, text, options, &block) |
|
126 end |
|
127end |