Functional tests now work properly, bearing in mind whether a user is logged in or...
[depot.git] / vendor / rails / actionpack / lib / action_view / helpers / javascript_helper.rb
1 require 'action_view/helpers/tag_helper'
2 require 'action_view/helpers/prototype_helper'
3
4 module ActionView
5 module Helpers
6 # Provides functionality for working with JavaScript in your views.
7 #
8 # == Ajax, controls and visual effects
9 #
10 # * For information on using Ajax, see
11 # ActionView::Helpers::PrototypeHelper.
12 # * For information on using controls and visual effects, see
13 # ActionView::Helpers::ScriptaculousHelper.
14 #
15 # == Including the JavaScript libraries into your pages
16 #
17 # Rails includes the Prototype JavaScript framework and the Scriptaculous
18 # JavaScript controls and visual effects library. If you wish to use
19 # these libraries and their helpers (ActionView::Helpers::PrototypeHelper
20 # and ActionView::Helpers::ScriptaculousHelper), you must do one of the
21 # following:
22 #
23 # * Use <tt><%= javascript_include_tag :defaults %></tt> in the HEAD
24 # section of your page (recommended): This function will return
25 # references to the JavaScript files created by the +rails+ command in
26 # your <tt>public/javascripts</tt> directory. Using it is recommended as
27 # the browser can then cache the libraries instead of fetching all the
28 # functions anew on every request.
29 # * Use <tt><%= javascript_include_tag 'prototype' %></tt>: As above, but
30 # will only include the Prototype core library, which means you are able
31 # to use all basic AJAX functionality. For the Scriptaculous-based
32 # JavaScript helpers, like visual effects, autocompletion, drag and drop
33 # and so on, you should use the method described above.
34 #
35 # For documentation on +javascript_include_tag+ see
36 # ActionView::Helpers::AssetTagHelper.
37 module JavaScriptHelper
38 unless const_defined? :JAVASCRIPT_PATH
39 JAVASCRIPT_PATH = File.join(File.dirname(__FILE__), 'javascripts')
40 end
41
42 include PrototypeHelper
43
44 # Returns a link of the given +name+ that will trigger a JavaScript +function+ using the
45 # onclick handler and return false after the fact.
46 #
47 # The first argument +name+ is used as the link text.
48 #
49 # The next arguments are optional and may include the javascript function definition and a hash of html_options.
50 #
51 # The +function+ argument can be omitted in favor of an +update_page+
52 # block, which evaluates to a string when the template is rendered
53 # (instead of making an Ajax request first).
54 #
55 # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button"
56 #
57 # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil
58 #
59 #
60 # Examples:
61 # link_to_function "Greeting", "alert('Hello world!')"
62 # Produces:
63 # <a onclick="alert('Hello world!'); return false;" href="#">Greeting</a>
64 #
65 # link_to_function(image_tag("delete"), "if (confirm('Really?')) do_delete()")
66 # Produces:
67 # <a onclick="if (confirm('Really?')) do_delete(); return false;" href="#">
68 # <img src="/images/delete.png?" alt="Delete"/>
69 # </a>
70 #
71 # link_to_function("Show me more", nil, :id => "more_link") do |page|
72 # page[:details].visual_effect :toggle_blind
73 # page[:more_link].replace_html "Show me less"
74 # end
75 # Produces:
76 # <a href="#" id="more_link" onclick="try {
77 # $(&quot;details&quot;).visualEffect(&quot;toggle_blind&quot;);
78 # $(&quot;more_link&quot;).update(&quot;Show me less&quot;);
79 # }
80 # catch (e) {
81 # alert('RJS error:\n\n' + e.toString());
82 # alert('$(\&quot;details\&quot;).visualEffect(\&quot;toggle_blind\&quot;);
83 # \n$(\&quot;more_link\&quot;).update(\&quot;Show me less\&quot;);');
84 # throw e
85 # };
86 # return false;">Show me more</a>
87 #
88 def link_to_function(name, *args, &block)
89 html_options = args.extract_options!.symbolize_keys
90
91 function = block_given? ? update_page(&block) : args[0] || ''
92 onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;"
93 href = html_options[:href] || '#'
94
95 content_tag(:a, name, html_options.merge(:href => href, :onclick => onclick))
96 end
97
98 # Returns a button with the given +name+ text that'll trigger a JavaScript +function+ using the
99 # onclick handler.
100 #
101 # The first argument +name+ is used as the button's value or display text.
102 #
103 # The next arguments are optional and may include the javascript function definition and a hash of html_options.
104 #
105 # The +function+ argument can be omitted in favor of an +update_page+
106 # block, which evaluates to a string when the template is rendered
107 # (instead of making an Ajax request first).
108 #
109 # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button"
110 #
111 # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil
112 #
113 # Examples:
114 # button_to_function "Greeting", "alert('Hello world!')"
115 # button_to_function "Delete", "if (confirm('Really?')) do_delete()"
116 # button_to_function "Details" do |page|
117 # page[:details].visual_effect :toggle_slide
118 # end
119 # button_to_function "Details", :class => "details_button" do |page|
120 # page[:details].visual_effect :toggle_slide
121 # end
122 def button_to_function(name, *args, &block)
123 html_options = args.extract_options!.symbolize_keys
124
125 function = block_given? ? update_page(&block) : args[0] || ''
126 onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};"
127
128 tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick))
129 end
130
131 JS_ESCAPE_MAP = {
132 '\\' => '\\\\',
133 '</' => '<\/',
134 "\r\n" => '\n',
135 "\n" => '\n',
136 "\r" => '\n',
137 '"' => '\\"',
138 "'" => "\\'" }
139
140 # Escape carrier returns and single and double quotes for JavaScript segments.
141 def escape_javascript(javascript)
142 if javascript
143 javascript.gsub(/(\\|<\/|\r\n|[\n\r"'])/) { JS_ESCAPE_MAP[$1] }
144 else
145 ''
146 end
147 end
148
149 # Returns a JavaScript tag with the +content+ inside. Example:
150 # javascript_tag "alert('All is good')"
151 #
152 # Returns:
153 # <script type="text/javascript">
154 # //<![CDATA[
155 # alert('All is good')
156 # //]]>
157 # </script>
158 #
159 # +html_options+ may be a hash of attributes for the <script> tag. Example:
160 # javascript_tag "alert('All is good')", :defer => 'defer'
161 # # => <script defer="defer" type="text/javascript">alert('All is good')</script>
162 #
163 # Instead of passing the content as an argument, you can also use a block
164 # in which case, you pass your +html_options+ as the first parameter.
165 # <% javascript_tag :defer => 'defer' do -%>
166 # alert('All is good')
167 # <% end -%>
168 def javascript_tag(content_or_options_with_block = nil, html_options = {}, &block)
169 content =
170 if block_given?
171 html_options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
172 capture(&block)
173 else
174 content_or_options_with_block
175 end
176
177 tag = content_tag(:script, javascript_cdata_section(content), html_options.merge(:type => Mime::JS))
178
179 if block_called_from_erb?(block)
180 concat(tag)
181 else
182 tag
183 end
184 end
185
186 def javascript_cdata_section(content) #:nodoc:
187 "\n//#{cdata_section("\n#{content}\n//")}\n"
188 end
189
190 protected
191 def options_for_javascript(options)
192 if options.empty?
193 '{}'
194 else
195 "{#{options.keys.map { |k| "#{k}:#{options[k]}" }.sort.join(', ')}}"
196 end
197 end
198
199 def array_or_string_for_javascript(option)
200 if option.kind_of?(Array)
201 "['#{option.join('\',\'')}']"
202 elsif !option.nil?
203 "'#{option}'"
204 end
205 end
206 end
207 end
208 end