actionview 4.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionview might be problematic. Click here for more details.

Files changed (106) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +274 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +34 -0
  5. data/lib/action_view.rb +97 -0
  6. data/lib/action_view/base.rb +205 -0
  7. data/lib/action_view/buffers.rb +49 -0
  8. data/lib/action_view/context.rb +36 -0
  9. data/lib/action_view/dependency_tracker.rb +93 -0
  10. data/lib/action_view/digestor.rb +116 -0
  11. data/lib/action_view/flows.rb +76 -0
  12. data/lib/action_view/helpers.rb +64 -0
  13. data/lib/action_view/helpers/active_model_helper.rb +49 -0
  14. data/lib/action_view/helpers/asset_tag_helper.rb +322 -0
  15. data/lib/action_view/helpers/asset_url_helper.rb +355 -0
  16. data/lib/action_view/helpers/atom_feed_helper.rb +203 -0
  17. data/lib/action_view/helpers/cache_helper.rb +200 -0
  18. data/lib/action_view/helpers/capture_helper.rb +216 -0
  19. data/lib/action_view/helpers/controller_helper.rb +25 -0
  20. data/lib/action_view/helpers/csrf_helper.rb +30 -0
  21. data/lib/action_view/helpers/date_helper.rb +1075 -0
  22. data/lib/action_view/helpers/debug_helper.rb +39 -0
  23. data/lib/action_view/helpers/form_helper.rb +1876 -0
  24. data/lib/action_view/helpers/form_options_helper.rb +843 -0
  25. data/lib/action_view/helpers/form_tag_helper.rb +746 -0
  26. data/lib/action_view/helpers/javascript_helper.rb +75 -0
  27. data/lib/action_view/helpers/number_helper.rb +425 -0
  28. data/lib/action_view/helpers/output_safety_helper.rb +38 -0
  29. data/lib/action_view/helpers/record_tag_helper.rb +108 -0
  30. data/lib/action_view/helpers/rendering_helper.rb +90 -0
  31. data/lib/action_view/helpers/sanitize_helper.rb +256 -0
  32. data/lib/action_view/helpers/tag_helper.rb +176 -0
  33. data/lib/action_view/helpers/tags.rb +41 -0
  34. data/lib/action_view/helpers/tags/base.rb +148 -0
  35. data/lib/action_view/helpers/tags/check_box.rb +64 -0
  36. data/lib/action_view/helpers/tags/checkable.rb +16 -0
  37. data/lib/action_view/helpers/tags/collection_check_boxes.rb +44 -0
  38. data/lib/action_view/helpers/tags/collection_helpers.rb +85 -0
  39. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +36 -0
  40. data/lib/action_view/helpers/tags/collection_select.rb +28 -0
  41. data/lib/action_view/helpers/tags/color_field.rb +25 -0
  42. data/lib/action_view/helpers/tags/date_field.rb +13 -0
  43. data/lib/action_view/helpers/tags/date_select.rb +72 -0
  44. data/lib/action_view/helpers/tags/datetime_field.rb +22 -0
  45. data/lib/action_view/helpers/tags/datetime_local_field.rb +19 -0
  46. data/lib/action_view/helpers/tags/datetime_select.rb +8 -0
  47. data/lib/action_view/helpers/tags/email_field.rb +8 -0
  48. data/lib/action_view/helpers/tags/file_field.rb +8 -0
  49. data/lib/action_view/helpers/tags/grouped_collection_select.rb +29 -0
  50. data/lib/action_view/helpers/tags/hidden_field.rb +8 -0
  51. data/lib/action_view/helpers/tags/label.rb +65 -0
  52. data/lib/action_view/helpers/tags/month_field.rb +13 -0
  53. data/lib/action_view/helpers/tags/number_field.rb +18 -0
  54. data/lib/action_view/helpers/tags/password_field.rb +12 -0
  55. data/lib/action_view/helpers/tags/radio_button.rb +31 -0
  56. data/lib/action_view/helpers/tags/range_field.rb +8 -0
  57. data/lib/action_view/helpers/tags/search_field.rb +24 -0
  58. data/lib/action_view/helpers/tags/select.rb +41 -0
  59. data/lib/action_view/helpers/tags/tel_field.rb +8 -0
  60. data/lib/action_view/helpers/tags/text_area.rb +18 -0
  61. data/lib/action_view/helpers/tags/text_field.rb +29 -0
  62. data/lib/action_view/helpers/tags/time_field.rb +13 -0
  63. data/lib/action_view/helpers/tags/time_select.rb +8 -0
  64. data/lib/action_view/helpers/tags/time_zone_select.rb +20 -0
  65. data/lib/action_view/helpers/tags/url_field.rb +8 -0
  66. data/lib/action_view/helpers/tags/week_field.rb +13 -0
  67. data/lib/action_view/helpers/text_helper.rb +447 -0
  68. data/lib/action_view/helpers/translation_helper.rb +111 -0
  69. data/lib/action_view/helpers/url_helper.rb +625 -0
  70. data/lib/action_view/layouts.rb +426 -0
  71. data/lib/action_view/locale/en.yml +56 -0
  72. data/lib/action_view/log_subscriber.rb +44 -0
  73. data/lib/action_view/lookup_context.rb +249 -0
  74. data/lib/action_view/model_naming.rb +12 -0
  75. data/lib/action_view/path_set.rb +77 -0
  76. data/lib/action_view/railtie.rb +49 -0
  77. data/lib/action_view/record_identifier.rb +84 -0
  78. data/lib/action_view/renderer/abstract_renderer.rb +47 -0
  79. data/lib/action_view/renderer/partial_renderer.rb +492 -0
  80. data/lib/action_view/renderer/renderer.rb +50 -0
  81. data/lib/action_view/renderer/streaming_template_renderer.rb +103 -0
  82. data/lib/action_view/renderer/template_renderer.rb +96 -0
  83. data/lib/action_view/rendering.rb +145 -0
  84. data/lib/action_view/routing_url_for.rb +109 -0
  85. data/lib/action_view/tasks/dependencies.rake +17 -0
  86. data/lib/action_view/template.rb +340 -0
  87. data/lib/action_view/template/error.rb +141 -0
  88. data/lib/action_view/template/handlers.rb +53 -0
  89. data/lib/action_view/template/handlers/builder.rb +26 -0
  90. data/lib/action_view/template/handlers/erb.rb +145 -0
  91. data/lib/action_view/template/handlers/raw.rb +11 -0
  92. data/lib/action_view/template/resolver.rb +329 -0
  93. data/lib/action_view/template/text.rb +34 -0
  94. data/lib/action_view/template/types.rb +57 -0
  95. data/lib/action_view/test_case.rb +272 -0
  96. data/lib/action_view/testing/resolvers.rb +50 -0
  97. data/lib/action_view/vendor/html-scanner.rb +20 -0
  98. data/lib/action_view/vendor/html-scanner/html/document.rb +68 -0
  99. data/lib/action_view/vendor/html-scanner/html/node.rb +532 -0
  100. data/lib/action_view/vendor/html-scanner/html/sanitizer.rb +188 -0
  101. data/lib/action_view/vendor/html-scanner/html/selector.rb +830 -0
  102. data/lib/action_view/vendor/html-scanner/html/tokenizer.rb +107 -0
  103. data/lib/action_view/vendor/html-scanner/html/version.rb +11 -0
  104. data/lib/action_view/version.rb +11 -0
  105. data/lib/action_view/view_paths.rb +96 -0
  106. metadata +218 -0
@@ -0,0 +1,746 @@
1
+ require 'cgi'
2
+ require 'action_view/helpers/tag_helper'
3
+ require 'active_support/core_ext/string/output_safety'
4
+ require 'active_support/core_ext/module/attribute_accessors'
5
+
6
+ module ActionView
7
+ # = Action View Form Tag Helpers
8
+ module Helpers
9
+ # Provides a number of methods for creating form tags that don't rely on an Active Record object assigned to the template like
10
+ # FormHelper does. Instead, you provide the names and values manually.
11
+ #
12
+ # NOTE: The HTML options <tt>disabled</tt>, <tt>readonly</tt>, and <tt>multiple</tt> can all be treated as booleans. So specifying
13
+ # <tt>disabled: true</tt> will give <tt>disabled="disabled"</tt>.
14
+ module FormTagHelper
15
+ extend ActiveSupport::Concern
16
+
17
+ include UrlHelper
18
+ include TextHelper
19
+
20
+ mattr_accessor :embed_authenticity_token_in_remote_forms
21
+ self.embed_authenticity_token_in_remote_forms = false
22
+
23
+ # Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like
24
+ # ActionController::Base#url_for. The method for the form defaults to POST.
25
+ #
26
+ # ==== Options
27
+ # * <tt>:multipart</tt> - If set to true, the enctype is set to "multipart/form-data".
28
+ # * <tt>:method</tt> - The method to use when submitting the form, usually either "get" or "post".
29
+ # If "patch", "put", "delete", or another verb is used, a hidden input with name <tt>_method</tt>
30
+ # is added to simulate the verb over post.
31
+ # * <tt>:authenticity_token</tt> - Authenticity token to use in the form. Use only if you need to
32
+ # pass custom authenticity token string, or to not add authenticity_token field at all
33
+ # (by passing <tt>false</tt>). Remote forms may omit the embedded authenticity token
34
+ # by setting <tt>config.action_view.embed_authenticity_token_in_remote_forms = false</tt>.
35
+ # This is helpful when you're fragment-caching the form. Remote forms get the
36
+ # authenticity token from the <tt>meta</tt> tag, so embedding is unnecessary unless you
37
+ # support browsers without JavaScript.
38
+ # * A list of parameters to feed to the URL the form will be posted to.
39
+ # * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the
40
+ # submit behavior. By default this behavior is an ajax submit.
41
+ # * <tt>:enforce_utf8</tt> - If set to false, a hidden input with name utf8 is not output.
42
+ #
43
+ # ==== Examples
44
+ # form_tag('/posts')
45
+ # # => <form action="/posts" method="post">
46
+ #
47
+ # form_tag('/posts/1', method: :put)
48
+ # # => <form action="/posts/1" method="post"> ... <input name="_method" type="hidden" value="put" /> ...
49
+ #
50
+ # form_tag('/upload', multipart: true)
51
+ # # => <form action="/upload" method="post" enctype="multipart/form-data">
52
+ #
53
+ # <%= form_tag('/posts') do -%>
54
+ # <div><%= submit_tag 'Save' %></div>
55
+ # <% end -%>
56
+ # # => <form action="/posts" method="post"><div><input type="submit" name="commit" value="Save" /></div></form>
57
+ #
58
+ # <%= form_tag('/posts', remote: true) %>
59
+ # # => <form action="/posts" method="post" data-remote="true">
60
+ #
61
+ # form_tag('http://far.away.com/form', authenticity_token: false)
62
+ # # form without authenticity token
63
+ #
64
+ # form_tag('http://far.away.com/form', authenticity_token: "cf50faa3fe97702ca1ae")
65
+ # # form with custom authenticity token
66
+ #
67
+ def form_tag(url_for_options = {}, options = {}, &block)
68
+ html_options = html_options_for_form(url_for_options, options)
69
+ if block_given?
70
+ form_tag_in_block(html_options, &block)
71
+ else
72
+ form_tag_html(html_options)
73
+ end
74
+ end
75
+
76
+ # Creates a dropdown selection box, or if the <tt>:multiple</tt> option is set to true, a multiple
77
+ # choice selection box.
78
+ #
79
+ # Helpers::FormOptions can be used to create common select boxes such as countries, time zones, or
80
+ # associated records. <tt>option_tags</tt> is a string containing the option tags for the select box.
81
+ #
82
+ # ==== Options
83
+ # * <tt>:multiple</tt> - If set to true the selection will allow multiple choices.
84
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
85
+ # * <tt>:include_blank</tt> - If set to true, an empty option will be created.
86
+ # * <tt>:prompt</tt> - Create a prompt option with blank value and the text asking user to select something
87
+ # * Any other key creates standard HTML attributes for the tag.
88
+ #
89
+ # ==== Examples
90
+ # select_tag "people", options_from_collection_for_select(@people, "id", "name")
91
+ # # <select id="people" name="people"><option value="1">David</option></select>
92
+ #
93
+ # select_tag "people", "<option>David</option>".html_safe
94
+ # # => <select id="people" name="people"><option>David</option></select>
95
+ #
96
+ # select_tag "count", "<option>1</option><option>2</option><option>3</option><option>4</option>".html_safe
97
+ # # => <select id="count" name="count"><option>1</option><option>2</option>
98
+ # # <option>3</option><option>4</option></select>
99
+ #
100
+ # select_tag "colors", "<option>Red</option><option>Green</option><option>Blue</option>".html_safe, multiple: true
101
+ # # => <select id="colors" multiple="multiple" name="colors[]"><option>Red</option>
102
+ # # <option>Green</option><option>Blue</option></select>
103
+ #
104
+ # select_tag "locations", "<option>Home</option><option selected='selected'>Work</option><option>Out</option>".html_safe
105
+ # # => <select id="locations" name="locations"><option>Home</option><option selected='selected'>Work</option>
106
+ # # <option>Out</option></select>
107
+ #
108
+ # select_tag "access", "<option>Read</option><option>Write</option>".html_safe, multiple: true, class: 'form_input'
109
+ # # => <select class="form_input" id="access" multiple="multiple" name="access[]"><option>Read</option>
110
+ # # <option>Write</option></select>
111
+ #
112
+ # select_tag "people", options_from_collection_for_select(@people, "id", "name"), include_blank: true
113
+ # # => <select id="people" name="people"><option value=""></option><option value="1">David</option></select>
114
+ #
115
+ # select_tag "people", options_from_collection_for_select(@people, "id", "name"), prompt: "Select something"
116
+ # # => <select id="people" name="people"><option value="">Select something</option><option value="1">David</option></select>
117
+ #
118
+ # select_tag "destination", "<option>NYC</option><option>Paris</option><option>Rome</option>".html_safe, disabled: true
119
+ # # => <select disabled="disabled" id="destination" name="destination"><option>NYC</option>
120
+ # # <option>Paris</option><option>Rome</option></select>
121
+ #
122
+ # select_tag "credit_card", options_for_select([ "VISA", "MasterCard" ], "MasterCard")
123
+ # # => <select id="credit_card" name="credit_card"><option>VISA</option>
124
+ # # <option selected="selected">MasterCard</option></select>
125
+ def select_tag(name, option_tags = nil, options = {})
126
+ option_tags ||= ""
127
+ html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name
128
+
129
+ if options.delete(:include_blank)
130
+ option_tags = content_tag(:option, '', :value => '').safe_concat(option_tags)
131
+ end
132
+
133
+ if prompt = options.delete(:prompt)
134
+ option_tags = content_tag(:option, prompt, :value => '').safe_concat(option_tags)
135
+ end
136
+
137
+ content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
138
+ end
139
+
140
+ # Creates a standard text field; use these text fields to input smaller chunks of text like a username
141
+ # or a search query.
142
+ #
143
+ # ==== Options
144
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
145
+ # * <tt>:size</tt> - The number of visible characters that will fit in the input.
146
+ # * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter.
147
+ # * <tt>:placeholder</tt> - The text contained in the field by default which is removed when the field receives focus.
148
+ # * Any other key creates standard HTML attributes for the tag.
149
+ #
150
+ # ==== Examples
151
+ # text_field_tag 'name'
152
+ # # => <input id="name" name="name" type="text" />
153
+ #
154
+ # text_field_tag 'query', 'Enter your search query here'
155
+ # # => <input id="query" name="query" type="text" value="Enter your search query here" />
156
+ #
157
+ # text_field_tag 'search', nil, placeholder: 'Enter search term...'
158
+ # # => <input id="search" name="search" placeholder="Enter search term..." type="text" />
159
+ #
160
+ # text_field_tag 'request', nil, class: 'special_input'
161
+ # # => <input class="special_input" id="request" name="request" type="text" />
162
+ #
163
+ # text_field_tag 'address', '', size: 75
164
+ # # => <input id="address" name="address" size="75" type="text" value="" />
165
+ #
166
+ # text_field_tag 'zip', nil, maxlength: 5
167
+ # # => <input id="zip" maxlength="5" name="zip" type="text" />
168
+ #
169
+ # text_field_tag 'payment_amount', '$0.00', disabled: true
170
+ # # => <input disabled="disabled" id="payment_amount" name="payment_amount" type="text" value="$0.00" />
171
+ #
172
+ # text_field_tag 'ip', '0.0.0.0', maxlength: 15, size: 20, class: "ip-input"
173
+ # # => <input class="ip-input" id="ip" maxlength="15" name="ip" size="20" type="text" value="0.0.0.0" />
174
+ def text_field_tag(name, value = nil, options = {})
175
+ tag :input, { "type" => "text", "name" => name, "id" => sanitize_to_id(name), "value" => value }.update(options.stringify_keys)
176
+ end
177
+
178
+ # Creates a label element. Accepts a block.
179
+ #
180
+ # ==== Options
181
+ # * Creates standard HTML attributes for the tag.
182
+ #
183
+ # ==== Examples
184
+ # label_tag 'name'
185
+ # # => <label for="name">Name</label>
186
+ #
187
+ # label_tag 'name', 'Your name'
188
+ # # => <label for="name">Your name</label>
189
+ #
190
+ # label_tag 'name', nil, class: 'small_label'
191
+ # # => <label for="name" class="small_label">Name</label>
192
+ def label_tag(name = nil, content_or_options = nil, options = nil, &block)
193
+ if block_given? && content_or_options.is_a?(Hash)
194
+ options = content_or_options = content_or_options.stringify_keys
195
+ else
196
+ options ||= {}
197
+ options = options.stringify_keys
198
+ end
199
+ options["for"] = sanitize_to_id(name) unless name.blank? || options.has_key?("for")
200
+ content_tag :label, content_or_options || name.to_s.humanize, options, &block
201
+ end
202
+
203
+ # Creates a hidden form input field used to transmit data that would be lost due to HTTP's statelessness or
204
+ # data that should be hidden from the user.
205
+ #
206
+ # ==== Options
207
+ # * Creates standard HTML attributes for the tag.
208
+ #
209
+ # ==== Examples
210
+ # hidden_field_tag 'tags_list'
211
+ # # => <input id="tags_list" name="tags_list" type="hidden" />
212
+ #
213
+ # hidden_field_tag 'token', 'VUBJKB23UIVI1UU1VOBVI@'
214
+ # # => <input id="token" name="token" type="hidden" value="VUBJKB23UIVI1UU1VOBVI@" />
215
+ #
216
+ # hidden_field_tag 'collected_input', '', onchange: "alert('Input collected!')"
217
+ # # => <input id="collected_input" name="collected_input" onchange="alert('Input collected!')"
218
+ # # type="hidden" value="" />
219
+ def hidden_field_tag(name, value = nil, options = {})
220
+ text_field_tag(name, value, options.stringify_keys.update("type" => "hidden"))
221
+ end
222
+
223
+ # Creates a file upload field. If you are using file uploads then you will also need
224
+ # to set the multipart option for the form tag:
225
+ #
226
+ # <%= form_tag '/upload', multipart: true do %>
227
+ # <label for="file">File to Upload</label> <%= file_field_tag "file" %>
228
+ # <%= submit_tag %>
229
+ # <% end %>
230
+ #
231
+ # The specified URL will then be passed a File object containing the selected file, or if the field
232
+ # was left blank, a StringIO object.
233
+ #
234
+ # ==== Options
235
+ # * Creates standard HTML attributes for the tag.
236
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
237
+ # * <tt>:multiple</tt> - If set to true, *in most updated browsers* the user will be allowed to select multiple files.
238
+ # * <tt>:accept</tt> - If set to one or multiple mime-types, the user will be suggested a filter when choosing a file. You still need to set up model validations.
239
+ #
240
+ # ==== Examples
241
+ # file_field_tag 'attachment'
242
+ # # => <input id="attachment" name="attachment" type="file" />
243
+ #
244
+ # file_field_tag 'avatar', class: 'profile_input'
245
+ # # => <input class="profile_input" id="avatar" name="avatar" type="file" />
246
+ #
247
+ # file_field_tag 'picture', disabled: true
248
+ # # => <input disabled="disabled" id="picture" name="picture" type="file" />
249
+ #
250
+ # file_field_tag 'resume', value: '~/resume.doc'
251
+ # # => <input id="resume" name="resume" type="file" value="~/resume.doc" />
252
+ #
253
+ # file_field_tag 'user_pic', accept: 'image/png,image/gif,image/jpeg'
254
+ # # => <input accept="image/png,image/gif,image/jpeg" id="user_pic" name="user_pic" type="file" />
255
+ #
256
+ # file_field_tag 'file', accept: 'text/html', class: 'upload', value: 'index.html'
257
+ # # => <input accept="text/html" class="upload" id="file" name="file" type="file" value="index.html" />
258
+ def file_field_tag(name, options = {})
259
+ text_field_tag(name, nil, options.update("type" => "file"))
260
+ end
261
+
262
+ # Creates a password field, a masked text field that will hide the users input behind a mask character.
263
+ #
264
+ # ==== Options
265
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
266
+ # * <tt>:size</tt> - The number of visible characters that will fit in the input.
267
+ # * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter.
268
+ # * Any other key creates standard HTML attributes for the tag.
269
+ #
270
+ # ==== Examples
271
+ # password_field_tag 'pass'
272
+ # # => <input id="pass" name="pass" type="password" />
273
+ #
274
+ # password_field_tag 'secret', 'Your secret here'
275
+ # # => <input id="secret" name="secret" type="password" value="Your secret here" />
276
+ #
277
+ # password_field_tag 'masked', nil, class: 'masked_input_field'
278
+ # # => <input class="masked_input_field" id="masked" name="masked" type="password" />
279
+ #
280
+ # password_field_tag 'token', '', size: 15
281
+ # # => <input id="token" name="token" size="15" type="password" value="" />
282
+ #
283
+ # password_field_tag 'key', nil, maxlength: 16
284
+ # # => <input id="key" maxlength="16" name="key" type="password" />
285
+ #
286
+ # password_field_tag 'confirm_pass', nil, disabled: true
287
+ # # => <input disabled="disabled" id="confirm_pass" name="confirm_pass" type="password" />
288
+ #
289
+ # password_field_tag 'pin', '1234', maxlength: 4, size: 6, class: "pin_input"
290
+ # # => <input class="pin_input" id="pin" maxlength="4" name="pin" size="6" type="password" value="1234" />
291
+ def password_field_tag(name = "password", value = nil, options = {})
292
+ text_field_tag(name, value, options.update("type" => "password"))
293
+ end
294
+
295
+ # Creates a text input area; use a textarea for longer text inputs such as blog posts or descriptions.
296
+ #
297
+ # ==== Options
298
+ # * <tt>:size</tt> - A string specifying the dimensions (columns by rows) of the textarea (e.g., "25x10").
299
+ # * <tt>:rows</tt> - Specify the number of rows in the textarea
300
+ # * <tt>:cols</tt> - Specify the number of columns in the textarea
301
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
302
+ # * <tt>:escape</tt> - By default, the contents of the text input are HTML escaped.
303
+ # If you need unescaped contents, set this to false.
304
+ # * Any other key creates standard HTML attributes for the tag.
305
+ #
306
+ # ==== Examples
307
+ # text_area_tag 'post'
308
+ # # => <textarea id="post" name="post"></textarea>
309
+ #
310
+ # text_area_tag 'bio', @user.bio
311
+ # # => <textarea id="bio" name="bio">This is my biography.</textarea>
312
+ #
313
+ # text_area_tag 'body', nil, rows: 10, cols: 25
314
+ # # => <textarea cols="25" id="body" name="body" rows="10"></textarea>
315
+ #
316
+ # text_area_tag 'body', nil, size: "25x10"
317
+ # # => <textarea name="body" id="body" cols="25" rows="10"></textarea>
318
+ #
319
+ # text_area_tag 'description', "Description goes here.", disabled: true
320
+ # # => <textarea disabled="disabled" id="description" name="description">Description goes here.</textarea>
321
+ #
322
+ # text_area_tag 'comment', nil, class: 'comment_input'
323
+ # # => <textarea class="comment_input" id="comment" name="comment"></textarea>
324
+ def text_area_tag(name, content = nil, options = {})
325
+ options = options.stringify_keys
326
+
327
+ if size = options.delete("size")
328
+ options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
329
+ end
330
+
331
+ escape = options.delete("escape") { true }
332
+ content = ERB::Util.html_escape(content) if escape
333
+
334
+ content_tag :textarea, content.to_s.html_safe, { "name" => name, "id" => sanitize_to_id(name) }.update(options)
335
+ end
336
+
337
+ # Creates a check box form input tag.
338
+ #
339
+ # ==== Options
340
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
341
+ # * Any other key creates standard HTML options for the tag.
342
+ #
343
+ # ==== Examples
344
+ # check_box_tag 'accept'
345
+ # # => <input id="accept" name="accept" type="checkbox" value="1" />
346
+ #
347
+ # check_box_tag 'rock', 'rock music'
348
+ # # => <input id="rock" name="rock" type="checkbox" value="rock music" />
349
+ #
350
+ # check_box_tag 'receive_email', 'yes', true
351
+ # # => <input checked="checked" id="receive_email" name="receive_email" type="checkbox" value="yes" />
352
+ #
353
+ # check_box_tag 'tos', 'yes', false, class: 'accept_tos'
354
+ # # => <input class="accept_tos" id="tos" name="tos" type="checkbox" value="yes" />
355
+ #
356
+ # check_box_tag 'eula', 'accepted', false, disabled: true
357
+ # # => <input disabled="disabled" id="eula" name="eula" type="checkbox" value="accepted" />
358
+ def check_box_tag(name, value = "1", checked = false, options = {})
359
+ html_options = { "type" => "checkbox", "name" => name, "id" => sanitize_to_id(name), "value" => value }.update(options.stringify_keys)
360
+ html_options["checked"] = "checked" if checked
361
+ tag :input, html_options
362
+ end
363
+
364
+ # Creates a radio button; use groups of radio buttons named the same to allow users to
365
+ # select from a group of options.
366
+ #
367
+ # ==== Options
368
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
369
+ # * Any other key creates standard HTML options for the tag.
370
+ #
371
+ # ==== Examples
372
+ # radio_button_tag 'gender', 'male'
373
+ # # => <input id="gender_male" name="gender" type="radio" value="male" />
374
+ #
375
+ # radio_button_tag 'receive_updates', 'no', true
376
+ # # => <input checked="checked" id="receive_updates_no" name="receive_updates" type="radio" value="no" />
377
+ #
378
+ # radio_button_tag 'time_slot', "3:00 p.m.", false, disabled: true
379
+ # # => <input disabled="disabled" id="time_slot_300_pm" name="time_slot" type="radio" value="3:00 p.m." />
380
+ #
381
+ # radio_button_tag 'color', "green", true, class: "color_input"
382
+ # # => <input checked="checked" class="color_input" id="color_green" name="color" type="radio" value="green" />
383
+ def radio_button_tag(name, value, checked = false, options = {})
384
+ html_options = { "type" => "radio", "name" => name, "id" => "#{sanitize_to_id(name)}_#{sanitize_to_id(value)}", "value" => value }.update(options.stringify_keys)
385
+ html_options["checked"] = "checked" if checked
386
+ tag :input, html_options
387
+ end
388
+
389
+ # Creates a submit button with the text <tt>value</tt> as the caption.
390
+ #
391
+ # ==== Options
392
+ # * <tt>:data</tt> - This option can be used to add custom data attributes.
393
+ # * <tt>:disabled</tt> - If true, the user will not be able to use this input.
394
+ # * Any other key creates standard HTML options for the tag.
395
+ #
396
+ # ==== Data attributes
397
+ #
398
+ # * <tt>confirm: 'question?'</tt> - If present the unobtrusive JavaScript
399
+ # drivers will provide a prompt with the question specified. If the user accepts,
400
+ # the form is processed normally, otherwise no action is taken.
401
+ # * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a
402
+ # disabled version of the submit button when the form is submitted. This feature is
403
+ # provided by the unobtrusive JavaScript driver.
404
+ #
405
+ # ==== Examples
406
+ # submit_tag
407
+ # # => <input name="commit" type="submit" value="Save changes" />
408
+ #
409
+ # submit_tag "Edit this article"
410
+ # # => <input name="commit" type="submit" value="Edit this article" />
411
+ #
412
+ # submit_tag "Save edits", disabled: true
413
+ # # => <input disabled="disabled" name="commit" type="submit" value="Save edits" />
414
+ #
415
+ # submit_tag "Complete sale", data: { disable_with: "Please wait..." }
416
+ # # => <input name="commit" data-disable-with="Please wait..." type="submit" value="Complete sale" />
417
+ #
418
+ # submit_tag nil, class: "form_submit"
419
+ # # => <input class="form_submit" name="commit" type="submit" />
420
+ #
421
+ # submit_tag "Edit", class: "edit_button"
422
+ # # => <input class="edit_button" name="commit" type="submit" value="Edit" />
423
+ #
424
+ # submit_tag "Save", data: { confirm: "Are you sure?" }
425
+ # # => <input name='commit' type='submit' value='Save' data-confirm="Are you sure?" />
426
+ #
427
+ def submit_tag(value = "Save changes", options = {})
428
+ options = options.stringify_keys
429
+
430
+ tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options)
431
+ end
432
+
433
+ # Creates a button element that defines a <tt>submit</tt> button,
434
+ # <tt>reset</tt>button or a generic button which can be used in
435
+ # JavaScript, for example. You can use the button tag as a regular
436
+ # submit tag but it isn't supported in legacy browsers. However,
437
+ # the button tag allows richer labels such as images and emphasis,
438
+ # so this helper will also accept a block.
439
+ #
440
+ # ==== Options
441
+ # * <tt>:data</tt> - This option can be used to add custom data attributes.
442
+ # * <tt>:disabled</tt> - If true, the user will not be able to
443
+ # use this input.
444
+ # * Any other key creates standard HTML options for the tag.
445
+ #
446
+ # ==== Data attributes
447
+ #
448
+ # * <tt>confirm: 'question?'</tt> - If present, the
449
+ # unobtrusive JavaScript drivers will provide a prompt with
450
+ # the question specified. If the user accepts, the form is
451
+ # processed normally, otherwise no action is taken.
452
+ # * <tt>:disable_with</tt> - Value of this parameter will be
453
+ # used as the value for a disabled version of the submit
454
+ # button when the form is submitted. This feature is provided
455
+ # by the unobtrusive JavaScript driver.
456
+ #
457
+ # ==== Examples
458
+ # button_tag
459
+ # # => <button name="button" type="submit">Button</button>
460
+ #
461
+ # button_tag(type: 'button') do
462
+ # content_tag(:strong, 'Ask me!')
463
+ # end
464
+ # # => <button name="button" type="button">
465
+ # # <strong>Ask me!</strong>
466
+ # # </button>
467
+ #
468
+ # button_tag "Checkout", data: { :disable_with => "Please wait..." }
469
+ # # => <button data-disable-with="Please wait..." name="button" type="submit">Checkout</button>
470
+ #
471
+ def button_tag(content_or_options = nil, options = nil, &block)
472
+ options = content_or_options if block_given? && content_or_options.is_a?(Hash)
473
+ options ||= {}
474
+ options = options.stringify_keys
475
+
476
+ options.reverse_merge! 'name' => 'button', 'type' => 'submit'
477
+
478
+ content_tag :button, content_or_options || 'Button', options, &block
479
+ end
480
+
481
+ # Displays an image which when clicked will submit the form.
482
+ #
483
+ # <tt>source</tt> is passed to AssetTagHelper#path_to_image
484
+ #
485
+ # ==== Options
486
+ # * <tt>:data</tt> - This option can be used to add custom data attributes.
487
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
488
+ # * Any other key creates standard HTML options for the tag.
489
+ #
490
+ # ==== Data attributes
491
+ #
492
+ # * <tt>confirm: 'question?'</tt> - This will add a JavaScript confirm
493
+ # prompt with the question specified. If the user accepts, the form is
494
+ # processed normally, otherwise no action is taken.
495
+ #
496
+ # ==== Examples
497
+ # image_submit_tag("login.png")
498
+ # # => <input alt="Login" src="/images/login.png" type="image" />
499
+ #
500
+ # image_submit_tag("purchase.png", disabled: true)
501
+ # # => <input alt="Purchase" disabled="disabled" src="/images/purchase.png" type="image" />
502
+ #
503
+ # image_submit_tag("search.png", class: 'search_button', alt: 'Find')
504
+ # # => <input alt="Find" class="search_button" src="/images/search.png" type="image" />
505
+ #
506
+ # image_submit_tag("agree.png", disabled: true, class: "agree_disagree_button")
507
+ # # => <input alt="Agree" class="agree_disagree_button" disabled="disabled" src="/images/agree.png" type="image" />
508
+ #
509
+ # image_submit_tag("save.png", data: { confirm: "Are you sure?" })
510
+ # # => <input alt="Save" src="/images/save.png" data-confirm="Are you sure?" type="image" />
511
+ def image_submit_tag(source, options = {})
512
+ options = options.stringify_keys
513
+ tag :input, { "alt" => image_alt(source), "type" => "image", "src" => path_to_image(source) }.update(options)
514
+ end
515
+
516
+ # Creates a field set for grouping HTML form elements.
517
+ #
518
+ # <tt>legend</tt> will become the fieldset's title (optional as per W3C).
519
+ # <tt>options</tt> accept the same values as tag.
520
+ #
521
+ # ==== Examples
522
+ # <%= field_set_tag do %>
523
+ # <p><%= text_field_tag 'name' %></p>
524
+ # <% end %>
525
+ # # => <fieldset><p><input id="name" name="name" type="text" /></p></fieldset>
526
+ #
527
+ # <%= field_set_tag 'Your details' do %>
528
+ # <p><%= text_field_tag 'name' %></p>
529
+ # <% end %>
530
+ # # => <fieldset><legend>Your details</legend><p><input id="name" name="name" type="text" /></p></fieldset>
531
+ #
532
+ # <%= field_set_tag nil, class: 'format' do %>
533
+ # <p><%= text_field_tag 'name' %></p>
534
+ # <% end %>
535
+ # # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
536
+ def field_set_tag(legend = nil, options = nil, &block)
537
+ output = tag(:fieldset, options, true)
538
+ output.safe_concat(content_tag(:legend, legend)) unless legend.blank?
539
+ output.concat(capture(&block)) if block_given?
540
+ output.safe_concat("</fieldset>")
541
+ end
542
+
543
+ # Creates a text field of type "color".
544
+ #
545
+ # ==== Options
546
+ # * Accepts the same options as text_field_tag.
547
+ def color_field_tag(name, value = nil, options = {})
548
+ text_field_tag(name, value, options.stringify_keys.update("type" => "color"))
549
+ end
550
+
551
+ # Creates a text field of type "search".
552
+ #
553
+ # ==== Options
554
+ # * Accepts the same options as text_field_tag.
555
+ def search_field_tag(name, value = nil, options = {})
556
+ text_field_tag(name, value, options.stringify_keys.update("type" => "search"))
557
+ end
558
+
559
+ # Creates a text field of type "tel".
560
+ #
561
+ # ==== Options
562
+ # * Accepts the same options as text_field_tag.
563
+ def telephone_field_tag(name, value = nil, options = {})
564
+ text_field_tag(name, value, options.stringify_keys.update("type" => "tel"))
565
+ end
566
+ alias phone_field_tag telephone_field_tag
567
+
568
+ # Creates a text field of type "date".
569
+ #
570
+ # ==== Options
571
+ # * Accepts the same options as text_field_tag.
572
+ def date_field_tag(name, value = nil, options = {})
573
+ text_field_tag(name, value, options.stringify_keys.update("type" => "date"))
574
+ end
575
+
576
+ # Creates a text field of type "time".
577
+ #
578
+ # === Options
579
+ # * <tt>:min</tt> - The minimum acceptable value.
580
+ # * <tt>:max</tt> - The maximum acceptable value.
581
+ # * <tt>:step</tt> - The acceptable value granularity.
582
+ # * Otherwise accepts the same options as text_field_tag.
583
+ def time_field_tag(name, value = nil, options = {})
584
+ text_field_tag(name, value, options.stringify_keys.update("type" => "time"))
585
+ end
586
+
587
+ # Creates a text field of type "datetime".
588
+ #
589
+ # === Options
590
+ # * <tt>:min</tt> - The minimum acceptable value.
591
+ # * <tt>:max</tt> - The maximum acceptable value.
592
+ # * <tt>:step</tt> - The acceptable value granularity.
593
+ # * Otherwise accepts the same options as text_field_tag.
594
+ def datetime_field_tag(name, value = nil, options = {})
595
+ text_field_tag(name, value, options.stringify_keys.update("type" => "datetime"))
596
+ end
597
+
598
+ # Creates a text field of type "datetime-local".
599
+ #
600
+ # === Options
601
+ # * <tt>:min</tt> - The minimum acceptable value.
602
+ # * <tt>:max</tt> - The maximum acceptable value.
603
+ # * <tt>:step</tt> - The acceptable value granularity.
604
+ # * Otherwise accepts the same options as text_field_tag.
605
+ def datetime_local_field_tag(name, value = nil, options = {})
606
+ text_field_tag(name, value, options.stringify_keys.update("type" => "datetime-local"))
607
+ end
608
+
609
+ # Creates a text field of type "month".
610
+ #
611
+ # === Options
612
+ # * <tt>:min</tt> - The minimum acceptable value.
613
+ # * <tt>:max</tt> - The maximum acceptable value.
614
+ # * <tt>:step</tt> - The acceptable value granularity.
615
+ # * Otherwise accepts the same options as text_field_tag.
616
+ def month_field_tag(name, value = nil, options = {})
617
+ text_field_tag(name, value, options.stringify_keys.update("type" => "month"))
618
+ end
619
+
620
+ # Creates a text field of type "week".
621
+ #
622
+ # === Options
623
+ # * <tt>:min</tt> - The minimum acceptable value.
624
+ # * <tt>:max</tt> - The maximum acceptable value.
625
+ # * <tt>:step</tt> - The acceptable value granularity.
626
+ # * Otherwise accepts the same options as text_field_tag.
627
+ def week_field_tag(name, value = nil, options = {})
628
+ text_field_tag(name, value, options.stringify_keys.update("type" => "week"))
629
+ end
630
+
631
+ # Creates a text field of type "url".
632
+ #
633
+ # ==== Options
634
+ # * Accepts the same options as text_field_tag.
635
+ def url_field_tag(name, value = nil, options = {})
636
+ text_field_tag(name, value, options.stringify_keys.update("type" => "url"))
637
+ end
638
+
639
+ # Creates a text field of type "email".
640
+ #
641
+ # ==== Options
642
+ # * Accepts the same options as text_field_tag.
643
+ def email_field_tag(name, value = nil, options = {})
644
+ text_field_tag(name, value, options.stringify_keys.update("type" => "email"))
645
+ end
646
+
647
+ # Creates a number field.
648
+ #
649
+ # ==== Options
650
+ # * <tt>:min</tt> - The minimum acceptable value.
651
+ # * <tt>:max</tt> - The maximum acceptable value.
652
+ # * <tt>:in</tt> - A range specifying the <tt>:min</tt> and
653
+ # <tt>:max</tt> values.
654
+ # * <tt>:step</tt> - The acceptable value granularity.
655
+ # * Otherwise accepts the same options as text_field_tag.
656
+ #
657
+ # ==== Examples
658
+ # number_field_tag 'quantity', nil, in: 1...10
659
+ # # => <input id="quantity" name="quantity" min="1" max="9" type="number" />
660
+ def number_field_tag(name, value = nil, options = {})
661
+ options = options.stringify_keys
662
+ options["type"] ||= "number"
663
+ if range = options.delete("in") || options.delete("within")
664
+ options.update("min" => range.min, "max" => range.max)
665
+ end
666
+ text_field_tag(name, value, options)
667
+ end
668
+
669
+ # Creates a range form element.
670
+ #
671
+ # ==== Options
672
+ # * Accepts the same options as number_field_tag.
673
+ def range_field_tag(name, value = nil, options = {})
674
+ number_field_tag(name, value, options.stringify_keys.update("type" => "range"))
675
+ end
676
+
677
+ # Creates the hidden UTF8 enforcer tag. Override this method in a helper
678
+ # to customize the tag.
679
+ def utf8_enforcer_tag
680
+ tag(:input, :type => "hidden", :name => "utf8", :value => "&#x2713;".html_safe)
681
+ end
682
+
683
+ private
684
+ def html_options_for_form(url_for_options, options)
685
+ options.stringify_keys.tap do |html_options|
686
+ html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart")
687
+ # The following URL is unescaped, this is just a hash of options, and it is the
688
+ # responsibility of the caller to escape all the values.
689
+ html_options["action"] = url_for(url_for_options)
690
+ html_options["accept-charset"] = "UTF-8"
691
+
692
+ html_options["data-remote"] = true if html_options.delete("remote")
693
+
694
+ if html_options["data-remote"] &&
695
+ !embed_authenticity_token_in_remote_forms &&
696
+ html_options["authenticity_token"].blank?
697
+ # The authenticity token is taken from the meta tag in this case
698
+ html_options["authenticity_token"] = false
699
+ elsif html_options["authenticity_token"] == true
700
+ # Include the default authenticity_token, which is only generated when its set to nil,
701
+ # but we needed the true value to override the default of no authenticity_token on data-remote.
702
+ html_options["authenticity_token"] = nil
703
+ end
704
+ end
705
+ end
706
+
707
+ def extra_tags_for_form(html_options)
708
+ authenticity_token = html_options.delete("authenticity_token")
709
+ method = html_options.delete("method").to_s
710
+
711
+ method_tag = case method
712
+ when /^get$/i # must be case-insensitive, but can't use downcase as might be nil
713
+ html_options["method"] = "get"
714
+ ''
715
+ when /^post$/i, "", nil
716
+ html_options["method"] = "post"
717
+ token_tag(authenticity_token)
718
+ else
719
+ html_options["method"] = "post"
720
+ method_tag(method) + token_tag(authenticity_token)
721
+ end
722
+
723
+ enforce_utf8 = html_options.delete("enforce_utf8") { true }
724
+ tags = (enforce_utf8 ? utf8_enforcer_tag : ''.html_safe) << method_tag
725
+ content_tag(:div, tags, :style => 'margin:0;padding:0;display:inline')
726
+ end
727
+
728
+ def form_tag_html(html_options)
729
+ extra_tags = extra_tags_for_form(html_options)
730
+ tag(:form, html_options, true) + extra_tags
731
+ end
732
+
733
+ def form_tag_in_block(html_options, &block)
734
+ content = capture(&block)
735
+ output = form_tag_html(html_options)
736
+ output << content
737
+ output.safe_concat("</form>")
738
+ end
739
+
740
+ # see http://www.w3.org/TR/html4/types.html#type-name
741
+ def sanitize_to_id(name)
742
+ name.to_s.delete(']').gsub(/[^-a-zA-Z0-9:.]/, "_")
743
+ end
744
+ end
745
+ end
746
+ end