X-Git-Url: https://git.njae.me.uk/?a=blobdiff_plain;f=vendor%2Frails%2Factionpack%2Flib%2Faction_view%2Fhelpers%2Ftext_helper.rb;fp=vendor%2Frails%2Factionpack%2Flib%2Faction_view%2Fhelpers%2Ftext_helper.rb;h=36f757565208a22ccb24fff4431bd5b17d145020;hb=d115f2e23823271635bad69229a42cd8ac68debe;hp=0000000000000000000000000000000000000000;hpb=37cb670bf3ddde90b214e591f100ed4446469484;p=depot.git diff --git a/vendor/rails/actionpack/lib/action_view/helpers/text_helper.rb b/vendor/rails/actionpack/lib/action_view/helpers/text_helper.rb new file mode 100644 index 0000000..36f7575 --- /dev/null +++ b/vendor/rails/actionpack/lib/action_view/helpers/text_helper.rb @@ -0,0 +1,601 @@ +require 'action_view/helpers/tag_helper' + +begin + require 'html/document' +rescue LoadError + html_scanner_path = "#{File.dirname(__FILE__)}/../../action_controller/vendor/html-scanner" + if File.directory?(html_scanner_path) + $:.unshift html_scanner_path + require 'html/document' + end +end + +module ActionView + module Helpers #:nodoc: + # The TextHelper module provides a set of methods for filtering, formatting + # and transforming strings, which can reduce the amount of inline Ruby code in + # your views. These helper methods extend ActionView making them callable + # within your template files. + module TextHelper + # The preferred method of outputting text in your views is to use the + # <%= "text" %> eRuby syntax. The regular _puts_ and _print_ methods + # do not operate as expected in an eRuby code block. If you absolutely must + # output text within a non-output code block (i.e., <% %>), you can use the concat method. + # + # ==== Examples + # <% + # concat "hello" + # # is the equivalent of <%= "hello" %> + # + # if (logged_in == true): + # concat "Logged in!" + # else + # concat link_to('login', :action => login) + # end + # # will either display "Logged in!" or a login link + # %> + def concat(string, unused_binding = nil) + if unused_binding + ActiveSupport::Deprecation.warn("The binding argument of #concat is no longer needed. Please remove it from your views and helpers.", caller) + end + + output_buffer << string + end + + # Truncates a given +text+ after a given :length if +text+ is longer than :length + # (defaults to 30). The last characters will be replaced with the :omission (defaults to "..."). + # + # ==== Examples + # + # truncate("Once upon a time in a world far far away") + # # => Once upon a time in a world f... + # + # truncate("Once upon a time in a world far far away", :length => 14) + # # => Once upon a... + # + # truncate("And they found that many people were sleeping better.", :length => 25, "(clipped)") + # # => And they found that many (clipped) + # + # truncate("And they found that many people were sleeping better.", :omission => "... (continued)", :length => 15) + # # => And they found... (continued) + # + # You can still use truncate with the old API that accepts the + # +length+ as its optional second and the +ellipsis+ as its + # optional third parameter: + # truncate("Once upon a time in a world far far away", 14) + # # => Once upon a time in a world f... + # + # truncate("And they found that many people were sleeping better.", 15, "... (continued)") + # # => And they found... (continued) + def truncate(text, *args) + options = args.extract_options! + unless args.empty? + ActiveSupport::Deprecation.warn('truncate takes an option hash instead of separate ' + + 'length and omission arguments', caller) + + options[:length] = args[0] || 30 + options[:omission] = args[1] || "..." + end + options.reverse_merge!(:length => 30, :omission => "...") + + if text + l = options[:length] - options[:omission].mb_chars.length + chars = text.mb_chars + (chars.length > options[:length] ? chars[0...l] + options[:omission] : text).to_s + end + end + + # Highlights one or more +phrases+ everywhere in +text+ by inserting it into + # a :highlighter string. The highlighter can be specialized by passing :highlighter + # as a single-quoted string with \1 where the phrase is to be inserted (defaults to + # '\1') + # + # ==== Examples + # highlight('You searched for: rails', 'rails') + # # => You searched for: rails + # + # highlight('You searched for: ruby, rails, dhh', 'actionpack') + # # => You searched for: ruby, rails, dhh + # + # highlight('You searched for: rails', ['for', 'rails'], :highlighter => '\1') + # # => You searched for: rails + # + # highlight('You searched for: rails', 'rails', :highlighter => '\1') + # # => You searched for: rails + # + # You can still use highlight with the old API that accepts the + # +highlighter+ as its optional third parameter: + # highlight('You searched for: rails', 'rails', '\1') # => You searched for: rails + def highlight(text, phrases, *args) + options = args.extract_options! + unless args.empty? + options[:highlighter] = args[0] || '\1' + end + options.reverse_merge!(:highlighter => '\1') + + if text.blank? || phrases.blank? + text + else + match = Array(phrases).map { |p| Regexp.escape(p) }.join('|') + text.gsub(/(#{match})/i, options[:highlighter]) + end + end + + # Extracts an excerpt from +text+ that matches the first instance of +phrase+. + # The :radius option expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters + # defined in :radius (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+, + # then the :omission option (which defaults to "...") will be prepended/appended accordingly. The resulting string + # will be stripped in any case. If the +phrase+ isn't found, nil is returned. + # + # ==== Examples + # excerpt('This is an example', 'an', :radius => 5) + # # => ...s is an exam... + # + # excerpt('This is an example', 'is', :radius => 5) + # # => This is a... + # + # excerpt('This is an example', 'is') + # # => This is an example + # + # excerpt('This next thing is an example', 'ex', :radius => 2) + # # => ...next... + # + # excerpt('This is also an example', 'an', :radius => 8, :omission => ' ') + # # => is also an example + # + # You can still use excerpt with the old API that accepts the + # +radius+ as its optional third and the +ellipsis+ as its + # optional forth parameter: + # excerpt('This is an example', 'an', 5) # => ...s is an exam... + # excerpt('This is also an example', 'an', 8, ' ') # => is also an example + def excerpt(text, phrase, *args) + options = args.extract_options! + unless args.empty? + options[:radius] = args[0] || 100 + options[:omission] = args[1] || "..." + end + options.reverse_merge!(:radius => 100, :omission => "...") + + if text && phrase + phrase = Regexp.escape(phrase) + + if found_pos = text.mb_chars =~ /(#{phrase})/i + start_pos = [ found_pos - options[:radius], 0 ].max + end_pos = [ [ found_pos + phrase.mb_chars.length + options[:radius] - 1, 0].max, text.mb_chars.length ].min + + prefix = start_pos > 0 ? options[:omission] : "" + postfix = end_pos < text.mb_chars.length - 1 ? options[:omission] : "" + + prefix + text.mb_chars[start_pos..end_pos].strip + postfix + else + nil + end + end + end + + # Attempts to pluralize the +singular+ word unless +count+ is 1. If + # +plural+ is supplied, it will use that when count is > 1, otherwise + # it will use the Inflector to determine the plural form + # + # ==== Examples + # pluralize(1, 'person') + # # => 1 person + # + # pluralize(2, 'person') + # # => 2 people + # + # pluralize(3, 'person', 'users') + # # => 3 users + # + # pluralize(0, 'person') + # # => 0 people + def pluralize(count, singular, plural = nil) + "#{count || 0} " + ((count == 1 || count == '1') ? singular : (plural || singular.pluralize)) + end + + # Wraps the +text+ into lines no longer than +line_width+ width. This method + # breaks on the first whitespace character that does not exceed +line_width+ + # (which is 80 by default). + # + # ==== Examples + # + # word_wrap('Once upon a time') + # # => Once upon a time + # + # word_wrap('Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding a successor to the throne turned out to be more trouble than anyone could have imagined...') + # # => Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding\n a successor to the throne turned out to be more trouble than anyone could have\n imagined... + # + # word_wrap('Once upon a time', :line_width => 8) + # # => Once upon\na time + # + # word_wrap('Once upon a time', :line_width => 1) + # # => Once\nupon\na\ntime + # + # You can still use word_wrap with the old API that accepts the + # +line_width+ as its optional second parameter: + # word_wrap('Once upon a time', 8) # => Once upon\na time + def word_wrap(text, *args) + options = args.extract_options! + unless args.blank? + options[:line_width] = args[0] || 80 + end + options.reverse_merge!(:line_width => 80) + + text.split("\n").collect do |line| + line.length > options[:line_width] ? line.gsub(/(.{1,#{options[:line_width]}})(\s+|$)/, "\\1\n").strip : line + end * "\n" + end + + begin + require_library_or_gem "redcloth" unless Object.const_defined?(:RedCloth) + + # Returns the text with all the Textile[http://www.textism.com/tools/textile] codes turned into HTML tags. + # + # You can learn more about Textile's syntax at its website[http://www.textism.com/tools/textile]. + # This method is only available if RedCloth[http://whytheluckystiff.net/ruby/redcloth/] + # is available. + # + # ==== Examples + # textilize("*This is Textile!* Rejoice!") + # # => "

This is Textile! Rejoice!

" + # + # textilize("I _love_ ROR(Ruby on Rails)!") + # # => "

I love ROR!

" + # + # textilize("h2. Textile makes markup -easy- simple!") + # # => "

Textile makes markup easy simple!

" + # + # textilize("Visit the Rails website "here":http://www.rubyonrails.org/.) + # # => "

Visit the Rails website here.

" + def textilize(text) + if text.blank? + "" + else + textilized = RedCloth.new(text, [ :hard_breaks ]) + textilized.hard_breaks = true if textilized.respond_to?(:hard_breaks=) + textilized.to_html + end + end + + # Returns the text with all the Textile codes turned into HTML tags, + # but without the bounding

tag that RedCloth adds. + # + # You can learn more about Textile's syntax at its website[http://www.textism.com/tools/textile]. + # This method is only available if RedCloth[http://whytheluckystiff.net/ruby/redcloth/] + # is available. + # + # ==== Examples + # textilize_without_paragraph("*This is Textile!* Rejoice!") + # # => "This is Textile! Rejoice!" + # + # textilize_without_paragraph("I _love_ ROR(Ruby on Rails)!") + # # => "I love ROR!" + # + # textilize_without_paragraph("h2. Textile makes markup -easy- simple!") + # # => "

Textile makes markup easy simple!

" + # + # textilize_without_paragraph("Visit the Rails website "here":http://www.rubyonrails.org/.) + # # => "Visit the Rails website here." + def textilize_without_paragraph(text) + textiled = textilize(text) + if textiled[0..2] == "

" then textiled = textiled[3..-1] end + if textiled[-4..-1] == "

" then textiled = textiled[0..-5] end + return textiled + end + rescue LoadError + # We can't really help what's not there + end + + begin + require_library_or_gem "bluecloth" unless Object.const_defined?(:BlueCloth) + + # Returns the text with all the Markdown codes turned into HTML tags. + # This method is only available if BlueCloth[http://www.deveiate.org/projects/BlueCloth] + # is available. + # + # ==== Examples + # markdown("We are using __Markdown__ now!") + # # => "

We are using Markdown now!

" + # + # markdown("We like to _write_ `code`, not just _read_ it!") + # # => "

We like to write code, not just read it!

" + # + # markdown("The [Markdown website](http://daringfireball.net/projects/markdown/) has more information.") + # # => "

The Markdown website + # # has more information.

" + # + # markdown('![The ROR logo](http://rubyonrails.com/images/rails.png "Ruby on Rails")') + # # => '

The ROR logo

' + def markdown(text) + text.blank? ? "" : BlueCloth.new(text).to_html + end + rescue LoadError + # We can't really help what's not there + end + + # Returns +text+ transformed into HTML using simple formatting rules. + # Two or more consecutive newlines(\n\n) are considered as a + # paragraph and wrapped in

tags. One newline (\n) is + # considered as a linebreak and a
tag is appended. This + # method does not remove the newlines from the +text+. + # + # You can pass any HTML attributes into html_options. These + # will be added to all created paragraphs. + # ==== Examples + # my_text = "Here is some basic text...\n...with a line break." + # + # simple_format(my_text) + # # => "

Here is some basic text...\n
...with a line break.

" + # + # more_text = "We want to put a paragraph...\n\n...right there." + # + # simple_format(more_text) + # # => "

We want to put a paragraph...

\n\n

...right there.

" + # + # simple_format("Look ma! A class!", :class => 'description') + # # => "

Look ma! A class!

" + def simple_format(text, html_options={}) + start_tag = tag('p', html_options, true) + text = text.to_s.dup + text.gsub!(/\r\n?/, "\n") # \r\n and \r -> \n + text.gsub!(/\n\n+/, "

\n\n#{start_tag}") # 2+ newline -> paragraph + text.gsub!(/([^\n]\n)(?=[^\n])/, '\1
') # 1 newline -> br + text.insert 0, start_tag + text << "

" + end + + # Turns all URLs and e-mail addresses into clickable links. The :link option + # will limit what should be linked. You can add HTML attributes to the links using + # :href_options. Possible values for :link are :all (default), + # :email_addresses, and :urls. If a block is given, each URL and + # e-mail address is yielded and the result is used as the link text. + # + # ==== Examples + # auto_link("Go to http://www.rubyonrails.org and say hello to david@loudthinking.com") + # # => "Go to http://www.rubyonrails.org and + # # say hello to david@loudthinking.com" + # + # auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :urls) + # # => "Visit http://www.loudthinking.com/ + # # 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 david@loudthinking.com" + # + # post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com." + # auto_link(post_body, :href_options => { :target => '_blank' }) do |text| + # truncate(text, 15) + # end + # # => "Welcome to my new blog at http://www.m.... + # Please e-mail me at me@email.com." + # + # + # You can still use auto_link 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) # => Once upon\na time + # # => "Welcome to my new blog at http://www.myblog.com. + # Please e-mail me at me@email.com." + # + # auto_link(post_body, :all, :target => "_blank") # => Once upon\na time + # # => "Welcome to my new blog at http://www.myblog.com. + # Please e-mail me at me@email.com." + def auto_link(text, *args, &block)#link = :all, href_options = {}, &block) + return '' 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 => {}) + + case options[:link].to_sym + when :all then auto_link_email_addresses(auto_link_urls(text, options[:html], &block), &block) + when :email_addresses then auto_link_email_addresses(text, &block) + when :urls then auto_link_urls(text, options[:html], &block) + end + end + + # Creates a Cycle object whose _to_s_ method cycles through elements of an + # array every time it is called. This can be used for example, to alternate + # classes for table rows. You can use named cycles to allow nesting in loops. + # Passing a Hash as the last parameter with a :name key will create a + # named cycle. The default name for a cycle without a +:name+ key is + # "default". You can manually reset a cycle by calling reset_cycle + # and passing the name of the cycle. The current cycle string can be obtained + # anytime using the current_cycle method. + # + # ==== Examples + # # Alternate CSS classes for even and odd numbers... + # @items = [1,2,3,4] + # + # <% @items.each do |item| %> + # "> + # + # + # <% end %> + #
item
+ # + # + # # Cycle CSS classes for rows, and text colors for values within each row + # @items = x = [{:first => 'Robert', :middle => 'Daniel', :last => 'James'}, + # {:first => 'Emily', :middle => 'Shannon', :maiden => 'Pike', :last => 'Hicks'}, + # {:first => 'June', :middle => 'Dae', :last => 'Jones'}] + # <% @items.each do |item| %> + # "row_class") -%>"> + # + # <% item.values.each do |value| %> + # <%# Create a named cycle "colors" %> + # "colors") -%>"> + # <%= value %> + # + # <% end %> + # <% reset_cycle("colors") %> + # + # + # <% end %> + def cycle(first_value, *values) + if (values.last.instance_of? Hash) + params = values.pop + name = params[:name] + else + name = "default" + end + values.unshift(first_value) + + cycle = get_cycle(name) + if (cycle.nil? || cycle.values != values) + cycle = set_cycle(name, Cycle.new(*values)) + end + return cycle.to_s + end + + # Returns the current cycle string after a cycle has been started. Useful + # for complex table highlighing or any other design need which requires + # the current cycle string in more than one place. + # + # ==== Example + # # Alternate background colors + # @items = [1,2,3,4] + # <% @items.each do |item| %> + #
"> + # <%= item %> + #
+ # <% end %> + def current_cycle(name = "default") + cycle = get_cycle(name) + cycle.current_value unless cycle.nil? + end + + # Resets a cycle so that it starts from the first element the next time + # it is called. Pass in +name+ to reset a named cycle. + # + # ==== Example + # # Alternate CSS classes for even and odd numbers... + # @items = [[1,2,3,4], [5,6,3], [3,4,5,6,7,4]] + # + # <% @items.each do |item| %> + # "> + # <% item.each do |value| %> + # "colors") -%>"> + # <%= value %> + # + # <% end %> + # + # <% reset_cycle("colors") %> + # + # <% end %> + #
+ def reset_cycle(name = "default") + cycle = get_cycle(name) + cycle.reset unless cycle.nil? + end + + class Cycle #:nodoc: + attr_reader :values + + def initialize(first_value, *values) + @values = values.unshift(first_value) + reset + end + + def reset + @index = 0 + end + + def current_value + @values[previous_index].to_s + end + + def to_s + value = @values[@index].to_s + @index = next_index + return value + end + + private + + def next_index + step_index(1) + end + + def previous_index + step_index(-1) + end + + def step_index(n) + (@index + n) % @values.size + end + end + + private + # The cycle helpers need to store the cycles in a place that is + # guaranteed to be reset every time a page is rendered, so it + # uses an instance variable of ActionView::Base. + def get_cycle(name) + @_cycles = Hash.new unless defined?(@_cycles) + return @_cycles[name] + end + + def set_cycle(name, cycle_object) + @_cycles = Hash.new unless defined?(@_cycles) + @_cycles[name] = cycle_object + end + + AUTO_LINK_RE = %r{ + ( # leading text + <\w+.*?>| # leading HTML tag, or + [^=!:'"/]| # leading punctuation, or + ^ # beginning of line + ) + ( + (?:https?://)| # protocol spec, or + (?:www\.) # www.* + ) + ( + [-\w]+ # subdomain or domain + (?:\.[-\w]+)* # remaining subdomains or domain + (?::\d+)? # port + (?:/(?:[~\w\+@%=\(\)-]|(?:[,.;:'][^\s$]))*)* # path + (?:\?[\w\+@%&=.;:-]+)? # query string + (?:\#[\w\-]*)? # trailing anchor + ) + ([[:punct:]]|<|$|) # trailing text + }x unless const_defined?(:AUTO_LINK_RE) + + # 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 = {}) + extra_options = tag_options(html_options.stringify_keys) || "" + text.gsub(AUTO_LINK_RE) do + all, a, b, c, d = $&, $1, $2, $3, $4 + if a =~ /#{text}#{d}) + 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) + body = text.dup + text.gsub(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do + text = $1 + + if body.match(/]*>(.*)(#{Regexp.escape(text)})(.*)<\/a>/) + text + else + display_text = (block_given?) ? yield(text) : text + %{#{display_text}} + end + end + end + end + end +end