lib/padrino-helpers/format_helpers.rb



module Padrino
  module Helpers
    module FormatHelpers

      # Returns escaped text to protect against malicious content
      def escape_html(text)
        Rack::Utils.escape_html(text)
      end
      alias h escape_html
      alias sanitize_html escape_html

      # Returns escaped text to protect against malicious content
      # Returns blank if the text is empty
      def h!(text, blank_text = ' ')
        return blank_text if text.nil? || text.empty?
        h text
      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 <p> tags. One newline (\n) is considered as a linebreak and a <br /> tag is appended.
      # This method does not remove the newlines from the text.
      # simple_format("hello\nworld") # => "<p>hello<br/>world</p>"
      def simple_format(text, html_options={})
        start_tag = tag('p', html_options.merge(:open => true))
        text = text.to_s.dup
        text.gsub!(/\r\n?/, "\n")                    # \r\n and \r -> \n
        text.gsub!(/\n\n+/, "</p>\n\n#{start_tag}")  # 2+ newline  -> paragraph
        text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline   -> br
        text.insert 0, start_tag
        text << "</p>"
      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
      # pluralize(2, 'person') => '2 people'
      def pluralize(count, singular, plural = nil)
        "#{count || 0} " + ((count == 1 || count == '1') ? singular : (plural || singular.pluralize))
      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 "…") for a total length not exceeding :length.
      # truncate("Once upon a time in a world far far away", :length => 8) => "Once upon..."
      def truncate(text, *args)
        options = args.extract_options!
        options.reverse_merge!(:length => 30, :omission => "...")
        if text
          len = options[:length] - options[:omission].length
          chars = text
          (chars.length > options[:length] ? chars[0...len] + options[:omission] : text).to_s
        end
      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).
      # word_wrap('Once upon a time', :line_width => 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

      # Smart time helper which returns relative text representing times for recent dates
      # and absolutes for dates that are far removed from the current date
      # time_in_words(10.days.ago) => '10 days ago'
      def time_in_words(date)
        date = date.to_date
        date = Date.parse(date, true) unless /Date.*/ =~ date.class.to_s
        days = (date - Date.today).to_i

        return 'today'     if days >= 0 and days < 1
        return 'tomorrow'  if days >= 1 and days < 2
        return 'yesterday' if days >= -1 and days < 0

        return "in #{days} days"      if days.abs < 60 and days > 0
        return "#{days.abs} days ago" if days.abs < 60 and days < 0

        return date.strftime('%A, %B %e') if days.abs < 182
        return date.strftime('%A, %B %e, %Y')
      end
      alias time_ago time_in_words

      # Returns relative time in words referencing the given date
      # relative_time_ago(Time.now) => 'about a minute ago'
      def relative_time_ago(from_time)
        distance_in_minutes = (((Time.now - from_time.to_time).abs)/60).round
        case distance_in_minutes
          when 0..1 then 'about a minute'
          when 2..44 then "#{distance_in_minutes} minutes"
          when 45..89 then 'about 1 hour'
          when 90..1439 then "about #{(distance_in_minutes.to_f / 60.0).round} hours"
          when 1440..2879 then '1 day'
          when 2880..43199 then "#{(distance_in_minutes / 1440).round} days"
          when 43200..86399 then 'about 1 month'
          when 86400..525599 then "#{(distance_in_minutes / 43200).round} months"
          when 525600..1051199 then 'about 1 year'
          else "over #{(distance_in_minutes / 525600).round} years"
        end
      end

      # Used in xxxx.js.erb files to escape html so that it can be passed to javascript from Padrino
      # js_escape_html("<h1>Hey</h1>")
      def js_escape_html(html_content)
        return '' unless html_content
        javascript_mapping = { '\\' => '\\\\', '</' => '<\/', "\r\n" => '\n', "\n" => '\n' }
        javascript_mapping.merge("\r" => '\n', '"' => '\\"', "'" => "\\'")
        escaped_string = html_content.gsub(/(\\|<\/|\r\n|[\n\r"'])/) { javascript_mapping[$1] }
        "\"#{escaped_string}\""
      end

      alias escape_javascript js_escape_html
      alias escape_for_javascript js_escape_html

    end
  end
end