From f4ccc179530d5b9436da87d3c221dfa8fa89119a Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 21 Jun 2008 14:54:10 -0700 Subject: Performance: javascript helper tweaks to speed up escaping and reduce object allocations when building options strings --- .../lib/action_view/helpers/javascript_helper.rb | 129 ++++++++++++--------- 1 file changed, 71 insertions(+), 58 deletions(-) (limited to 'actionpack/lib/action_view/helpers/javascript_helper.rb') diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index 7404a251e4..31571de6c4 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 <%= javascript_include_tag :defaults %> in the HEAD - # section of your page (recommended): This function will return + # * Use <%= javascript_include_tag :defaults %> in the HEAD + # section of your page (recommended): This function will return # references to the JavaScript files created by the +rails+ command in # your public/javascripts 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 <%= javascript_include_tag 'prototype' %>: As above, but + # * Use <%= javascript_include_tag 'prototype' %>: 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 <%= define_javascript_functions %>: 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 # Show me more # 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 ' 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(' # # +html_options+ may be a hash of attributes for the # # 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 -- cgit v1.2.3