actionview 6.0.0

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