actionview 5.2.3

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