5 module Helpers #:nodoc:
6 # Provides methods to generate HTML tags programmatically when you can't use
7 # a Builder. By default, they output XHTML compliant tags.
11 BOOLEAN_ATTRIBUTES = Set.new(%w(disabled readonly multiple))
13 # Returns an empty HTML tag of type +name+ which by default is XHTML
14 # compliant. Set +open+ to true to create an open tag compatible
15 # with HTML 4.0 and below. Add HTML attributes by passing an attributes
16 # hash to +options+. Set +escape+ to false to disable attribute value
20 # The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and
21 # <tt>readonly</tt>), which you can give a value of true in the +options+ hash. You can use
22 # symbols or strings for the attribute names.
28 # tag("br", nil, true)
31 # tag("input", { :type => 'text', :disabled => true })
32 # # => <input type="text" disabled="disabled" />
34 # tag("img", { :src => "open & shut.png" })
35 # # => <img src="open & shut.png" />
37 # tag("img", { :src => "open & shut.png" }, false, false)
38 # # => <img src="open & shut.png" />
39 def tag(name, options = nil, open = false, escape = true)
40 "<#{name}#{tag_options(options, escape) if options}" + (open ? ">" : " />")
43 # Returns an HTML block tag of type +name+ surrounding the +content+. Add
44 # HTML attributes by passing an attributes hash to +options+.
45 # Instead of passing the content as an argument, you can also use a block
46 # in which case, you pass your +options+ as the second parameter.
47 # Set escape to false to disable attribute value escaping.
50 # The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and
51 # <tt>readonly</tt>), which you can give a value of true in the +options+ hash. You can use
52 # symbols or strings for the attribute names.
55 # content_tag(:p, "Hello world!")
56 # # => <p>Hello world!</p>
57 # content_tag(:div, content_tag(:p, "Hello world!"), :class => "strong")
58 # # => <div class="strong"><p>Hello world!</p></div>
59 # content_tag("select", options, :multiple => true)
60 # # => <select multiple="multiple">...options...</select>
62 # <% content_tag :div, :class => "strong" do -%>
65 # # => <div class="strong"><p>Hello world!</p></div>
66 def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
68 options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
69 content = capture(&block)
70 content_tag = content_tag_string(name, content, options, escape)
71 block_is_within_action_view?(block) ? concat(content_tag, block.binding) : content_tag
73 content = content_or_options_with_block
74 content_tag_string(name, content, options, escape)
78 # Returns a CDATA section with the given +content+. CDATA sections
79 # are used to escape blocks of text containing characters which would
80 # otherwise be recognized as markup. CDATA sections begin with the string
81 # <tt><![CDATA[</tt> and end with (and may not contain) the string <tt>]]></tt>.
84 # cdata_section("<hello world>")
85 # # => <![CDATA[<hello world>]]>
87 # cdata_section(File.read("hello_world.txt"))
88 # # => <![CDATA[<hello from a text file]]>
89 def cdata_section(content)
90 "<![CDATA[#{content}]]>"
93 # Returns an escaped version of +html+ without affecting existing escaped entities.
96 # escape_once("1 > 2 & 3")
97 # # => "1 < 2 & 3"
99 # escape_once("<< Accept & Checkout")
100 # # => "<< Accept & Checkout"
101 def escape_once(html)
102 html.to_s.gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |special| ERB::Util::HTML_ESCAPE[special] }
106 def content_tag_string(name, content, options, escape = true)
107 tag_options = tag_options(options, escape) if options
108 "<#{name}#{tag_options}>#{content}</#{name}>"
111 def tag_options(options, escape = true)
112 unless options.blank?
115 options.each do |key, value|
118 value = BOOLEAN_ATTRIBUTES.include?(key) ? key : escape_once(value)
119 attrs << %(#{key}="#{value}")
122 attrs = options.map { |key, value| %(#{key}="#{value}") }
124 " #{attrs.sort * ' '}" unless attrs.empty?
128 def block_is_within_action_view?(block)
129 eval("defined? _erbout", block.binding)