omg-actionview 8.0.0.alpha1

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