actionview 5.2.8.1 → 6.0.0.beta1

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

Potentially problematic release.


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

Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +106 -162
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/action_view/buffers.rb +15 -0
  6. data/lib/action_view/context.rb +5 -4
  7. data/lib/action_view/digestor.rb +7 -6
  8. data/lib/action_view/gem_version.rb +4 -4
  9. data/lib/action_view/helpers/asset_tag_helper.rb +4 -27
  10. data/lib/action_view/helpers/asset_url_helper.rb +4 -3
  11. data/lib/action_view/helpers/cache_helper.rb +18 -10
  12. data/lib/action_view/helpers/capture_helper.rb +4 -0
  13. data/lib/action_view/helpers/csrf_helper.rb +1 -1
  14. data/lib/action_view/helpers/date_helper.rb +69 -25
  15. data/lib/action_view/helpers/form_helper.rb +240 -8
  16. data/lib/action_view/helpers/form_options_helper.rb +23 -15
  17. data/lib/action_view/helpers/form_tag_helper.rb +9 -9
  18. data/lib/action_view/helpers/javascript_helper.rb +10 -11
  19. data/lib/action_view/helpers/number_helper.rb +5 -0
  20. data/lib/action_view/helpers/sanitize_helper.rb +3 -3
  21. data/lib/action_view/helpers/tag_helper.rb +13 -43
  22. data/lib/action_view/helpers/tags/base.rb +8 -4
  23. data/lib/action_view/helpers/tags/color_field.rb +1 -1
  24. data/lib/action_view/helpers/tags/translator.rb +1 -6
  25. data/lib/action_view/helpers/text_helper.rb +3 -3
  26. data/lib/action_view/helpers/translation_helper.rb +11 -18
  27. data/lib/action_view/helpers/url_helper.rb +14 -14
  28. data/lib/action_view/helpers.rb +0 -2
  29. data/lib/action_view/log_subscriber.rb +6 -6
  30. data/lib/action_view/lookup_context.rb +4 -4
  31. data/lib/action_view/railtie.rb +18 -0
  32. data/lib/action_view/record_identifier.rb +2 -2
  33. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +40 -1
  34. data/lib/action_view/renderer/partial_renderer.rb +2 -2
  35. data/lib/action_view/renderer/streaming_template_renderer.rb +1 -1
  36. data/lib/action_view/rendering.rb +5 -4
  37. data/lib/action_view/routing_url_for.rb +12 -11
  38. data/lib/action_view/template/handlers/erb.rb +12 -2
  39. data/lib/action_view/template/resolver.rb +56 -16
  40. data/lib/action_view/template.rb +25 -8
  41. data/lib/action_view/test_case.rb +1 -1
  42. data/lib/action_view/testing/resolvers.rb +1 -1
  43. data/lib/action_view.rb +1 -1
  44. data/lib/assets/compiled/rails-ujs.js +39 -22
  45. metadata +17 -18
  46. data/lib/action_view/helpers/record_tag_helper.rb +0 -23
@@ -14,15 +14,35 @@ module ActionView
14
14
  def cache_collection_render(instrumentation_payload)
15
15
  return yield unless @options[:cached]
16
16
 
17
+ # Result is a hash with the key represents the
18
+ # key used for cache lookup and the value is the item
19
+ # on which the partial is being rendered
17
20
  keyed_collection = collection_by_cache_keys
21
+
22
+ # Pull all partials from cache
23
+ # Result is a hash, key matches the entry in
24
+ # `keyed_collection` where the cache was retrieved and the
25
+ # value is the value that was present in the cache
18
26
  cached_partials = collection_cache.read_multi(*keyed_collection.keys)
19
27
  instrumentation_payload[:cache_hits] = cached_partials.size
20
28
 
29
+ # Extract the items for the keys that are not found
30
+ # Set the uncached values to instance variable @collection
31
+ # which is used by the caller
21
32
  @collection = keyed_collection.reject { |key, _| cached_partials.key?(key) }.values
33
+
34
+ # If all elements are already in cache then
35
+ # rendered partials will be an empty array
36
+ #
37
+ # If the cache is missing elements then
38
+ # the block will be called against the remaining items
39
+ # in the @collection.
22
40
  rendered_partials = @collection.empty? ? [] : yield
23
41
 
24
42
  index = 0
25
43
  fetch_or_cache_partial(cached_partials, order_by: keyed_collection.each_key) do
44
+ # This block is called once
45
+ # for every cache miss while preserving order.
26
46
  rendered_partials[index].tap { index += 1 }
27
47
  end
28
48
  end
@@ -40,10 +60,29 @@ module ActionView
40
60
  end
41
61
 
42
62
  def expanded_cache_key(key)
43
- key = @view.combined_fragment_cache_key(@view.cache_fragment_name(key, virtual_path: @template.virtual_path))
63
+ key = @view.combined_fragment_cache_key(@view.cache_fragment_name(key, virtual_path: @template.virtual_path, digest_path: digest_path))
44
64
  key.frozen? ? key.dup : key # #read_multi & #write may require mutability, Dalli 2.6.0.
45
65
  end
46
66
 
67
+ def digest_path
68
+ @digest_path ||= @view.digest_path_from_virtual(@template.virtual_path)
69
+ end
70
+
71
+ # `order_by` is an enumerable object containing keys of the cache,
72
+ # all keys are passed in whether found already or not.
73
+ #
74
+ # `cached_partials` is a hash. If the value exists
75
+ # it represents the rendered partial from the cache
76
+ # otherwise `Hash#fetch` will take the value of its block.
77
+ #
78
+ # This method expects a block that will return the rendered
79
+ # partial. An example is to render all results
80
+ # for each element that was not found in the cache and store it as an array.
81
+ # Order it so that the first empty cache element in `cached_partials`
82
+ # corresponds to the first element in `rendered_partials`.
83
+ #
84
+ # If the partial is not already cached it will also be
85
+ # written back to the underlying cache store.
47
86
  def fetch_or_cache_partial(cached_partials, order_by:)
48
87
  order_by.map do |cache_key|
49
88
  cached_partials.fetch(cache_key) do
@@ -363,7 +363,7 @@ module ActionView
363
363
  @options = options
364
364
  @block = block
365
365
 
366
- @locals = options[:locals] || {}
366
+ @locals = options[:locals] ? options[:locals].symbolize_keys : {}
367
367
  @details = extract_details(options)
368
368
 
369
369
  prepend_formats(options[:formats])
@@ -523,7 +523,7 @@ module ActionView
523
523
 
524
524
  def retrieve_variable(path, as)
525
525
  variable = as || begin
526
- base = path[-1] == "/".freeze ? "".freeze : File.basename(path)
526
+ base = path[-1] == "/" ? "" : File.basename(path)
527
527
  raise_invalid_identifier(path) unless base =~ /\A_?(.*?)(?:\.\w+)*\z/
528
528
  $1.to_sym
529
529
  end
@@ -33,7 +33,7 @@ module ActionView
33
33
  logger = ActionView::Base.logger
34
34
  return unless logger
35
35
 
36
- message = "\n#{exception.class} (#{exception.message}):\n".dup
36
+ message = +"\n#{exception.class} (#{exception.message}):\n"
37
37
  message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
38
38
  message << " " << exception.backtrace.join("\n ")
39
39
  logger.fatal("#{message}\n\n")
@@ -64,10 +64,11 @@ module ActionView
64
64
  # An instance of a view class. The default view class is ActionView::Base.
65
65
  #
66
66
  # The view class must have the following methods:
67
- # View.new[lookup_context, assigns, controller]
68
- # Create a new ActionView instance for a controller and we can also pass the arguments.
69
- # View#render(option)
70
- # Returns String with the rendered template
67
+ #
68
+ # * <tt>View.new(lookup_context, assigns, controller)</tt> Create a new
69
+ # ActionView instance for a controller and we can also pass the arguments.
70
+ #
71
+ # * <tt>View#render(option)</tt> — Returns String with the rendered template.
71
72
  #
72
73
  # Override this method in a module to change the default behavior.
73
74
  def view_context
@@ -84,25 +84,24 @@ module ActionView
84
84
  super(only_path: _generate_paths_by_default)
85
85
  when Hash
86
86
  options = options.symbolize_keys
87
- unless options.key?(:only_path)
88
- options[:only_path] = only_path?(options[:host])
89
- end
87
+ ensure_only_path_option(options)
90
88
 
91
89
  super(options)
92
90
  when ActionController::Parameters
93
- unless options.key?(:only_path)
94
- options[:only_path] = only_path?(options[:host])
95
- end
91
+ ensure_only_path_option(options)
96
92
 
97
93
  super(options)
98
94
  when :back
99
95
  _back_url
100
96
  when Array
101
97
  components = options.dup
102
- if _generate_paths_by_default
103
- polymorphic_path(components, components.extract_options!)
98
+ options = components.extract_options!
99
+ ensure_only_path_option(options)
100
+
101
+ if options[:only_path]
102
+ polymorphic_path(components, options)
104
103
  else
105
- polymorphic_url(components, components.extract_options!)
104
+ polymorphic_url(components, options)
106
105
  end
107
106
  else
108
107
  method = _generate_paths_by_default ? :path : :url
@@ -138,8 +137,10 @@ module ActionView
138
137
  true
139
138
  end
140
139
 
141
- def only_path?(host)
142
- _generate_paths_by_default unless host
140
+ def ensure_only_path_option(options)
141
+ unless options.key?(:only_path)
142
+ options[:only_path] = _generate_paths_by_default unless options[:host]
143
+ end
143
144
  end
144
145
  end
145
146
  end
@@ -14,7 +14,17 @@ module ActionView
14
14
  class_attribute :erb_implementation, default: Erubi
15
15
 
16
16
  # Do not escape templates of these mime types.
17
- class_attribute :escape_whitelist, default: ["text/plain"]
17
+ class_attribute :escape_ignore_list, default: ["text/plain"]
18
+
19
+ [self, singleton_class].each do |base|
20
+ base.alias_method :escape_whitelist, :escape_ignore_list
21
+ base.alias_method :escape_whitelist=, :escape_ignore_list=
22
+
23
+ base.deprecate(
24
+ escape_whitelist: "use #escape_ignore_list instead",
25
+ :escape_whitelist= => "use #escape_ignore_list= instead"
26
+ )
27
+ end
18
28
 
19
29
  ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*")
20
30
 
@@ -47,7 +57,7 @@ module ActionView
47
57
 
48
58
  self.class.erb_implementation.new(
49
59
  erb,
50
- escape: (self.class.escape_whitelist.include? template.type),
60
+ escape: (self.class.escape_ignore_list.include? template.type),
51
61
  trim: (self.class.erb_trim_mode == "-")
52
62
  ).src
53
63
  end
@@ -16,7 +16,7 @@ module ActionView
16
16
  alias_method :partial?, :partial
17
17
 
18
18
  def self.build(name, prefix, partial)
19
- virtual = "".dup
19
+ virtual = +""
20
20
  virtual << "#{prefix}/" unless prefix.empty?
21
21
  virtual << (partial ? "_#{name}" : name)
22
22
  new name, prefix, partial, virtual
@@ -221,9 +221,7 @@ module ActionView
221
221
  end
222
222
 
223
223
  def query(path, details, formats, outside_app_allowed)
224
- query = build_query(path, details)
225
-
226
- template_paths = find_template_paths(query)
224
+ template_paths = find_template_paths_from_details(path, details)
227
225
  template_paths = reject_files_external_to_app(template_paths) unless outside_app_allowed
228
226
 
229
227
  template_paths.map do |template|
@@ -243,6 +241,11 @@ module ActionView
243
241
  files.reject { |filename| !inside_path?(@path, filename) }
244
242
  end
245
243
 
244
+ def find_template_paths_from_details(path, details)
245
+ query = build_query(path, details)
246
+ find_template_paths(query)
247
+ end
248
+
246
249
  def find_template_paths(query)
247
250
  Dir[query].uniq.reject do |filename|
248
251
  File.directory?(filename) ||
@@ -279,7 +282,7 @@ module ActionView
279
282
  end
280
283
 
281
284
  def escape_entry(entry)
282
- entry.gsub(/[*?{}\[\]]/, '\\\\\\&'.freeze)
285
+ entry.gsub(/[*?{}\[\]]/, '\\\\\\&')
283
286
  end
284
287
 
285
288
  # Returns the file mtime from the filesystem.
@@ -291,7 +294,7 @@ module ActionView
291
294
  # from the path, or the handler, we should return the array of formats given
292
295
  # to the resolver.
293
296
  def extract_handler_and_format_and_variant(path)
294
- pieces = File.basename(path).split(".".freeze)
297
+ pieces = File.basename(path).split(".")
295
298
  pieces.shift
296
299
 
297
300
  extension = pieces.pop
@@ -362,19 +365,56 @@ module ActionView
362
365
 
363
366
  # An Optimized resolver for Rails' most common case.
364
367
  class OptimizedFileSystemResolver < FileSystemResolver #:nodoc:
365
- def build_query(path, details)
366
- query = escape_entry(File.join(@path, path))
368
+ private
367
369
 
368
- exts = EXTENSIONS.map do |ext, prefix|
369
- if ext == :variants && details[ext] == :any
370
- "{#{prefix}*,}"
371
- else
372
- "{#{details[ext].compact.uniq.map { |e| "#{prefix}#{e}," }.join}}"
370
+ def find_template_paths_from_details(path, details)
371
+ # Instead of checking for every possible path, as our other globs would
372
+ # do, scan the directory for files with the right prefix.
373
+ query = "#{escape_entry(File.join(@path, path))}*"
374
+
375
+ regex = build_regex(path, details)
376
+
377
+ Dir[query].uniq.reject do |filename|
378
+ # This regex match does double duty of finding only files which match
379
+ # details (instead of just matching the prefix) and also filtering for
380
+ # case-insensitive file systems.
381
+ !regex.match?(filename) ||
382
+ File.directory?(filename)
383
+ end.sort_by do |filename|
384
+ # Because we scanned the directory, instead of checking for files
385
+ # one-by-one, they will be returned in an arbitrary order.
386
+ # We can use the matches found by the regex and sort by their index in
387
+ # details.
388
+ match = filename.match(regex)
389
+ EXTENSIONS.keys.reverse.map do |ext|
390
+ if ext == :variants && details[ext] == :any
391
+ match[ext].nil? ? 0 : 1
392
+ elsif match[ext].nil?
393
+ # No match should be last
394
+ details[ext].length
395
+ else
396
+ found = match[ext].to_sym
397
+ details[ext].index(found)
398
+ end
399
+ end
373
400
  end
374
- end.join
401
+ end
375
402
 
376
- query + exts
377
- end
403
+ def build_regex(path, details)
404
+ query = escape_entry(File.join(@path, path))
405
+ exts = EXTENSIONS.map do |ext, prefix|
406
+ match =
407
+ if ext == :variants && details[ext] == :any
408
+ ".*?"
409
+ else
410
+ details[ext].compact.uniq.map { |e| Regexp.escape(e) }.join("|")
411
+ end
412
+ prefix = Regexp.escape(prefix)
413
+ "(#{prefix}(?<#{ext}>#{match}))?"
414
+ end.join
415
+
416
+ %r{\A#{query}#{exts}\z}
417
+ end
378
418
  end
379
419
 
380
420
  # The same as FileSystemResolver but does not allow templates to store
@@ -9,6 +9,8 @@ module ActionView
9
9
  class Template
10
10
  extend ActiveSupport::Autoload
11
11
 
12
+ mattr_accessor :finalize_compiled_template_methods, default: true
13
+
12
14
  # === Encodings in ActionView::Template
13
15
  #
14
16
  # ActionView::Template is one of a few sources of potential
@@ -186,7 +188,7 @@ module ActionView
186
188
  end
187
189
 
188
190
  def inspect
189
- @inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", "".freeze) : identifier
191
+ @inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", "") : identifier
190
192
  end
191
193
 
192
194
  # This method is responsible for properly setting the encoding of the
@@ -233,6 +235,19 @@ module ActionView
233
235
  end
234
236
  end
235
237
 
238
+
239
+ # Exceptions are marshalled when using the parallel test runner with DRb, so we need
240
+ # to ensure that references to the template object can be marshalled as well. This means forgoing
241
+ # the marshalling of the compiler mutex and instantiating that again on unmarshalling.
242
+ def marshal_dump # :nodoc:
243
+ [ @source, @identifier, @handler, @compiled, @original_encoding, @locals, @virtual_path, @updated_at, @formats, @variants ]
244
+ end
245
+
246
+ def marshal_load(array) # :nodoc:
247
+ @source, @identifier, @handler, @compiled, @original_encoding, @locals, @virtual_path, @updated_at, @formats, @variants = *array
248
+ @compile_mutex = Mutex.new
249
+ end
250
+
236
251
  private
237
252
 
238
253
  # Compile a template. This method ensures a template is compiled
@@ -284,7 +299,7 @@ module ActionView
284
299
 
285
300
  # Make sure that the resulting String to be eval'd is in the
286
301
  # encoding of the code
287
- source = <<-end_src.dup
302
+ source = +<<-end_src
288
303
  def #{method_name}(local_assigns, output_buffer)
289
304
  _old_virtual_path, @virtual_path = @virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code}
290
305
  ensure
@@ -307,7 +322,9 @@ module ActionView
307
322
  end
308
323
 
309
324
  mod.module_eval(source, identifier, 0)
310
- ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
325
+ if finalize_compiled_template_methods
326
+ ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
327
+ end
311
328
  end
312
329
 
313
330
  def handle_render_error(view, e)
@@ -331,19 +348,19 @@ module ActionView
331
348
  locals = locals.grep(/\A@?(?![A-Z0-9])(?:[[:alnum:]_]|[^\0-\177])+\z/)
332
349
 
333
350
  # Assign for the same variable is to suppress unused variable warning
334
- locals.each_with_object("".dup) { |key, code| code << "#{key} = local_assigns[:#{key}]; #{key} = #{key};" }
351
+ locals.each_with_object(+"") { |key, code| code << "#{key} = local_assigns[:#{key}]; #{key} = #{key};" }
335
352
  end
336
353
 
337
354
  def method_name
338
355
  @method_name ||= begin
339
- m = "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}".dup
340
- m.tr!("-".freeze, "_".freeze)
356
+ m = +"_#{identifier_method_name}__#{@identifier.hash}_#{__id__}"
357
+ m.tr!("-", "_")
341
358
  m
342
359
  end
343
360
  end
344
361
 
345
362
  def identifier_method_name
346
- inspect.tr("^a-z_".freeze, "_".freeze)
363
+ inspect.tr("^a-z_", "_")
347
364
  end
348
365
 
349
366
  def instrument(action, &block) # :doc:
@@ -351,7 +368,7 @@ module ActionView
351
368
  end
352
369
 
353
370
  def instrument_render_template(&block)
354
- ActiveSupport::Notifications.instrument("!render_template.action_view".freeze, instrument_payload, &block)
371
+ ActiveSupport::Notifications.instrument("!render_template.action_view", instrument_payload, &block)
355
372
  end
356
373
 
357
374
  def instrument_payload
@@ -107,7 +107,7 @@ module ActionView
107
107
  # empty string ensures buffer has UTF-8 encoding as
108
108
  # new without arguments returns ASCII-8BIT encoded buffer like String#new
109
109
  @output_buffer = ActiveSupport::SafeBuffer.new ""
110
- @rendered = "".dup
110
+ @rendered = +""
111
111
 
112
112
  make_test_case_available_to_view!
113
113
  say_no_to_protect_against_forgery!
@@ -22,7 +22,7 @@ module ActionView #:nodoc:
22
22
  private
23
23
 
24
24
  def query(path, exts, _, _)
25
- query = "".dup
25
+ query = +""
26
26
  EXTENSIONS.each_key do |ext|
27
27
  query << "(" << exts[ext].map { |e| e && Regexp.escape(".#{e}") }.join("|") << "|)"
28
28
  end
data/lib/action_view.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright (c) 2004-2018 David Heinemeier Hansson
4
+ # Copyright (c) 2004-2019 David Heinemeier Hansson
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining
7
7
  # a copy of this software and associated documentation files (the
@@ -2,7 +2,7 @@
2
2
  Unobtrusive JavaScript
3
3
  https://github.com/rails/rails/blob/master/actionview/app/assets/javascripts
4
4
  Released under the MIT license
5
- */;
5
+ */
6
6
 
7
7
  (function() {
8
8
  var context = this;
@@ -32,17 +32,12 @@ Released under the MIT license
32
32
 
33
33
  (function() {
34
34
  (function() {
35
- var nonce;
35
+ var cspNonce;
36
36
 
37
- nonce = null;
38
-
39
- Rails.loadCSPNonce = function() {
40
- var ref;
41
- return nonce = (ref = document.querySelector("meta[name=csp-nonce]")) != null ? ref.content : void 0;
42
- };
43
-
44
- Rails.cspNonce = function() {
45
- return nonce != null ? nonce : Rails.loadCSPNonce();
37
+ cspNonce = Rails.cspNonce = function() {
38
+ var meta;
39
+ meta = document.querySelector('meta[name=csp-nonce]');
40
+ return meta && meta.content;
46
41
  };
47
42
 
48
43
  }).call(this);
@@ -247,8 +242,8 @@ Released under the MIT license
247
242
  }
248
243
  if (!options.crossDomain) {
249
244
  xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
250
- CSRFProtection(xhr);
251
245
  }
246
+ CSRFProtection(xhr);
252
247
  xhr.withCredentials = !!options.withCredentials;
253
248
  xhr.onreadystatechange = function() {
254
249
  if (xhr.readyState === XMLHttpRequest.DONE) {
@@ -270,7 +265,7 @@ Released under the MIT license
270
265
  script.setAttribute('nonce', cspNonce());
271
266
  script.text = response;
272
267
  document.head.appendChild(script).parentNode.removeChild(script);
273
- } else if (type.match(/\b(xml|html|svg)\b/)) {
268
+ } else if (type.match(/\bxml\b/)) {
274
269
  parser = new DOMParser();
275
270
  type = type.replace(/;.+/, '');
276
271
  try {
@@ -370,6 +365,10 @@ Released under the MIT license
370
365
  }
371
366
  };
372
367
 
368
+ Rails.confirm = function(message, element) {
369
+ return confirm(message);
370
+ };
371
+
373
372
  allowAction = function(element) {
374
373
  var answer, callback, message;
375
374
  message = element.getAttribute('data-confirm');
@@ -379,7 +378,7 @@ Released under the MIT license
379
378
  answer = false;
380
379
  if (fire(element, 'confirm')) {
381
380
  try {
382
- answer = confirm(message);
381
+ answer = Rails.confirm(message, element);
383
382
  } catch (error) {}
384
383
  callback = fire(element, 'confirm:complete', [answer]);
385
384
  }
@@ -388,7 +387,7 @@ Released under the MIT license
388
387
 
389
388
  }).call(this);
390
389
  (function() {
391
- var disableFormElement, disableFormElements, disableLinkElement, enableFormElement, enableFormElements, enableLinkElement, formElements, getData, matches, setData, stopEverything;
390
+ var disableFormElement, disableFormElements, disableLinkElement, enableFormElement, enableFormElements, enableLinkElement, formElements, getData, isXhrRedirect, matches, setData, stopEverything;
392
391
 
393
392
  matches = Rails.matches, getData = Rails.getData, setData = Rails.setData, stopEverything = Rails.stopEverything, formElements = Rails.formElements;
394
393
 
@@ -402,7 +401,14 @@ Released under the MIT license
402
401
 
403
402
  Rails.enableElement = function(e) {
404
403
  var element;
405
- element = e instanceof Event ? e.target : e;
404
+ if (e instanceof Event) {
405
+ if (isXhrRedirect(e)) {
406
+ return;
407
+ }
408
+ element = e.target;
409
+ } else {
410
+ element = e;
411
+ }
406
412
  if (matches(element, Rails.linkDisableSelector)) {
407
413
  return enableLinkElement(element);
408
414
  } else if (matches(element, Rails.buttonDisableSelector) || matches(element, Rails.formEnableSelector)) {
@@ -426,6 +432,9 @@ Released under the MIT license
426
432
 
427
433
  disableLinkElement = function(element) {
428
434
  var replacement;
435
+ if (getData(element, 'ujs:disabled')) {
436
+ return;
437
+ }
429
438
  replacement = element.getAttribute('data-disable-with');
430
439
  if (replacement != null) {
431
440
  setData(element, 'ujs:enable-with', element.innerHTML);
@@ -452,6 +461,9 @@ Released under the MIT license
452
461
 
453
462
  disableFormElement = function(element) {
454
463
  var replacement;
464
+ if (getData(element, 'ujs:disabled')) {
465
+ return;
466
+ }
455
467
  replacement = element.getAttribute('data-disable-with');
456
468
  if (replacement != null) {
457
469
  if (matches(element, 'button')) {
@@ -485,6 +497,12 @@ Released under the MIT license
485
497
  return setData(element, 'ujs:disabled', null);
486
498
  };
487
499
 
500
+ isXhrRedirect = function(event) {
501
+ var ref, xhr;
502
+ xhr = (ref = event.detail) != null ? ref[0] : void 0;
503
+ return (xhr != null ? xhr.getResponseHeader("X-Xhr-Redirect") : void 0) != null;
504
+ };
505
+
488
506
  }).call(this);
489
507
  (function() {
490
508
  var stopEverything;
@@ -622,23 +640,23 @@ Released under the MIT license
622
640
  };
623
641
 
624
642
  Rails.preventInsignificantClick = function(e) {
625
- var data, insignificantMetaClick, link, metaClick, method, nonPrimaryMouseClick;
643
+ var data, insignificantMetaClick, link, metaClick, method, primaryMouseKey;
626
644
  link = this;
627
645
  method = (link.getAttribute('data-method') || 'GET').toUpperCase();
628
646
  data = link.getAttribute('data-params');
629
647
  metaClick = e.metaKey || e.ctrlKey;
630
648
  insignificantMetaClick = metaClick && method === 'GET' && !data;
631
- nonPrimaryMouseClick = (e.button != null) && e.button !== 0;
632
- if (nonPrimaryMouseClick || insignificantMetaClick) {
649
+ primaryMouseKey = e.button === 0;
650
+ if (!primaryMouseKey || insignificantMetaClick) {
633
651
  return e.stopImmediatePropagation();
634
652
  }
635
653
  };
636
654
 
637
655
  }).call(this);
638
656
  (function() {
639
- var $, CSRFProtection, delegate, disableElement, enableElement, fire, formSubmitButtonClick, getData, handleConfirm, handleDisabledElement, handleMethod, handleRemote, loadCSPNonce, preventInsignificantClick, refreshCSRFTokens;
657
+ var $, CSRFProtection, delegate, disableElement, enableElement, fire, formSubmitButtonClick, getData, handleConfirm, handleDisabledElement, handleMethod, handleRemote, preventInsignificantClick, refreshCSRFTokens;
640
658
 
641
- fire = Rails.fire, delegate = Rails.delegate, getData = Rails.getData, $ = Rails.$, refreshCSRFTokens = Rails.refreshCSRFTokens, CSRFProtection = Rails.CSRFProtection, loadCSPNonce = Rails.loadCSPNonce, enableElement = Rails.enableElement, disableElement = Rails.disableElement, handleDisabledElement = Rails.handleDisabledElement, handleConfirm = Rails.handleConfirm, preventInsignificantClick = Rails.preventInsignificantClick, handleRemote = Rails.handleRemote, formSubmitButtonClick = Rails.formSubmitButtonClick, handleMethod = Rails.handleMethod;
659
+ fire = Rails.fire, delegate = Rails.delegate, getData = Rails.getData, $ = Rails.$, refreshCSRFTokens = Rails.refreshCSRFTokens, CSRFProtection = Rails.CSRFProtection, enableElement = Rails.enableElement, disableElement = Rails.disableElement, handleDisabledElement = Rails.handleDisabledElement, handleConfirm = Rails.handleConfirm, preventInsignificantClick = Rails.preventInsignificantClick, handleRemote = Rails.handleRemote, formSubmitButtonClick = Rails.formSubmitButtonClick, handleMethod = Rails.handleMethod;
642
660
 
643
661
  if ((typeof jQuery !== "undefined" && jQuery !== null) && (jQuery.ajax != null)) {
644
662
  if (jQuery.rails) {
@@ -701,7 +719,6 @@ Released under the MIT license
701
719
  delegate(document, Rails.formInputClickSelector, 'click', handleConfirm);
702
720
  delegate(document, Rails.formInputClickSelector, 'click', formSubmitButtonClick);
703
721
  document.addEventListener('DOMContentLoaded', refreshCSRFTokens);
704
- document.addEventListener('DOMContentLoaded', loadCSPNonce);
705
722
  return window._rails_loaded = true;
706
723
  };
707
724