actionview 5.0.7.2 → 5.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionview might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +92 -384
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/action_view.rb +5 -5
- data/lib/action_view/base.rb +19 -19
- data/lib/action_view/buffers.rb +1 -1
- data/lib/action_view/context.rb +1 -1
- data/lib/action_view/dependency_tracker.rb +4 -5
- data/lib/action_view/digestor.rb +6 -7
- data/lib/action_view/flows.rb +5 -6
- data/lib/action_view/gem_version.rb +3 -3
- data/lib/action_view/helpers.rb +1 -1
- data/lib/action_view/helpers/active_model_helper.rb +8 -8
- data/lib/action_view/helpers/asset_tag_helper.rb +62 -36
- data/lib/action_view/helpers/asset_url_helper.rb +111 -49
- data/lib/action_view/helpers/atom_feed_helper.rb +12 -13
- data/lib/action_view/helpers/cache_helper.rb +34 -20
- data/lib/action_view/helpers/capture_helper.rb +2 -2
- data/lib/action_view/helpers/controller_helper.rb +3 -11
- data/lib/action_view/helpers/csrf_helper.rb +3 -3
- data/lib/action_view/helpers/date_helper.rb +109 -107
- data/lib/action_view/helpers/debug_helper.rb +2 -3
- data/lib/action_view/helpers/form_helper.rb +406 -31
- data/lib/action_view/helpers/form_options_helper.rb +12 -12
- data/lib/action_view/helpers/form_tag_helper.rb +19 -18
- data/lib/action_view/helpers/javascript_helper.rb +6 -6
- data/lib/action_view/helpers/number_helper.rb +48 -46
- data/lib/action_view/helpers/output_safety_helper.rb +8 -8
- data/lib/action_view/helpers/rendering_helper.rb +2 -2
- data/lib/action_view/helpers/sanitize_helper.rb +6 -8
- data/lib/action_view/helpers/tag_helper.rb +194 -77
- data/lib/action_view/helpers/tags/base.rb +121 -102
- data/lib/action_view/helpers/tags/check_box.rb +17 -17
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +8 -8
- data/lib/action_view/helpers/tags/collection_helpers.rb +60 -60
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +2 -2
- data/lib/action_view/helpers/tags/collection_select.rb +2 -2
- data/lib/action_view/helpers/tags/date_select.rb +36 -36
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +2 -2
- data/lib/action_view/helpers/tags/label.rb +4 -0
- data/lib/action_view/helpers/tags/password_field.rb +1 -1
- data/lib/action_view/helpers/tags/radio_button.rb +4 -4
- data/lib/action_view/helpers/tags/select.rb +9 -9
- data/lib/action_view/helpers/tags/text_area.rb +1 -1
- data/lib/action_view/helpers/tags/text_field.rb +5 -5
- data/lib/action_view/helpers/tags/translator.rb +14 -12
- data/lib/action_view/helpers/text_helper.rb +20 -19
- data/lib/action_view/helpers/translation_helper.rb +6 -6
- data/lib/action_view/helpers/url_helper.rb +42 -38
- data/lib/action_view/layouts.rb +51 -47
- data/lib/action_view/log_subscriber.rb +24 -9
- data/lib/action_view/lookup_context.rb +19 -25
- data/lib/action_view/path_set.rb +19 -19
- data/lib/action_view/railtie.rb +3 -3
- data/lib/action_view/record_identifier.rb +6 -6
- data/lib/action_view/renderer/abstract_renderer.rb +17 -17
- data/lib/action_view/renderer/partial_renderer.rb +188 -187
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +7 -1
- data/lib/action_view/renderer/streaming_template_renderer.rb +45 -47
- data/lib/action_view/renderer/template_renderer.rb +64 -66
- data/lib/action_view/rendering.rb +4 -5
- data/lib/action_view/routing_url_for.rb +9 -13
- data/lib/action_view/tasks/cache_digests.rake +7 -7
- data/lib/action_view/template.rb +26 -27
- data/lib/action_view/template/error.rb +5 -15
- data/lib/action_view/template/handlers.rb +4 -4
- data/lib/action_view/template/handlers/builder.rb +7 -7
- data/lib/action_view/template/handlers/erb.rb +9 -76
- data/lib/action_view/template/handlers/erb/deprecated_erubis.rb +9 -0
- data/lib/action_view/template/handlers/erb/erubi.rb +81 -0
- data/lib/action_view/template/handlers/erb/erubis.rb +81 -0
- data/lib/action_view/template/html.rb +2 -4
- data/lib/action_view/template/resolver.rb +107 -90
- data/lib/action_view/template/text.rb +5 -8
- data/lib/action_view/template/types.rb +1 -1
- data/lib/action_view/test_case.rb +20 -21
- data/lib/action_view/testing/resolvers.rb +29 -30
- data/lib/action_view/version.rb +1 -1
- data/lib/action_view/view_paths.rb +20 -8
- data/lib/assets/compiled/rails-ujs.js +648 -0
- metadata +19 -14
@@ -1,9 +1,9 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
1
|
+
require "cgi"
|
2
|
+
require "erb"
|
3
|
+
require "action_view/helpers/form_helper"
|
4
|
+
require "active_support/core_ext/string/output_safety"
|
5
|
+
require "active_support/core_ext/array/extract_options"
|
6
|
+
require "active_support/core_ext/array/wrap"
|
7
7
|
|
8
8
|
module ActionView
|
9
9
|
# = Action View Form Option Helpers
|
@@ -363,7 +363,7 @@ module ActionView
|
|
363
363
|
html_attributes[:disabled] ||= disabled && option_value_selected?(value, disabled)
|
364
364
|
html_attributes[:value] = value
|
365
365
|
|
366
|
-
content_tag_string(:option, text, html_attributes)
|
366
|
+
tag_builder.content_tag_string(:option, text, html_attributes)
|
367
367
|
end.join("\n").html_safe
|
368
368
|
end
|
369
369
|
|
@@ -578,7 +578,7 @@ module ActionView
|
|
578
578
|
end
|
579
579
|
|
580
580
|
zone_options.safe_concat options_for_select(convert_zones[priority_zones], selected)
|
581
|
-
zone_options.safe_concat content_tag("option".freeze,
|
581
|
+
zone_options.safe_concat content_tag("option".freeze, "-------------", value: "", disabled: true)
|
582
582
|
zone_options.safe_concat "\n"
|
583
583
|
|
584
584
|
zones = zones - priority_zones
|
@@ -651,12 +651,12 @@ module ActionView
|
|
651
651
|
# The HTML specification says when nothing is select on a collection of radio buttons
|
652
652
|
# web browsers do not send any value to server.
|
653
653
|
# Unfortunately this introduces a gotcha:
|
654
|
-
# if a +User+ model has a +category_id+ field
|
655
|
-
# any strong parameters idiom like
|
654
|
+
# if a +User+ model has a +category_id+ field and in the form no category is selected, no +category_id+ parameter is sent. So,
|
655
|
+
# any strong parameters idiom like:
|
656
656
|
#
|
657
657
|
# params.require(:user).permit(...)
|
658
658
|
#
|
659
|
-
# will raise an error since no
|
659
|
+
# will raise an error since no <tt>{user: ...}</tt> will be present.
|
660
660
|
#
|
661
661
|
# To prevent this the helper generates an auxiliary hidden field before
|
662
662
|
# every collection of radio buttons. The hidden field has the same name as collection radio button and blank value.
|
@@ -800,7 +800,7 @@ module ActionView
|
|
800
800
|
end
|
801
801
|
|
802
802
|
def prompt_text(prompt)
|
803
|
-
prompt.kind_of?(String) ? prompt : I18n.translate(
|
803
|
+
prompt.kind_of?(String) ? prompt : I18n.translate("helpers.select.prompt", default: "Please select")
|
804
804
|
end
|
805
805
|
end
|
806
806
|
|
@@ -1,7 +1,7 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
1
|
+
require "cgi"
|
2
|
+
require "action_view/helpers/tag_helper"
|
3
|
+
require "active_support/core_ext/string/output_safety"
|
4
|
+
require "active_support/core_ext/module/attribute_accessors"
|
5
5
|
|
6
6
|
module ActionView
|
7
7
|
# = Action View Form Tag Helpers
|
@@ -134,11 +134,11 @@ module ActionView
|
|
134
134
|
|
135
135
|
if options.include?(:include_blank)
|
136
136
|
include_blank = options.delete(:include_blank)
|
137
|
-
options_for_blank_options_tag = { value:
|
137
|
+
options_for_blank_options_tag = { value: "" }
|
138
138
|
|
139
139
|
if include_blank == true
|
140
|
-
include_blank =
|
141
|
-
options_for_blank_options_tag[:label] =
|
140
|
+
include_blank = ""
|
141
|
+
options_for_blank_options_tag[:label] = " "
|
142
142
|
end
|
143
143
|
|
144
144
|
if include_blank
|
@@ -147,7 +147,7 @@ module ActionView
|
|
147
147
|
end
|
148
148
|
|
149
149
|
if prompt = options.delete(:prompt)
|
150
|
-
option_tags = content_tag("option".freeze, prompt, value:
|
150
|
+
option_tags = content_tag("option".freeze, prompt, value: "").safe_concat(option_tags)
|
151
151
|
end
|
152
152
|
|
153
153
|
content_tag "select".freeze, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
|
@@ -449,7 +449,7 @@ module ActionView
|
|
449
449
|
end
|
450
450
|
|
451
451
|
# Creates a button element that defines a <tt>submit</tt> button,
|
452
|
-
# <tt>reset</tt>button or a generic button which can be used in
|
452
|
+
# <tt>reset</tt> button or a generic button which can be used in
|
453
453
|
# JavaScript, for example. You can use the button tag as a regular
|
454
454
|
# submit tag but it isn't supported in legacy browsers. However,
|
455
455
|
# the button tag does allow for richer labels such as images and emphasis,
|
@@ -506,12 +506,12 @@ module ActionView
|
|
506
506
|
options ||= {}
|
507
507
|
end
|
508
508
|
|
509
|
-
options = {
|
509
|
+
options = { "name" => "button", "type" => "submit" }.merge!(options.stringify_keys)
|
510
510
|
|
511
511
|
if block_given?
|
512
512
|
content_tag :button, options, &block
|
513
513
|
else
|
514
|
-
content_tag :button, content_or_options ||
|
514
|
+
content_tag :button, content_or_options || "Button", options
|
515
515
|
end
|
516
516
|
end
|
517
517
|
|
@@ -681,7 +681,7 @@ module ActionView
|
|
681
681
|
# * <tt>:step</tt> - The acceptable value granularity.
|
682
682
|
# * Otherwise accepts the same options as text_field_tag.
|
683
683
|
def datetime_field_tag(name, value = nil, options = {})
|
684
|
-
text_field_tag(name, value, options.merge(type:
|
684
|
+
text_field_tag(name, value, options.merge(type: "datetime-local"))
|
685
685
|
end
|
686
686
|
|
687
687
|
alias datetime_local_field_tag datetime_field_tag
|
@@ -845,11 +845,12 @@ module ActionView
|
|
845
845
|
authenticity_token = html_options.delete("authenticity_token")
|
846
846
|
method = html_options.delete("method").to_s.downcase
|
847
847
|
|
848
|
-
method_tag =
|
849
|
-
|
848
|
+
method_tag = \
|
849
|
+
case method
|
850
|
+
when "get"
|
850
851
|
html_options["method"] = "get"
|
851
|
-
|
852
|
-
when
|
852
|
+
""
|
853
|
+
when "post", ""
|
853
854
|
html_options["method"] = "post"
|
854
855
|
token_tag(authenticity_token, form_options: {
|
855
856
|
action: html_options["action"],
|
@@ -861,7 +862,7 @@ module ActionView
|
|
861
862
|
action: html_options["action"],
|
862
863
|
method: method
|
863
864
|
})
|
864
|
-
|
865
|
+
end
|
865
866
|
|
866
867
|
if html_options.delete("enforce_utf8") { true }
|
867
868
|
utf8_enforcer_tag + method_tag
|
@@ -883,7 +884,7 @@ module ActionView
|
|
883
884
|
|
884
885
|
# see http://www.w3.org/TR/html4/types.html#type-name
|
885
886
|
def sanitize_to_id(name)
|
886
|
-
name.to_s.delete(
|
887
|
+
name.to_s.delete("]").tr("^-a-zA-Z0-9:.", "_")
|
887
888
|
end
|
888
889
|
|
889
890
|
def set_default_disable_with(value, tag_options)
|
@@ -1,11 +1,11 @@
|
|
1
|
-
require
|
1
|
+
require "action_view/helpers/tag_helper"
|
2
2
|
|
3
3
|
module ActionView
|
4
4
|
module Helpers
|
5
5
|
module JavaScriptHelper
|
6
6
|
JS_ESCAPE_MAP = {
|
7
7
|
'\\' => '\\\\',
|
8
|
-
|
8
|
+
"</" => '<\/',
|
9
9
|
"\r\n" => '\n',
|
10
10
|
"\n" => '\n',
|
11
11
|
"\r" => '\n',
|
@@ -13,8 +13,8 @@ module ActionView
|
|
13
13
|
"'" => "\\'"
|
14
14
|
}
|
15
15
|
|
16
|
-
JS_ESCAPE_MAP["\342\200\250".force_encoding(Encoding::UTF_8).encode!] =
|
17
|
-
JS_ESCAPE_MAP["\342\200\251".force_encoding(Encoding::UTF_8).encode!] =
|
16
|
+
JS_ESCAPE_MAP["\342\200\250".force_encoding(Encoding::UTF_8).encode!] = "
"
|
17
|
+
JS_ESCAPE_MAP["\342\200\251".force_encoding(Encoding::UTF_8).encode!] = "
"
|
18
18
|
|
19
19
|
# Escapes carriage returns and single and double quotes for JavaScript segments.
|
20
20
|
#
|
@@ -24,10 +24,10 @@ module ActionView
|
|
24
24
|
# $('some_element').replaceWith('<%= j render 'some/element_template' %>');
|
25
25
|
def escape_javascript(javascript)
|
26
26
|
if javascript
|
27
|
-
result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"'])/u) {|match| JS_ESCAPE_MAP[match] }
|
27
|
+
result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"'])/u) { |match| JS_ESCAPE_MAP[match] }
|
28
28
|
javascript.html_safe? ? result.html_safe : result
|
29
29
|
else
|
30
|
-
|
30
|
+
""
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
@@ -1,11 +1,10 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require "active_support/core_ext/hash/keys"
|
2
|
+
require "active_support/core_ext/string/output_safety"
|
3
|
+
require "active_support/number_helper"
|
4
4
|
|
5
5
|
module ActionView
|
6
6
|
# = Action View Number Helpers
|
7
7
|
module Helpers #:nodoc:
|
8
|
-
|
9
8
|
# Provides methods for converting numbers into formatted strings.
|
10
9
|
# Methods are provided for phone numbers, currency, percentage,
|
11
10
|
# precision, positional notation, file size and pretty printing.
|
@@ -13,7 +12,6 @@ module ActionView
|
|
13
12
|
# Most methods expect a +number+ argument, and will return it
|
14
13
|
# unchanged if can't be converted into a valid number.
|
15
14
|
module NumberHelper
|
16
|
-
|
17
15
|
# Raised when argument +number+ param given to the helpers is invalid and
|
18
16
|
# the option :raise is set to +true+.
|
19
17
|
class InvalidNumberError < StandardError
|
@@ -94,7 +92,7 @@ module ActionView
|
|
94
92
|
# (defaults to "%u%n"). Fields are <tt>%u</tt> for the
|
95
93
|
# currency, and <tt>%n</tt> for the number.
|
96
94
|
# * <tt>:negative_format</tt> - Sets the format for negative
|
97
|
-
# numbers (defaults to prepending
|
95
|
+
# numbers (defaults to prepending a hyphen to the formatted
|
98
96
|
# number given by <tt>:format</tt>). Accepts the same fields
|
99
97
|
# than <tt>:format</tt>, except <tt>%n</tt> is here the
|
100
98
|
# absolute value of the number.
|
@@ -173,6 +171,9 @@ module ActionView
|
|
173
171
|
# to ",").
|
174
172
|
# * <tt>:separator</tt> - Sets the separator between the
|
175
173
|
# fractional and integer digits (defaults to ".").
|
174
|
+
# * <tt>:delimiter_pattern</tt> - Sets a custom regular expression used for
|
175
|
+
# deriving the placement of delimiter. Helpful when using currency formats
|
176
|
+
# like INR.
|
176
177
|
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
|
177
178
|
# the argument is invalid.
|
178
179
|
#
|
@@ -189,6 +190,9 @@ module ActionView
|
|
189
190
|
# number_with_delimiter(98765432.98, delimiter: " ", separator: ",")
|
190
191
|
# # => 98 765 432,98
|
191
192
|
#
|
193
|
+
# number_with_delimiter("123456.78",
|
194
|
+
# delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/) # => "1,23,456.78"
|
195
|
+
#
|
192
196
|
# number_with_delimiter("112a", raise: true) # => raise InvalidNumberError
|
193
197
|
def number_with_delimiter(number, options = {})
|
194
198
|
delegate_number_helper_method(:number_to_delimited, number, options)
|
@@ -263,8 +267,6 @@ module ActionView
|
|
263
267
|
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
|
264
268
|
# insignificant zeros after the decimal separator (defaults to
|
265
269
|
# +true+)
|
266
|
-
# * <tt>:prefix</tt> - If +:si+ formats the number using the SI
|
267
|
-
# prefix (defaults to :binary)
|
268
270
|
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
|
269
271
|
# the argument is invalid.
|
270
272
|
#
|
@@ -395,53 +397,53 @@ module ActionView
|
|
395
397
|
|
396
398
|
private
|
397
399
|
|
398
|
-
|
399
|
-
|
400
|
-
|
400
|
+
def delegate_number_helper_method(method, number, options)
|
401
|
+
return unless number
|
402
|
+
options = escape_unsafe_options(options.symbolize_keys)
|
401
403
|
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
404
|
+
wrap_with_output_safety_handling(number, options.delete(:raise)) {
|
405
|
+
ActiveSupport::NumberHelper.public_send(method, number, options)
|
406
|
+
}
|
407
|
+
end
|
406
408
|
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
409
|
+
def escape_unsafe_options(options)
|
410
|
+
options[:format] = ERB::Util.html_escape(options[:format]) if options[:format]
|
411
|
+
options[:negative_format] = ERB::Util.html_escape(options[:negative_format]) if options[:negative_format]
|
412
|
+
options[:separator] = ERB::Util.html_escape(options[:separator]) if options[:separator]
|
413
|
+
options[:delimiter] = ERB::Util.html_escape(options[:delimiter]) if options[:delimiter]
|
414
|
+
options[:unit] = ERB::Util.html_escape(options[:unit]) if options[:unit] && !options[:unit].html_safe?
|
415
|
+
options[:units] = escape_units(options[:units]) if options[:units] && Hash === options[:units]
|
416
|
+
options
|
417
|
+
end
|
416
418
|
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
419
|
+
def escape_units(units)
|
420
|
+
Hash[units.map do |k, v|
|
421
|
+
[k, ERB::Util.html_escape(v)]
|
422
|
+
end]
|
423
|
+
end
|
422
424
|
|
423
|
-
|
424
|
-
|
425
|
-
|
425
|
+
def wrap_with_output_safety_handling(number, raise_on_invalid, &block)
|
426
|
+
valid_float = valid_float?(number)
|
427
|
+
raise InvalidNumberError, number if raise_on_invalid && !valid_float
|
426
428
|
|
427
|
-
|
429
|
+
formatted_number = yield
|
428
430
|
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
431
|
+
if valid_float || number.html_safe?
|
432
|
+
formatted_number.html_safe
|
433
|
+
else
|
434
|
+
formatted_number
|
435
|
+
end
|
433
436
|
end
|
434
|
-
end
|
435
437
|
|
436
|
-
|
437
|
-
|
438
|
-
|
438
|
+
def valid_float?(number)
|
439
|
+
!parse_float(number, false).nil?
|
440
|
+
end
|
439
441
|
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
442
|
+
def parse_float(number, raise_error)
|
443
|
+
Float(number)
|
444
|
+
rescue ArgumentError, TypeError
|
445
|
+
raise InvalidNumberError, number if raise_error
|
446
|
+
end
|
445
447
|
end
|
446
448
|
end
|
447
449
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "active_support/core_ext/string/output_safety"
|
2
2
|
|
3
3
|
module ActionView #:nodoc:
|
4
4
|
# = Action View Raw Output Helper
|
@@ -25,10 +25,10 @@ module ActionView #:nodoc:
|
|
25
25
|
# safe_join([raw("<p>foo</p>"), "<p>bar</p>"], "<br />")
|
26
26
|
# # => "<p>foo</p><br /><p>bar</p>"
|
27
27
|
#
|
28
|
-
# safe_join([raw("<p>foo</p>"), raw("<p>bar</p>")], raw("<br />")
|
28
|
+
# safe_join([raw("<p>foo</p>"), raw("<p>bar</p>")], raw("<br />"))
|
29
29
|
# # => "<p>foo</p><br /><p>bar</p>"
|
30
30
|
#
|
31
|
-
def safe_join(array, sep
|
31
|
+
def safe_join(array, sep = $,)
|
32
32
|
sep = ERB::Util.unwrapped_html_escape(sep)
|
33
33
|
|
34
34
|
array.flatten.map! { |i| ERB::Util.unwrapped_html_escape(i) }.join(sep).html_safe
|
@@ -42,9 +42,9 @@ module ActionView #:nodoc:
|
|
42
42
|
options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
|
43
43
|
|
44
44
|
default_connectors = {
|
45
|
-
:
|
46
|
-
:
|
47
|
-
:
|
45
|
+
words_connector: ", ",
|
46
|
+
two_words_connector: " and ",
|
47
|
+
last_word_connector: ", and "
|
48
48
|
}
|
49
49
|
if defined?(I18n)
|
50
50
|
i18n_connectors = I18n.translate(:'support.array', locale: options[:locale], default: {})
|
@@ -54,13 +54,13 @@ module ActionView #:nodoc:
|
|
54
54
|
|
55
55
|
case array.length
|
56
56
|
when 0
|
57
|
-
|
57
|
+
"".html_safe
|
58
58
|
when 1
|
59
59
|
ERB::Util.html_escape(array[0])
|
60
60
|
when 2
|
61
61
|
safe_join([array[0], array[1]], options[:two_words_connector])
|
62
62
|
else
|
63
|
-
safe_join([safe_join(array[0...-1], options[:words_connector]), options[:last_word_connector], array[-1]])
|
63
|
+
safe_join([safe_join(array[0...-1], options[:words_connector]), options[:last_word_connector], array[-1]], nil)
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|
@@ -27,12 +27,12 @@ module ActionView
|
|
27
27
|
case options
|
28
28
|
when Hash
|
29
29
|
if block_given?
|
30
|
-
view_renderer.render_partial(self, options.merge(:
|
30
|
+
view_renderer.render_partial(self, options.merge(partial: options[:layout]), &block)
|
31
31
|
else
|
32
32
|
view_renderer.render(self, options)
|
33
33
|
end
|
34
34
|
else
|
35
|
-
view_renderer.render_partial(self, :
|
35
|
+
view_renderer.render_partial(self, partial: options, locals: locals, &block)
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "active_support/core_ext/object/try"
|
2
|
+
require "rails-html-sanitizer"
|
3
3
|
|
4
4
|
module ActionView
|
5
5
|
# = Action View Sanitize Helpers
|
@@ -45,17 +45,15 @@ module ActionView
|
|
45
45
|
# Providing a custom Rails::Html scrubber:
|
46
46
|
#
|
47
47
|
# class CommentScrubber < Rails::Html::PermitScrubber
|
48
|
-
# def
|
49
|
-
#
|
48
|
+
# def initialize
|
49
|
+
# super
|
50
|
+
# self.tags = %w( form script comment blockquote )
|
51
|
+
# self.attributes = %w( style )
|
50
52
|
# end
|
51
53
|
#
|
52
54
|
# def skip_node?(node)
|
53
55
|
# node.text?
|
54
56
|
# end
|
55
|
-
#
|
56
|
-
# def scrub_attribute?(name)
|
57
|
-
# name == 'style'
|
58
|
-
# end
|
59
57
|
# end
|
60
58
|
#
|
61
59
|
# <%= sanitize @comment.body, scrubber: CommentScrubber.new %>
|
@@ -1,39 +1,207 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/string/output_safety"
|
4
|
+
require "set"
|
3
5
|
|
4
6
|
module ActionView
|
5
7
|
# = Action View Tag Helpers
|
6
8
|
module Helpers #:nodoc:
|
7
|
-
# Provides methods to generate HTML tags programmatically
|
8
|
-
#
|
9
|
+
# Provides methods to generate HTML tags programmatically both as a modern
|
10
|
+
# HTML5 compliant builder style and legacy XHTML compliant tags.
|
9
11
|
module TagHelper
|
10
12
|
extend ActiveSupport::Concern
|
11
13
|
include CaptureHelper
|
12
14
|
include OutputSafetyHelper
|
13
15
|
|
14
|
-
BOOLEAN_ATTRIBUTES = %w(
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
BOOLEAN_ATTRIBUTES = %w(allowfullscreen async autofocus autoplay checked
|
17
|
+
compact controls declare default defaultchecked
|
18
|
+
defaultmuted defaultselected defer disabled
|
19
|
+
enabled formnovalidate hidden indeterminate inert
|
20
|
+
ismap itemscope loop multiple muted nohref
|
21
|
+
noresize noshade novalidate nowrap open
|
22
|
+
pauseonexit readonly required reversed scoped
|
23
|
+
seamless selected sortable truespeed typemustmatch
|
24
|
+
visible).to_set
|
20
25
|
|
21
26
|
BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map(&:to_sym))
|
22
27
|
|
23
|
-
TAG_PREFIXES = [
|
28
|
+
TAG_PREFIXES = ["aria", "data", :aria, :data].to_set
|
24
29
|
|
25
|
-
PRE_CONTENT_STRINGS = Hash.new { ""
|
30
|
+
PRE_CONTENT_STRINGS = Hash.new { "" }
|
26
31
|
PRE_CONTENT_STRINGS[:textarea] = "\n"
|
27
32
|
PRE_CONTENT_STRINGS["textarea"] = "\n"
|
28
33
|
|
34
|
+
class TagBuilder #:nodoc:
|
35
|
+
include CaptureHelper
|
36
|
+
include OutputSafetyHelper
|
37
|
+
|
38
|
+
VOID_ELEMENTS = %i(area base br col embed hr img input keygen link meta param source track wbr).to_set
|
39
|
+
|
40
|
+
def initialize(view_context)
|
41
|
+
@view_context = view_context
|
42
|
+
end
|
43
|
+
|
44
|
+
def tag_string(name, content = nil, escape_attributes: true, **options, &block)
|
45
|
+
content = @view_context.capture(self, &block) if block_given?
|
46
|
+
if VOID_ELEMENTS.include?(name) && content.nil?
|
47
|
+
"<#{name.to_s.dasherize}#{tag_options(options, escape_attributes)}>".html_safe
|
48
|
+
else
|
49
|
+
content_tag_string(name.to_s.dasherize, content || "", options, escape_attributes)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def content_tag_string(name, content, options, escape = true)
|
54
|
+
tag_options = tag_options(options, escape) if options
|
55
|
+
content = ERB::Util.unwrapped_html_escape(content) if escape
|
56
|
+
"<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name]}#{content}</#{name}>".html_safe
|
57
|
+
end
|
58
|
+
|
59
|
+
def tag_options(options, escape = true)
|
60
|
+
return if options.blank?
|
61
|
+
output = "".dup
|
62
|
+
sep = " "
|
63
|
+
options.each_pair do |key, value|
|
64
|
+
if TAG_PREFIXES.include?(key) && value.is_a?(Hash)
|
65
|
+
value.each_pair do |k, v|
|
66
|
+
next if v.nil?
|
67
|
+
output << sep
|
68
|
+
output << prefix_tag_option(key, k, v, escape)
|
69
|
+
end
|
70
|
+
elsif BOOLEAN_ATTRIBUTES.include?(key)
|
71
|
+
if value
|
72
|
+
output << sep
|
73
|
+
output << boolean_tag_option(key)
|
74
|
+
end
|
75
|
+
elsif !value.nil?
|
76
|
+
output << sep
|
77
|
+
output << tag_option(key, value, escape)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
output unless output.empty?
|
81
|
+
end
|
82
|
+
|
83
|
+
def boolean_tag_option(key)
|
84
|
+
%(#{key}="#{key}")
|
85
|
+
end
|
29
86
|
|
30
|
-
|
87
|
+
def tag_option(key, value, escape)
|
88
|
+
if value.is_a?(Array)
|
89
|
+
value = escape ? safe_join(value, " ".freeze) : value.join(" ".freeze)
|
90
|
+
else
|
91
|
+
value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s
|
92
|
+
end
|
93
|
+
%(#{key}="#{value.gsub('"'.freeze, '"'.freeze)}")
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
def prefix_tag_option(prefix, key, value, escape)
|
98
|
+
key = "#{prefix}-#{key.to_s.dasherize}"
|
99
|
+
unless value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(BigDecimal)
|
100
|
+
value = value.to_json
|
101
|
+
end
|
102
|
+
tag_option(key, value, escape)
|
103
|
+
end
|
104
|
+
|
105
|
+
def respond_to_missing?(*args)
|
106
|
+
true
|
107
|
+
end
|
108
|
+
|
109
|
+
def method_missing(called, *args, &block)
|
110
|
+
tag_string(called, *args, &block)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns an HTML tag.
|
115
|
+
#
|
116
|
+
# === Building HTML tags
|
117
|
+
#
|
118
|
+
# Builds HTML5 compliant tags with a tag proxy. Every tag can be built with:
|
119
|
+
#
|
120
|
+
# tag.<tag name>(optional content, options)
|
121
|
+
#
|
122
|
+
# where tag name can be e.g. br, div, section, article, or any tag really.
|
123
|
+
#
|
124
|
+
# ==== Passing content
|
125
|
+
#
|
126
|
+
# Tags can pass content to embed within it:
|
127
|
+
#
|
128
|
+
# tag.h1 'All titles fit to print' # => <h1>All titles fit to print</h1>
|
129
|
+
#
|
130
|
+
# tag.div tag.p('Hello world!') # => <div><p>Hello world!</p></div>
|
131
|
+
#
|
132
|
+
# Content can also be captured with a block, which is useful in templates:
|
133
|
+
#
|
134
|
+
# <%= tag.p do %>
|
135
|
+
# The next great American novel starts here.
|
136
|
+
# <% end %>
|
137
|
+
# # => <p>The next great American novel starts here.</p>
|
138
|
+
#
|
139
|
+
# ==== Options
|
140
|
+
#
|
141
|
+
# Use symbol keyed options to add attributes to the generated tag.
|
142
|
+
#
|
143
|
+
# tag.section class: %w( kitties puppies )
|
144
|
+
# # => <section class="kitties puppies"></section>
|
145
|
+
#
|
146
|
+
# tag.section id: dom_id(@post)
|
147
|
+
# # => <section id="<generated dom id>"></section>
|
148
|
+
#
|
149
|
+
# Pass +true+ for any attributes that can render with no values, like +disabled+ and +readonly+.
|
150
|
+
#
|
151
|
+
# tag.input type: 'text', disabled: true
|
152
|
+
# # => <input type="text" disabled="disabled">
|
153
|
+
#
|
154
|
+
# HTML5 <tt>data-*</tt> attributes can be set with a single +data+ key
|
155
|
+
# pointing to a hash of sub-attributes.
|
156
|
+
#
|
157
|
+
# To play nicely with JavaScript conventions, sub-attributes are dasherized.
|
158
|
+
#
|
159
|
+
# tag.article data: { user_id: 123 }
|
160
|
+
# # => <article data-user-id="123"></article>
|
161
|
+
#
|
162
|
+
# Thus <tt>data-user-id</tt> can be accessed as <tt>dataset.userId</tt>.
|
163
|
+
#
|
164
|
+
# Data attribute values are encoded to JSON, with the exception of strings, symbols and
|
165
|
+
# BigDecimals.
|
166
|
+
# This may come in handy when using jQuery's HTML5-aware <tt>.data()</tt>
|
167
|
+
# from 1.4.3.
|
168
|
+
#
|
169
|
+
# tag.div data: { city_state: %w( Chigaco IL ) }
|
170
|
+
# # => <div data-city-state="["Chicago","IL"]"></div>
|
171
|
+
#
|
172
|
+
# The generated attributes are escaped by default. This can be disabled using
|
173
|
+
# +escape_attributes+.
|
174
|
+
#
|
175
|
+
# tag.img src: 'open & shut.png'
|
176
|
+
# # => <img src="open & shut.png">
|
177
|
+
#
|
178
|
+
# tag.img src: 'open & shut.png', escape_attributes: false
|
179
|
+
# # => <img src="open & shut.png">
|
180
|
+
#
|
181
|
+
# The tag builder respects
|
182
|
+
# {HTML5 void elements}[https://www.w3.org/TR/html5/syntax.html#void-elements]
|
183
|
+
# if no content is passed, and omits closing tags for those elements.
|
184
|
+
#
|
185
|
+
# # A standard element:
|
186
|
+
# tag.div # => <div></div>
|
187
|
+
#
|
188
|
+
# # A void element:
|
189
|
+
# tag.br # => <br>
|
190
|
+
#
|
191
|
+
# === Legacy syntax
|
192
|
+
#
|
193
|
+
# The following format is for legacy syntax support. It will be deprecated in future versions of Rails.
|
194
|
+
#
|
195
|
+
# tag(name, options = nil, open = false, escape = true)
|
196
|
+
#
|
197
|
+
# It returns an empty HTML tag of type +name+ which by default is XHTML
|
31
198
|
# compliant. Set +open+ to true to create an open tag compatible
|
32
199
|
# with HTML 4.0 and below. Add HTML attributes by passing an attributes
|
33
200
|
# hash to +options+. Set +escape+ to false to disable attribute value
|
34
201
|
# escaping.
|
35
202
|
#
|
36
203
|
# ==== Options
|
204
|
+
#
|
37
205
|
# You can use symbols or strings for the attribute names.
|
38
206
|
#
|
39
207
|
# Use +true+ with boolean attributes that can render with no value, like
|
@@ -42,16 +210,8 @@ module ActionView
|
|
42
210
|
# HTML5 <tt>data-*</tt> attributes can be set with a single +data+ key
|
43
211
|
# pointing to a hash of sub-attributes.
|
44
212
|
#
|
45
|
-
# To play nicely with JavaScript conventions sub-attributes are dasherized.
|
46
|
-
# For example, a key +user_id+ would render as <tt>data-user-id</tt> and
|
47
|
-
# thus accessed as <tt>dataset.userId</tt>.
|
48
|
-
#
|
49
|
-
# Values are encoded to JSON, with the exception of strings, symbols and
|
50
|
-
# BigDecimals.
|
51
|
-
# This may come in handy when using jQuery's HTML5-aware <tt>.data()</tt>
|
52
|
-
# from 1.4.3.
|
53
|
-
#
|
54
213
|
# ==== Examples
|
214
|
+
#
|
55
215
|
# tag("br")
|
56
216
|
# # => <br />
|
57
217
|
#
|
@@ -72,8 +232,12 @@ module ActionView
|
|
72
232
|
#
|
73
233
|
# tag("div", data: {name: 'Stephen', city_state: %w(Chicago IL)})
|
74
234
|
# # => <div data-name="Stephen" data-city-state="["Chicago","IL"]" />
|
75
|
-
def tag(name, options = nil, open = false, escape = true)
|
76
|
-
|
235
|
+
def tag(name = nil, options = nil, open = false, escape = true)
|
236
|
+
if name.nil?
|
237
|
+
tag_builder
|
238
|
+
else
|
239
|
+
"<#{name}#{tag_builder.tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe
|
240
|
+
end
|
77
241
|
end
|
78
242
|
|
79
243
|
# Returns an HTML block tag of type +name+ surrounding the +content+. Add
|
@@ -81,6 +245,7 @@ module ActionView
|
|
81
245
|
# Instead of passing the content as an argument, you can also use a block
|
82
246
|
# in which case, you pass your +options+ as the second parameter.
|
83
247
|
# Set escape to false to disable attribute value escaping.
|
248
|
+
# Note: this is legacy syntax, see +tag+ method description for details.
|
84
249
|
#
|
85
250
|
# ==== Options
|
86
251
|
# The +options+ hash can be used with attributes with no value like (<tt>disabled</tt> and
|
@@ -104,9 +269,9 @@ module ActionView
|
|
104
269
|
def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
|
105
270
|
if block_given?
|
106
271
|
options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
|
107
|
-
content_tag_string(name, capture(&block), options, escape)
|
272
|
+
tag_builder.content_tag_string(name, capture(&block), options, escape)
|
108
273
|
else
|
109
|
-
content_tag_string(name, content_or_options_with_block, options, escape)
|
274
|
+
tag_builder.content_tag_string(name, content_or_options_with_block, options, escape)
|
110
275
|
end
|
111
276
|
end
|
112
277
|
|
@@ -124,7 +289,7 @@ module ActionView
|
|
124
289
|
# cdata_section("hello]]>world")
|
125
290
|
# # => <![CDATA[hello]]]]><![CDATA[>world]]>
|
126
291
|
def cdata_section(content)
|
127
|
-
splitted = content.to_s.gsub(/\]\]\>/,
|
292
|
+
splitted = content.to_s.gsub(/\]\]\>/, "]]]]><![CDATA[>")
|
128
293
|
"<![CDATA[#{splitted}]]>".html_safe
|
129
294
|
end
|
130
295
|
|
@@ -140,56 +305,8 @@ module ActionView
|
|
140
305
|
end
|
141
306
|
|
142
307
|
private
|
143
|
-
|
144
|
-
|
145
|
-
tag_options = tag_options(options, escape) if options
|
146
|
-
content = ERB::Util.unwrapped_html_escape(content) if escape
|
147
|
-
"<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name]}#{content}</#{name}>".html_safe
|
148
|
-
end
|
149
|
-
|
150
|
-
def tag_options(options, escape = true)
|
151
|
-
return if options.blank?
|
152
|
-
output = ""
|
153
|
-
sep = " ".freeze
|
154
|
-
options.each_pair do |key, value|
|
155
|
-
if TAG_PREFIXES.include?(key) && value.is_a?(Hash)
|
156
|
-
value.each_pair do |k, v|
|
157
|
-
next if v.nil?
|
158
|
-
output << sep
|
159
|
-
output << prefix_tag_option(key, k, v, escape)
|
160
|
-
end
|
161
|
-
elsif BOOLEAN_ATTRIBUTES.include?(key)
|
162
|
-
if value
|
163
|
-
output << sep
|
164
|
-
output << boolean_tag_option(key)
|
165
|
-
end
|
166
|
-
elsif !value.nil?
|
167
|
-
output << sep
|
168
|
-
output << tag_option(key, value, escape)
|
169
|
-
end
|
170
|
-
end
|
171
|
-
output unless output.empty?
|
172
|
-
end
|
173
|
-
|
174
|
-
def prefix_tag_option(prefix, key, value, escape)
|
175
|
-
key = "#{prefix}-#{key.to_s.dasherize}"
|
176
|
-
unless value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(BigDecimal)
|
177
|
-
value = value.to_json
|
178
|
-
end
|
179
|
-
tag_option(key, value, escape)
|
180
|
-
end
|
181
|
-
|
182
|
-
def boolean_tag_option(key)
|
183
|
-
%(#{key}="#{key}")
|
184
|
-
end
|
185
|
-
|
186
|
-
def tag_option(key, value, escape)
|
187
|
-
if value.is_a?(Array)
|
188
|
-
value = escape ? safe_join(value, " ".freeze) : value.join(" ".freeze)
|
189
|
-
else
|
190
|
-
value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s
|
191
|
-
end
|
192
|
-
%(#{key}="#{value.gsub('"'.freeze, '"'.freeze)}")
|
308
|
+
def tag_builder
|
309
|
+
@tag_builder ||= TagBuilder.new(self)
|
193
310
|
end
|
194
311
|
end
|
195
312
|
end
|