PageRenderTime 58ms CodeModel.GetById 2ms app.highlight 50ms RepoModel.GetById 2ms app.codeStats 0ms

/scalate-jruby/src/main/resources/haml-3.0.25/lib/haml/helpers.rb

http://github.com/scalate/scalate
Ruby | 605 lines | 228 code | 48 blank | 329 comment | 21 complexity | fc53e966f35a25d3dca423e69d510abb MD5 | raw file
  1module Haml
  2  # This module contains various helpful methods to make it easier to do various tasks.
  3  # {Haml::Helpers} is automatically included in the context
  4  # that a Haml template is parsed in, so all these methods are at your
  5  # disposal from within the template.
  6  module Helpers
  7    # An object that raises an error when \{#to\_s} is called.
  8    # It's used to raise an error when the return value of a helper is used
  9    # when it shouldn't be.
 10    class ErrorReturn
 11      # @param message [String] The error message to raise when \{#to\_s} is called
 12      def initialize(method)
 13        @message = <<MESSAGE
 14#{method} outputs directly to the Haml template.
 15Disregard its return value and use the - operator,
 16or use capture_haml to get the value as a String.
 17MESSAGE
 18      end
 19
 20      # Raises an error.
 21      #
 22      # @raise [Haml::Error] The error
 23      def to_s
 24        raise Haml::Error.new(@message)
 25      rescue Haml::Error => e
 26        e.backtrace.shift
 27
 28        # If the ErrorReturn is used directly in the template,
 29        # we don't want Haml's stuff to get into the backtrace,
 30        # so we get rid of the format_script line.
 31        #
 32        # We also have to subtract one from the Haml line number
 33        # since the value is passed to format_script the line after
 34        # it's actually used.
 35        if e.backtrace.first =~ /^\(eval\):\d+:in `format_script/
 36          e.backtrace.shift
 37          e.backtrace.first.gsub!(/^\(haml\):(\d+)/) {|s| "(haml):#{$1.to_i - 1}"}
 38        end
 39        raise e
 40      end
 41
 42      # @return [String] A human-readable string representation
 43      def inspect
 44        "Haml::Helpers::ErrorReturn(#{@message.inspect})"
 45      end
 46    end
 47
 48    self.extend self
 49
 50    @@action_view_defined = false
 51
 52    # @return [Boolean] Whether or not ActionView is loaded
 53    def self.action_view?
 54      @@action_view_defined
 55    end
 56
 57    # Note: this does **not** need to be called when using Haml helpers
 58    # normally in Rails.
 59    #
 60    # Initializes the current object as though it were in the same context
 61    # as a normal ActionView instance using Haml.
 62    # This is useful if you want to use the helpers in a context
 63    # other than the normal setup with ActionView.
 64    # For example:
 65    #
 66    #     context = Object.new
 67    #     class << context
 68    #       include Haml::Helpers
 69    #     end
 70    #     context.init_haml_helpers
 71    #     context.haml_tag :p, "Stuff"
 72    #
 73    def init_haml_helpers
 74      @haml_buffer = Haml::Buffer.new(@haml_buffer, Haml::Engine.new('').send(:options_for_buffer))
 75      nil
 76    end
 77
 78    # Runs a block of code in a non-Haml context
 79    # (i.e. \{#is\_haml?} will return false).
 80    #
 81    # This is mainly useful for rendering sub-templates such as partials in a non-Haml language,
 82    # particularly where helpers may behave differently when run from Haml.
 83    #
 84    # Note that this is automatically applied to Rails partials.
 85    #
 86    # @yield A block which won't register as Haml
 87    def non_haml
 88      was_active = @haml_buffer.active?
 89      @haml_buffer.active = false
 90      yield
 91    ensure
 92      @haml_buffer.active = was_active
 93    end
 94
 95    # Uses \{#preserve} to convert any newlines inside whitespace-sensitive tags
 96    # into the HTML entities for endlines.
 97    #
 98    # @param tags [Array<String>] Tags that should have newlines escaped
 99    #
100    # @overload find_and_preserve(input, tags = haml_buffer.options[:preserve])
101    #   Escapes newlines within a string.
102    #
103    #   @param input [String] The string within which to escape newlines
104    # @overload find_and_preserve(tags = haml_buffer.options[:preserve])
105    #   Escapes newlines within a block of Haml code.
106    #
107    #   @yield The block within which to escape newlines
108    def find_and_preserve(input = nil, tags = haml_buffer.options[:preserve], &block)
109      return find_and_preserve(capture_haml(&block), input || tags) if block
110      input.to_s.gsub(/<(#{tags.map(&Regexp.method(:escape)).join('|')})([^>]*)>(.*?)(<\/\1>)/im) do
111        "<#{$1}#{$2}>#{preserve($3)}</#{$1}>"
112      end
113    end
114
115    # Takes any string, finds all the newlines, and converts them to
116    # HTML entities so they'll render correctly in
117    # whitespace-sensitive tags without screwing up the indentation.
118    #
119    # @overload perserve(input)
120    #   Escapes newlines within a string.
121    #
122    #   @param input [String] The string within which to escape all newlines
123    # @overload perserve
124    #   Escapes newlines within a block of Haml code.
125    #
126    #   @yield The block within which to escape newlines
127    def preserve(input = nil, &block)
128      return preserve(capture_haml(&block)) if block
129      input.to_s.chomp("\n").gsub(/\n/, '&#x000A;').gsub(/\r/, '')
130    end
131    alias_method :flatten, :preserve
132
133    # Takes an `Enumerable` object and a block
134    # and iterates over the enum,
135    # yielding each element to a Haml block
136    # and putting the result into `<li>` elements.
137    # This creates a list of the results of the block.
138    # For example:
139    #
140    #     = list_of([['hello'], ['yall']]) do |i|
141    #       = i[0]
142    #
143    # Produces:
144    #
145    #     <li>hello</li>
146    #     <li>yall</li>
147    #
148    # And
149    #
150    #     = list_of({:title => 'All the stuff', :description => 'A book about all the stuff.'}) do |key, val|
151    #       %h3= key.humanize
152    #       %p= val
153    #
154    # Produces:
155    #
156    #     <li>
157    #       <h3>Title</h3>
158    #       <p>All the stuff</p>
159    #     </li>
160    #     <li>
161    #       <h3>Description</h3>
162    #       <p>A book about all the stuff.</p>
163    #     </li>
164    #
165    # @param enum [Enumerable] The list of objects to iterate over
166    # @yield [item] A block which contains Haml code that goes within list items
167    # @yieldparam item An element of `enum`
168    def list_of(enum, &block)
169      to_return = enum.collect do |i|
170        result = capture_haml(i, &block)
171
172        if result.count("\n") > 1
173          result.gsub!("\n", "\n  ")
174          result = "\n  #{result.strip}\n"
175        else
176          result.strip!
177        end
178
179        "<li>#{result}</li>"
180      end
181      to_return.join("\n")
182    end
183
184    # Returns a hash containing default assignments for the `xmlns`, `lang`, and `xml:lang`
185    # attributes of the `html` HTML element.
186    # For example,
187    #
188    #     %html{html_attrs}
189    #
190    # becomes
191    #
192    #     <html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en-US' lang='en-US'>
193    #
194    # @param lang [String] The value of `xml:lang` and `lang`
195    # @return [{#to_s => String}] The attribute hash
196    def html_attrs(lang = 'en-US')
197      {:xmlns => "http://www.w3.org/1999/xhtml", 'xml:lang' => lang, :lang => lang}
198    end
199
200    # Increments the number of tabs the buffer automatically adds
201    # to the lines of the template.
202    # For example:
203    #
204    #     %h1 foo
205    #     - tab_up
206    #     %p bar
207    #     - tab_down
208    #     %strong baz
209    #
210    # Produces:
211    #
212    #     <h1>foo</h1>
213    #       <p>bar</p>
214    #     <strong>baz</strong>
215    #
216    # @param i [Fixnum] The number of tabs by which to increase the indentation
217    # @see #tab_down
218    def tab_up(i = 1)
219      haml_buffer.tabulation += i
220    end
221
222    # Decrements the number of tabs the buffer automatically adds
223    # to the lines of the template.
224    #
225    # @param i [Fixnum] The number of tabs by which to decrease the indentation
226    # @see #tab_up
227    def tab_down(i = 1)
228      haml_buffer.tabulation -= i
229    end
230
231    # Sets the number of tabs the buffer automatically adds
232    # to the lines of the template,
233    # but only for the duration of the block.
234    # For example:
235    #
236    #     %h1 foo
237    #     - with_tabs(2) do
238    #       %p bar
239    #     %strong baz
240    #
241    # Produces:
242    #
243    #     <h1>foo</h1>
244    #         <p>bar</p>
245    #     <strong>baz</strong>
246    #
247    #
248    # @param i [Fixnum] The number of tabs to use
249    # @yield A block in which the indentation will be `i` spaces
250    def with_tabs(i)
251      old_tabs = haml_buffer.tabulation
252      haml_buffer.tabulation = i
253      yield
254    ensure
255      haml_buffer.tabulation = old_tabs
256    end
257
258    # Surrounds a block of Haml code with strings,
259    # with no whitespace in between.
260    # For example:
261    #
262    #     = surround '(', ')' do
263    #       %a{:href => "food"} chicken
264    #
265    # Produces:
266    #
267    #     (<a href='food'>chicken</a>)
268    #
269    # and
270    #
271    #     = surround '*' do
272    #       %strong angry
273    #
274    # Produces:
275    #
276    #     *<strong>angry</strong>*
277    #
278    # @param front [String] The string to add before the Haml
279    # @param back [String] The string to add after the Haml
280    # @yield A block of Haml to surround
281    def surround(front, back = front, &block)
282      output = capture_haml(&block)
283
284      "#{front}#{output.chomp}#{back}\n"
285    end
286
287    # Prepends a string to the beginning of a Haml block,
288    # with no whitespace between.
289    # For example:
290    #
291    #     = precede '*' do
292    #       %span.small Not really
293    #
294    # Produces:
295    #
296    #     *<span class='small'>Not really</span>
297    #
298    # @param str [String] The string to add before the Haml
299    # @yield A block of Haml to prepend to
300    def precede(str, &block)
301      "#{str}#{capture_haml(&block).chomp}\n"
302    end
303
304    # Appends a string to the end of a Haml block,
305    # with no whitespace between.
306    # For example:
307    #
308    #     click
309    #     = succeed '.' do
310    #       %a{:href=>"thing"} here
311    #
312    # Produces:
313    #
314    #     click
315    #     <a href='thing'>here</a>.
316    #
317    # @param str [String] The string to add after the Haml
318    # @yield A block of Haml to append to
319    def succeed(str, &block)
320      "#{capture_haml(&block).chomp}#{str}\n"
321    end
322
323    # Captures the result of a block of Haml code,
324    # gets rid of the excess indentation,
325    # and returns it as a string.
326    # For example, after the following,
327    #
328    #     .foo
329    #       - foo = capture_haml(13) do |a|
330    #         %p= a
331    #
332    # the local variable `foo` would be assigned to `"<p>13</p>\n"`.
333    #
334    # @param args [Array] Arguments to pass into the block
335    # @yield [args] A block of Haml code that will be converted to a string
336    # @yieldparam args [Array] `args`
337    def capture_haml(*args, &block)
338      buffer = eval('_hamlout', block.binding) rescue haml_buffer
339      with_haml_buffer(buffer) do
340        position = haml_buffer.buffer.length
341
342        haml_buffer.capture_position = position
343        block.call(*args)
344
345        captured = haml_buffer.buffer.slice!(position..-1)
346        return captured if haml_buffer.options[:ugly]
347        captured = captured.split(/^/)
348
349        min_tabs = nil
350        captured.each do |line|
351          tabs = line.index(/[^ ]/) || line.length
352          min_tabs ||= tabs
353          min_tabs = min_tabs > tabs ? tabs : min_tabs
354        end
355
356        captured.map do |line|
357          line[min_tabs..-1]
358        end.join
359      end
360    ensure
361      haml_buffer.capture_position = nil
362    end
363
364    # Outputs text directly to the Haml buffer, with the proper indentation.
365    #
366    # @param text [#to_s] The text to output
367    def haml_concat(text = "")
368      unless haml_buffer.options[:ugly] || haml_indent == 0
369        haml_buffer.buffer << haml_indent <<
370          text.to_s.gsub("\n", "\n" + haml_indent) << "\n"
371      else
372        haml_buffer.buffer << text.to_s << "\n"
373      end
374      ErrorReturn.new("haml_concat")
375    end
376
377    # @return [String] The indentation string for the current line
378    def haml_indent
379      '  ' * haml_buffer.tabulation
380    end
381
382    # Creates an HTML tag with the given name and optionally text and attributes.
383    # Can take a block that will run between the opening and closing tags.
384    # If the block is a Haml block or outputs text using \{#haml\_concat},
385    # the text will be properly indented.
386    #
387    # `name` can be a string using the standard Haml class/id shorthand
388    # (e.g. "span#foo.bar", "#foo").
389    # Just like standard Haml tags, these class and id values
390    # will be merged with manually-specified attributes.
391    #
392    # `flags` is a list of symbol flags
393    # like those that can be put at the end of a Haml tag
394    # (`:/`, `:<`, and `:>`).
395    # Currently, only `:/` and `:<` are supported.
396    #
397    # `haml_tag` outputs directly to the buffer;
398    # its return value should not be used.
399    # If you need to get the results as a string,
400    # use \{#capture\_haml\}.
401    #
402    # For example,
403    #
404    #     haml_tag :table do
405    #       haml_tag :tr do
406    #         haml_tag 'td.cell' do
407    #           haml_tag :strong, "strong!"
408    #           haml_concat "data"
409    #         end
410    #         haml_tag :td do
411    #           haml_concat "more_data"
412    #         end
413    #       end
414    #     end
415    #
416    # outputs
417    #
418    #     <table>
419    #       <tr>
420    #         <td class='cell'>
421    #           <strong>
422    #             strong!
423    #           </strong>
424    #           data
425    #         </td>
426    #         <td>
427    #           more_data
428    #         </td>
429    #       </tr>
430    #     </table>
431    #
432    # @param name [#to_s] The name of the tag
433    # @param flags [Array<Symbol>] Haml end-of-tag flags
434    #
435    # @overload haml_tag(name, *flags, attributes = {})
436    #   @yield The block of Haml code within the tag
437    # @overload haml_tag(name, text, *flags, attributes = {})
438    #   @param text [#to_s] The text within the tag
439    def haml_tag(name, *rest, &block)
440      ret = ErrorReturn.new("haml_tag")
441
442      text = rest.shift.to_s unless [Symbol, Hash, NilClass].any? {|t| rest.first.is_a? t}
443      flags = []
444      flags << rest.shift while rest.first.is_a? Symbol
445      attrs = Haml::Util.map_keys(rest.shift || {}) {|key| key.to_s}
446      name, attrs = merge_name_and_attributes(name.to_s, attrs)
447
448      attributes = Haml::Precompiler.build_attributes(haml_buffer.html?,
449                                                      haml_buffer.options[:attr_wrapper],
450                                                      attrs)
451
452      if text.nil? && block.nil? && (haml_buffer.options[:autoclose].include?(name) || flags.include?(:/))
453        haml_concat "<#{name}#{attributes} />"
454        return ret
455      end
456
457      if flags.include?(:/)
458        raise Error.new("Self-closing tags can't have content.") if text
459        raise Error.new("Illegal nesting: nesting within a self-closing tag is illegal.") if block
460      end
461
462      tag = "<#{name}#{attributes}>"
463      if block.nil?
464        text = text.to_s
465        if text.include?("\n")
466          haml_concat tag
467          tab_up
468          haml_concat text
469          tab_down
470          haml_concat "</#{name}>"
471        else
472          tag << text << "</#{name}>"
473          haml_concat tag
474        end
475        return ret
476      end
477
478      if text
479        raise Error.new("Illegal nesting: content can't be both given to haml_tag :#{name} and nested within it.")
480      end
481
482      if flags.include?(:<)
483        tag << capture_haml(&block).strip << "</#{name}>"
484        haml_concat tag
485        return ret
486      end
487
488      haml_concat tag
489      tab_up
490      block.call
491      tab_down
492      haml_concat "</#{name}>"
493
494      ret
495    end
496
497    # Characters that need to be escaped to HTML entities from user input
498    HTML_ESCAPE = { '&'=>'&amp;', '<'=>'&lt;', '>'=>'&gt;', '"'=>'&quot;', "'"=>'&#039;', }
499
500    # Returns a copy of `text` with ampersands, angle brackets and quotes
501    # escaped into HTML entities.
502    #
503    # Note that if ActionView is loaded and XSS protection is enabled
504    # (as is the default for Rails 3.0+, and optional for version 2.3.5+),
505    # this won't escape text declared as "safe".
506    #
507    # @param text [String] The string to sanitize
508    # @return [String] The sanitized string
509    def html_escape(text)
510      Haml::Util.silence_warnings {text.to_s.gsub(/[\"><&]/n) {|s| HTML_ESCAPE[s]}}
511    end
512
513    # Escapes HTML entities in `text`, but without escaping an ampersand
514    # that is already part of an escaped entity.
515    #
516    # @param text [String] The string to sanitize
517    # @return [String] The sanitized string
518    def escape_once(text)
519      Haml::Util.silence_warnings do
520        text.to_s.gsub(/[\"><]|&(?!(?:[a-zA-Z]+|(#\d+));)/n) {|s| HTML_ESCAPE[s]}
521      end
522    end
523
524    # Returns whether or not the current template is a Haml template.
525    #
526    # This function, unlike other {Haml::Helpers} functions,
527    # also works in other `ActionView` templates,
528    # where it will always return false.
529    #
530    # @return [Boolean] Whether or not the current template is a Haml template
531    def is_haml?
532      !@haml_buffer.nil? && @haml_buffer.active?
533    end
534
535    # Returns whether or not `block` is defined directly in a Haml template.
536    #
537    # @param block [Proc] A Ruby block
538    # @return [Boolean] Whether or not `block` is defined directly in a Haml template
539    def block_is_haml?(block)
540      eval('_hamlout', block.binding)
541      true
542    rescue
543      false
544    end
545
546    private
547
548    # Parses the tag name used for \{#haml\_tag}
549    # and merges it with the Ruby attributes hash.
550    def merge_name_and_attributes(name, attributes_hash = {})
551      # skip merging if no ids or classes found in name
552      return name, attributes_hash unless name =~ /^(.+?)?([\.#].*)$/
553
554      return $1 || "div", Buffer.merge_attrs(
555        Precompiler.parse_class_and_id($2), attributes_hash)
556    end
557
558    # Runs a block of code with the given buffer as the currently active buffer.
559    #
560    # @param buffer [Haml::Buffer] The Haml buffer to use temporarily
561    # @yield A block in which the given buffer should be used
562    def with_haml_buffer(buffer)
563      @haml_buffer, old_buffer = buffer, @haml_buffer
564      old_buffer.active, old_was_active = false, old_buffer.active? if old_buffer
565      @haml_buffer.active, was_active = true, @haml_buffer.active?
566      yield
567    ensure
568      @haml_buffer.active = was_active
569      old_buffer.active = old_was_active if old_buffer
570      @haml_buffer = old_buffer
571    end
572
573    # The current {Haml::Buffer} object.
574    #
575    # @return [Haml::Buffer]
576    def haml_buffer
577      @haml_buffer
578    end
579
580    # Gives a proc the same local `_hamlout` and `_erbout` variables
581    # that the current template has.
582    #
583    # @param proc [#call] The proc to bind
584    # @return [Proc] A new proc with the new variables bound
585    def haml_bind_proc(&proc)
586      _hamlout = haml_buffer
587      _erbout = _hamlout.buffer
588      proc { |*args| proc.call(*args) }
589    end
590  end
591end
592
593# @private
594class Object
595  # Haml overrides various `ActionView` helpers,
596  # which call an \{#is\_haml?} method
597  # to determine whether or not the current context object
598  # is a proper Haml context.
599  # Because `ActionView` helpers may be included in non-`ActionView::Base` classes,
600  # it's a good idea to define \{#is\_haml?} for all objects.
601  def is_haml?
602    false
603  end
604end
605