diff options
Diffstat (limited to 'actionpack/lib/action_view/helpers')
8 files changed, 138 insertions, 102 deletions
diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb index 9cd9d3d06a..990c30b90d 100644 --- a/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/actionpack/lib/action_view/helpers/capture_helper.rb @@ -31,10 +31,13 @@ module ActionView # </body></html> # def capture(*args, &block) - if output_buffer + # Return captured buffer in erb. + if block_called_from_erb?(block) with_output_buffer { block.call(*args) } + + # Return block result otherwise, but protect buffer also. else - block.call(*args) + with_output_buffer { return block.call(*args) } end end @@ -117,6 +120,7 @@ module ActionView ivar = "@content_for_#{name}" content = capture(&block) if block_given? instance_variable_set(ivar, "#{instance_variable_get(ivar)}#{content}") + nil end private diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 7ed6272898..1aee9ef0a2 100755 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -696,15 +696,15 @@ module ActionView class FormBuilder def date_select(method, options = {}, html_options = {}) - @template.date_select(@object_name, method, options.merge(:object => @object)) + @template.date_select(@object_name, method, options.merge(:object => @object), html_options) end def time_select(method, options = {}, html_options = {}) - @template.time_select(@object_name, method, options.merge(:object => @object)) + @template.time_select(@object_name, method, options.merge(:object => @object), html_options) end def datetime_select(method, options = {}, html_options = {}) - @template.datetime_select(@object_name, method, options.merge(:object => @object)) + @template.datetime_select(@object_name, method, options.merge(:object => @object), html_options) end end end diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index b3f8e63c1b..0ca5cebcba 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -150,7 +150,8 @@ module ActionView # You can also supply an array of TimeZone objects # as +priority_zones+, so that they will be listed above the rest of the # (long) list. (You can use TimeZone.us_zones as a convenience for - # obtaining a list of the US time zones.) + # obtaining a list of the US time zones, or a Regexp to select the zones + # of your choice) # # Finally, this method supports a <tt>:default</tt> option, which selects # a default TimeZone if the object's time zone is +nil+. @@ -164,6 +165,8 @@ module ActionView # # time_zone_select( "user", 'time_zone', [ TimeZone['Alaska'], TimeZone['Hawaii'] ]) # + # time_zone_select( "user", 'time_zone', /Australia/) + # # time_zone_select( "user", "time_zone", TZInfo::Timezone.all.sort, :model => TZInfo::Timezone) def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {}) InstanceTag.new(object, method, self, nil, options.delete(:object)).to_time_zone_select_tag(priority_zones, options, html_options) @@ -292,7 +295,8 @@ module ActionView # selected option tag. You can also supply an array of TimeZone objects # as +priority_zones+, so that they will be listed above the rest of the # (long) list. (You can use TimeZone.us_zones as a convenience for - # obtaining a list of the US time zones.) + # obtaining a list of the US time zones, or a Regexp to select the zones + # of your choice) # # The +selected+ parameter must be either +nil+, or a string that names # a TimeZone. @@ -311,6 +315,9 @@ module ActionView convert_zones = lambda { |list| list.map { |z| [ z.to_s, z.name ] } } if priority_zones + if priority_zones.is_a?(Regexp) + priority_zones = model.all.find_all {|z| z =~ priority_zones} + end zone_options += options_for_select(convert_zones[priority_zones], selected) zone_options += "<option value=\"\" disabled=\"disabled\">-------------</option>\n" diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 3a97f1390f..ccebec3692 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -6,7 +6,7 @@ module ActionView # Provides a number of methods for creating form tags that doesn't rely on an Active Record object assigned to the template like # FormHelper does. Instead, you provide the names and values manually. # - # NOTE: The HTML options <tt>disabled</tt>, <tt>readonly</tt>, and <tt>multiple</tt> can all be treated as booleans. So specifying + # NOTE: The HTML options <tt>disabled</tt>, <tt>readonly</tt>, and <tt>multiple</tt> can all be treated as booleans. So specifying # <tt>:disabled => true</tt> will give <tt>disabled="disabled"</tt>. module FormTagHelper # Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like @@ -20,15 +20,15 @@ module ActionView # * A list of parameters to feed to the URL the form will be posted to. # # ==== Examples - # form_tag('/posts') + # form_tag('/posts') # # => <form action="/posts" method="post"> # - # form_tag('/posts/1', :method => :put) + # form_tag('/posts/1', :method => :put) # # => <form action="/posts/1" method="put"> # - # form_tag('/upload', :multipart => true) + # form_tag('/upload', :multipart => true) # # => <form action="/upload" method="post" enctype="multipart/form-data"> - # + # # <% form_tag '/posts' do -%> # <div><%= submit_tag 'Save' %></div> # <% end -%> @@ -88,7 +88,7 @@ module ActionView # * <tt>:size</tt> - The number of visible characters that will fit in the input. # * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter. # * Any other key creates standard HTML attributes for the tag. - # + # # ==== Examples # text_field_tag 'name' # # => <input id="name" name="name" type="text" /> @@ -146,13 +146,13 @@ module ActionView # # => <input id="token" name="token" type="hidden" value="VUBJKB23UIVI1UU1VOBVI@" /> # # hidden_field_tag 'collected_input', '', :onchange => "alert('Input collected!')" - # # => <input id="collected_input" name="collected_input" onchange="alert('Input collected!')" + # # => <input id="collected_input" name="collected_input" onchange="alert('Input collected!')" # # type="hidden" value="" /> def hidden_field_tag(name, value = nil, options = {}) text_field_tag(name, value, options.stringify_keys.update("type" => "hidden")) end - # Creates a file upload field. If you are using file uploads then you will also need + # Creates a file upload field. If you are using file uploads then you will also need # to set the multipart option for the form tag: # # <%= form_tag { :action => "post" }, { :multipart => true } %> @@ -160,7 +160,7 @@ module ActionView # <%= submit_tag %> # <%= end_form_tag %> # - # The specified URL will then be passed a File object containing the selected file, or if the field + # The specified URL will then be passed a File object containing the selected file, or if the field # was left blank, a StringIO object. # # ==== Options @@ -181,7 +181,7 @@ module ActionView # # => <input id="resume" name="resume" type="file" value="~/resume.doc" /> # # file_field_tag 'user_pic', :accept => 'image/png,image/gif,image/jpeg' - # # => <input accept="image/png,image/gif,image/jpeg" id="user_pic" name="user_pic" type="file" /> + # # => <input accept="image/png,image/gif,image/jpeg" id="user_pic" name="user_pic" type="file" /> # # file_field_tag 'file', :accept => 'text/html', :class => 'upload', :value => 'index.html' # # => <input accept="text/html" class="upload" id="file" name="file" type="file" value="index.html" /> @@ -286,7 +286,7 @@ module ActionView tag :input, html_options end - # Creates a radio button; use groups of radio buttons named the same to allow users to + # Creates a radio button; use groups of radio buttons named the same to allow users to # select from a group of options. # # ==== Options @@ -313,14 +313,14 @@ module ActionView tag :input, html_options end - # Creates a submit button with the text <tt>value</tt> as the caption. + # Creates a submit button with the text <tt>value</tt> as the caption. # # ==== Options # * <tt>:confirm => 'question?'</tt> - This will add a JavaScript confirm # prompt with the question specified. If the user accepts, the form is # processed normally, otherwise no action is taken. # * <tt>:disabled</tt> - If true, the user will not be able to use this input. - # * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a disabled version + # * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a disabled version # of the submit button when the form is submitted. # * Any other key creates standard HTML options for the tag. # @@ -335,7 +335,7 @@ module ActionView # # => <input disabled="disabled" name="commit" type="submit" value="Save edits" /> # # submit_tag "Complete sale", :disable_with => "Please wait..." - # # => <input name="commit" onclick="this.disabled=true;this.value='Please wait...';this.form.submit();" + # # => <input name="commit" onclick="this.disabled=true;this.value='Please wait...';this.form.submit();" # # type="submit" value="Complete sale" /> # # submit_tag nil, :class => "form_submit" @@ -346,7 +346,7 @@ module ActionView # # name="commit" type="submit" value="Edit" /> def submit_tag(value = "Save changes", options = {}) options.stringify_keys! - + if disable_with = options.delete("disable_with") options["onclick"] = [ "this.setAttribute('originalValue', this.value)", @@ -358,15 +358,15 @@ module ActionView "return result;", ].join(";") end - + if confirm = options.delete("confirm") options["onclick"] ||= '' options["onclick"] += "return #{confirm_javascript_function(confirm)};" end - + tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options.stringify_keys) end - + # Displays an image which when clicked will submit the form. # # <tt>source</tt> is passed to AssetTagHelper#image_path @@ -412,7 +412,7 @@ module ActionView concat(content) concat("</fieldset>") end - + private def html_options_for_form(url_for_options, options, *parameters_for_url) returning options.stringify_keys do |html_options| @@ -420,7 +420,7 @@ module ActionView html_options["action"] = url_for(url_for_options, *parameters_for_url) end end - + def extra_tags_for_form(html_options) case method = html_options.delete("method").to_s when /^get$/i # must be case-insentive, but can't use downcase as might be nil @@ -434,12 +434,12 @@ module ActionView content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag, :style => 'margin:0;padding:0') end end - + def form_tag_html(html_options) extra_tags = extra_tags_for_form(html_options) tag(:form, html_options, true) + extra_tags end - + def form_tag_in_block(html_options, &block) content = capture(&block) concat(form_tag_html(html_options)) diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index 7404a251e4..f89b6c2f70 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -4,10 +4,10 @@ require 'action_view/helpers/prototype_helper' module ActionView module Helpers # Provides functionality for working with JavaScript in your views. - # + # # == Ajax, controls and visual effects - # - # * For information on using Ajax, see + # + # * For information on using Ajax, see # ActionView::Helpers::PrototypeHelper. # * For information on using controls and visual effects, see # ActionView::Helpers::ScriptaculousHelper. @@ -20,22 +20,22 @@ module ActionView # and ActionView::Helpers::ScriptaculousHelper), you must do one of the # following: # - # * Use <tt><%= javascript_include_tag :defaults %></tt> in the HEAD - # section of your page (recommended): This function will return + # * Use <tt><%= javascript_include_tag :defaults %></tt> in the HEAD + # section of your page (recommended): This function will return # references to the JavaScript files created by the +rails+ command in # your <tt>public/javascripts</tt> directory. Using it is recommended as - # the browser can then cache the libraries instead of fetching all the + # the browser can then cache the libraries instead of fetching all the # functions anew on every request. - # * Use <tt><%= javascript_include_tag 'prototype' %></tt>: As above, but + # * Use <tt><%= javascript_include_tag 'prototype' %></tt>: As above, but # will only include the Prototype core library, which means you are able - # to use all basic AJAX functionality. For the Scriptaculous-based - # JavaScript helpers, like visual effects, autocompletion, drag and drop + # to use all basic AJAX functionality. For the Scriptaculous-based + # JavaScript helpers, like visual effects, autocompletion, drag and drop # and so on, you should use the method described above. # * Use <tt><%= define_javascript_functions %></tt>: this will copy all the # JavaScript support functions within a single script block. Not # recommended. # - # For documentation on +javascript_include_tag+ see + # For documentation on +javascript_include_tag+ see # ActionView::Helpers::AssetTagHelper. module JavaScriptHelper unless const_defined? :JAVASCRIPT_PATH @@ -43,13 +43,13 @@ module ActionView end include PrototypeHelper - - # Returns a link that will trigger a JavaScript +function+ using the + + # Returns a link that will trigger a JavaScript +function+ using the # onclick handler and return false after the fact. # # The +function+ argument can be omitted in favor of an +update_page+ # block, which evaluates to a string when the template is rendered - # (instead of making an Ajax request first). + # (instead of making an Ajax request first). # # Examples: # link_to_function "Greeting", "alert('Hello world!')" @@ -70,36 +70,31 @@ module ActionView # <a href="#" id="more_link" onclick="try { # $("details").visualEffect("toggle_blind"); # $("more_link").update("Show me less"); - # } - # catch (e) { - # alert('RJS error:\n\n' + e.toString()); + # } + # catch (e) { + # alert('RJS error:\n\n' + e.toString()); # alert('$(\"details\").visualEffect(\"toggle_blind\"); # \n$(\"more_link\").update(\"Show me less\");'); - # throw e + # throw e # }; # return false;">Show me more</a> # def link_to_function(name, *args, &block) - html_options = args.extract_options! - function = args[0] || '' - - html_options.symbolize_keys! - function = update_page(&block) if block_given? - content_tag( - "a", name, - html_options.merge({ - :href => html_options[:href] || "#", - :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function}; return false;" - }) - ) + html_options = args.extract_options!.symbolize_keys + + function = block_given? ? update_page(&block) : args[0] || '' + onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;" + href = html_options[:href] || '#' + + content_tag(:a, name, html_options.merge(:href => href, :onclick => onclick)) end - - # Returns a button that'll trigger a JavaScript +function+ using the + + # Returns a button that'll trigger a JavaScript +function+ using the # onclick handler. # # The +function+ argument can be omitted in favor of an +update_page+ # block, which evaluates to a string when the template is rendered - # (instead of making an Ajax request first). + # (instead of making an Ajax request first). # # Examples: # button_to_function "Greeting", "alert('Hello world!')" @@ -111,45 +106,56 @@ module ActionView # page[:details].visual_effect :toggle_slide # end def button_to_function(name, *args, &block) - html_options = args.extract_options! - function = args[0] || '' - - html_options.symbolize_keys! - function = update_page(&block) if block_given? - tag(:input, html_options.merge({ - :type => "button", :value => name, - :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};" - })) + html_options = args.extract_options!.symbolize_keys + + function = block_given? ? update_page(&block) : args[0] || '' + onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};" + + tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick)) end - # Includes the Action Pack JavaScript libraries inside a single <script> + # Includes the Action Pack JavaScript libraries inside a single <script> # tag. The function first includes prototype.js and then its core extensions, # (determined by filenames starting with "prototype"). # Afterwards, any additional scripts will be included in undefined order. # # Note: The recommended approach is to copy the contents of # lib/action_view/helpers/javascripts/ into your application's - # public/javascripts/ directory, and use +javascript_include_tag+ to + # public/javascripts/ directory, and use +javascript_include_tag+ to # create remote <script> links. def define_javascript_functions javascript = "<script type=\"#{Mime::JS}\">" - - # load prototype.js and its extensions first + + # load prototype.js and its extensions first prototype_libs = Dir.glob(File.join(JAVASCRIPT_PATH, 'prototype*')).sort.reverse - prototype_libs.each do |filename| + prototype_libs.each do |filename| javascript << "\n" << IO.read(filename) end - + # load other libraries - (Dir.glob(File.join(JAVASCRIPT_PATH, '*')) - prototype_libs).each do |filename| + (Dir.glob(File.join(JAVASCRIPT_PATH, '*')) - prototype_libs).each do |filename| javascript << "\n" << IO.read(filename) end javascript << '</script>' end + + JS_ESCAPE_MAP = { + '\\' => '\\\\', + '</' => '<\/', + "\r\n" => '\n', + "\n" => '\n', + "\r" => '\n', + '"' => '\\"', + "'" => "\\'" } + # Escape carrier returns and single and double quotes for JavaScript segments. def escape_javascript(javascript) - (javascript || '').gsub('\\','\0\0').gsub('</','<\/').gsub(/\r\n|\n|\r/, "\\n").gsub(/["']/) { |m| "\\#{m}" } + if javascript + javascript.gsub(/(\\|<\/|\r\n|[\n\r"'])/) { JS_ESCAPE_MAP[$1] } + else + '' + end end # Returns a JavaScript tag with the +content+ inside. Example: @@ -163,7 +169,7 @@ module ActionView # </script> # # +html_options+ may be a hash of attributes for the <script> tag. Example: - # javascript_tag "alert('All is good')", :defer => 'defer' + # javascript_tag "alert('All is good')", :defer => 'defer' # # => <script defer="defer" type="text/javascript">alert('All is good')</script> # # Instead of passing the content as an argument, you can also use a block @@ -180,30 +186,37 @@ module ActionView content_or_options_with_block end - tag = content_tag("script", javascript_cdata_section(content), html_options.merge(:type => Mime::JS)) + tag = content_tag(:script, javascript_cdata_section(content), html_options.merge(:type => Mime::JS)) - block_given? ? concat(tag) : tag + if block_called_from_erb?(block) + concat(tag) + else + tag + end end def javascript_cdata_section(content) #:nodoc: "\n//#{cdata_section("\n#{content}\n//")}\n" end - + protected def options_for_javascript(options) - '{' + options.map {|k, v| "#{k}:#{v}"}.sort.join(', ') + '}' + if options.empty? + '{}' + else + "{#{options.keys.map { |k| "#{k}:#{options[k]}" }.sort.join(', ')}}" + end end - + def array_or_string_for_javascript(option) - js_option = if option.kind_of?(Array) + if option.kind_of?(Array) "['#{option.join('\',\'')}']" elsif !option.nil? "'#{option}'" end - js_option end end - + JavascriptHelper = JavaScriptHelper unless const_defined? :JavascriptHelper end end diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb index e1abec1847..aeafd3906d 100644 --- a/actionpack/lib/action_view/helpers/tag_helper.rb +++ b/actionpack/lib/action_view/helpers/tag_helper.rb @@ -12,14 +12,14 @@ module ActionView BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple).to_set BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map(&:to_sym)) - # Returns an empty HTML tag of type +name+ which by default is XHTML - # compliant. Set +open+ to true to create an open tag compatible - # with HTML 4.0 and below. Add HTML attributes by passing an attributes + # Returns an empty HTML tag of type +name+ which by default is XHTML + # compliant. Set +open+ to true to create an open tag compatible + # with HTML 4.0 and below. Add HTML attributes by passing an attributes # hash to +options+. Set +escape+ to false to disable attribute value # escaping. # # ==== Options - # The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and + # The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and # <tt>readonly</tt>), which you can give a value of true in the +options+ hash. You can use # symbols or strings for the attribute names. # @@ -30,7 +30,7 @@ module ActionView # tag("br", nil, true) # # => <br> # - # tag("input", { :type => 'text', :disabled => true }) + # tag("input", { :type => 'text', :disabled => true }) # # => <input type="text" disabled="disabled" /> # # tag("img", { :src => "open & shut.png" }) @@ -43,13 +43,13 @@ module ActionView end # Returns an HTML block tag of type +name+ surrounding the +content+. Add - # HTML attributes by passing an attributes hash to +options+. + # HTML attributes by passing an attributes hash to +options+. # Instead of passing the content as an argument, you can also use a block # in which case, you pass your +options+ as the second parameter. # Set escape to false to disable attribute value escaping. # # ==== Options - # The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and + # The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and # <tt>readonly</tt>), which you can give a value of true in the +options+ hash. You can use # symbols or strings for the attribute names. # @@ -68,7 +68,13 @@ module ActionView def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block) if block_given? options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash) - concat(content_tag_string(name, capture(&block), options, escape)) + content_tag = content_tag_string(name, capture(&block), options, escape) + + if block_called_from_erb?(block) + concat(content_tag) + else + content_tag + end else content_tag_string(name, content_or_options_with_block, options, escape) end @@ -102,6 +108,16 @@ module ActionView end private + BLOCK_CALLED_FROM_ERB = 'defined? __in_erb_template' + + # Check whether we're called from an erb template. + # We'd return a string in any other case, but erb <%= ... %> + # can't take an <% end %> later on, so we have to use <% ... %> + # and implicitly concat. + def block_called_from_erb?(block) + block && eval(BLOCK_CALLED_FROM_ERB, block) + end + def content_tag_string(name, content, options, escape = true) tag_options = tag_options(options, escape) if options "<#{name}#{tag_options}>#{content}</#{name}>" diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index a1a91f6b3d..a6c48737e9 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -30,11 +30,7 @@ module ActionView ActiveSupport::Deprecation.warn("The binding argument of #concat is no longer needed. Please remove it from your views and helpers.") end - if output_buffer && string - output_buffer << string - else - string - end + output_buffer << string end if RUBY_VERSION < '1.9' @@ -472,7 +468,7 @@ module ActionView [-\w]+ # subdomain or domain (?:\.[-\w]+)* # remaining subdomains or domain (?::\d+)? # port - (?:/(?:(?:[~\w\+@%=\(\)-]|(?:[,.;:][^\s$]))+)?)* # path + (?:/(?:(?:[~\w\+@%=\(\)-]|(?:[,.;:'][^\s$]))+)?)* # path (?:\?[\w\+@%&=.;-]+)? # query string (?:\#[\w\-]*)? # trailing anchor ) diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index baecd304cd..d5b7255642 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -535,7 +535,7 @@ module ActionView when method "#{method_javascript_function(method, url, href)}return false;" when popup - popup_javascript_function(popup) + 'return false;' + "#{popup_javascript_function(popup)}return false;" else html_options["onclick"] end |