Skip to content
This repository
tree: 91a59b5caa
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 157 lines (137 sloc) 7.522 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
# encoding: utf-8

module RailsAutolink
  require 'active_support/core_ext/object/blank'
  require 'active_support/core_ext/array/extract_options'
  require 'active_support/core_ext/hash/reverse_merge'
  require 'active_support/core_ext/hash/keys'

  module ::ActionView
    module Helpers # :nodoc:
      module TextHelper
        # Turns all URLs and e-mail addresses into clickable links. The <tt>:link</tt> option
        # will limit what should be linked. You can add HTML attributes to the links using
        # <tt>:html</tt>. Possible values for <tt>:link</tt> are <tt>:all</tt> (default),
        # <tt>:email_addresses</tt>, and <tt>:urls</tt>. If a block is given, each URL and
        # e-mail address is yielded and the result is used as the link text. By default the
        # text given is sanitized, you can override this behaviour setting the
        # <tt>:sanitize</tt> option to false, or you can add options to the sanitization of
        # the text using the <tt>:sanitize_options</tt> option hash.
        #
        # ==== Examples
        # auto_link("Go to http://www.rubyonrails.org and say hello to david@loudthinking.com")
        # # => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> and
        # # say hello to <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
        #
        # auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :urls)
        # # => "Visit <a href=\"http://www.loudthinking.com/\">http://www.loudthinking.com/</a>
        # # or e-mail david@loudthinking.com"
        #
        # auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :email_addresses)
        # # => "Visit http://www.loudthinking.com/ or e-mail <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
        #
        # post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com."
        # auto_link(post_body, :html => { :target => '_blank' }) do |text|
        # truncate(text, :length => 15)
        # end
        # # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.m...</a>.
        # Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
        #
        #
        # You can still use <tt>auto_link</tt> with the old API that accepts the
        # +link+ as its optional second parameter and the +html_options+ hash
        # as its optional third parameter:
        # post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com."
        # auto_link(post_body, :urls)
        # # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\">http://www.myblog.com</a>.
        # Please e-mail me at me@email.com."
        #
        # auto_link(post_body, :all, :target => "_blank")
        # # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.myblog.com</a>.
        # Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
        def auto_link(text, *args, &block)#link = :all, html = {}, &block)
          return ''.html_safe if text.blank?

          options = args.size == 2 ? {} : args.extract_options! # this is necessary because the old auto_link API has a Hash as its last parameter
          unless args.empty?
            options[:link] = args[0] || :all
            options[:html] = args[1] || {}
          end
          options.reverse_merge!(:link => :all, :html => {})
          sanitize = (options[:sanitize] != false)
          sanitize_options = options[:sanitize_options] || {}
          text = conditional_sanitize(text, sanitize, sanitize_options).to_str
          case options[:link].to_sym
            when :all then conditional_html_safe(auto_link_email_addresses(auto_link_urls(text, options[:html], options, &block), options[:html], &block), sanitize)
            when :email_addresses then conditional_html_safe(auto_link_email_addresses(text, options[:html], &block), sanitize)
            when :urls then conditional_html_safe(auto_link_urls(text, options[:html], options, &block), sanitize)
          end
        end

        private

          AUTO_LINK_RE = %r{
(?: ([0-9A-Za-z+.:-]+:)// | www\. )
[^\s<]+
}x

          # regexps for determining context, used high-volume
          AUTO_LINK_CRE = [/<[^>]+$/, /^[^>]*>/, /<a\b.*?>/i, /<\/a>/i]

          AUTO_EMAIL_RE = /[\w.!#\$%+-]+@[\w-]+(?:\.[\w-]+)+/

          BRACKETS = { ']' => '[', ')' => '(', '}' => '{' }

          WORD_PATTERN = RUBY_VERSION < '1.9' ? '\w' : '\p{Word}'

          # Turns all urls into clickable links. If a block is given, each url
          # is yielded and the result is used as the link text.
          def auto_link_urls(text, html_options = {}, options = {})
            link_attributes = html_options.stringify_keys
            text.gsub(AUTO_LINK_RE) do
              scheme, href = $1, $&
              punctuation = []

              if auto_linked?($`, $')
                # do not change string; URL is already linked
                href
              else
                # don't include trailing punctuation character as part of the URL
                while href.sub!(/[^#{WORD_PATTERN}\/-]$/, '')
                  punctuation.push $&
                  if opening = BRACKETS[punctuation.last] and href.scan(opening).size > href.scan(punctuation.last).size
                    href << punctuation.pop
                    break
                  end
                end

                link_text = block_given?? yield(href) : href
                href = 'http://' + href unless scheme

                unless options[:sanitize] == false
                  link_text = sanitize(link_text)
                  href = sanitize(href)
                end
                content_tag(:a, link_text, link_attributes.merge('href' => href), !!options[:sanitize]) + punctuation.reverse.join('')
              end
            end
          end

          # Turns all email addresses into clickable links. If a block is given,
          # each email is yielded and the result is used as the link text.
          def auto_link_email_addresses(text, html_options = {}, options = {})
            text.gsub(AUTO_EMAIL_RE) do
              text = $&

              if auto_linked?($`, $')
                text.html_safe
              else
                display_text = (block_given?) ? yield(text) : text

                unless options[:sanitize] == false
                  text = sanitize(text)
                  display_text = sanitize(display_text) unless text == display_text
                end
                mail_to text, display_text, html_options
              end
            end
          end

          # Detects already linked context or position in the middle of a tag
          def auto_linked?(left, right)
            (left =~ AUTO_LINK_CRE[0] and right =~ AUTO_LINK_CRE[1]) or
              (left.rindex(AUTO_LINK_CRE[2]) and $' !~ AUTO_LINK_CRE[3])
          end

          def conditional_sanitize(target, condition, sanitize_options = {})
            condition ? sanitize(target, sanitize_options) : target
          end

          def conditional_html_safe(target, condition)
            condition ? target.html_safe : target
          end
      end
    end
  end
end
Something went wrong with that request. Please try again.