actionview 6.1.7.2 → 7.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -277
  3. data/MIT-LICENSE +2 -1
  4. data/README.rdoc +3 -3
  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 +37 -19
  8. data/lib/action_view/buffers.rb +107 -9
  9. data/lib/action_view/cache_expiry.rb +48 -37
  10. data/lib/action_view/context.rb +1 -1
  11. data/lib/action_view/dependency_tracker/erb_tracker.rb +154 -0
  12. data/lib/action_view/dependency_tracker/ripper_tracker.rb +59 -0
  13. data/lib/action_view/dependency_tracker.rb +6 -147
  14. data/lib/action_view/deprecator.rb +7 -0
  15. data/lib/action_view/digestor.rb +8 -5
  16. data/lib/action_view/flows.rb +4 -4
  17. data/lib/action_view/gem_version.rb +4 -4
  18. data/lib/action_view/helpers/active_model_helper.rb +3 -3
  19. data/lib/action_view/helpers/asset_tag_helper.rb +200 -60
  20. data/lib/action_view/helpers/asset_url_helper.rb +22 -21
  21. data/lib/action_view/helpers/atom_feed_helper.rb +8 -9
  22. data/lib/action_view/helpers/cache_helper.rb +55 -12
  23. data/lib/action_view/helpers/capture_helper.rb +34 -14
  24. data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
  25. data/lib/action_view/helpers/controller_helper.rb +8 -2
  26. data/lib/action_view/helpers/csp_helper.rb +3 -3
  27. data/lib/action_view/helpers/csrf_helper.rb +4 -4
  28. data/lib/action_view/helpers/date_helper.rb +123 -57
  29. data/lib/action_view/helpers/debug_helper.rb +6 -4
  30. data/lib/action_view/helpers/form_helper.rb +253 -97
  31. data/lib/action_view/helpers/form_options_helper.rb +72 -34
  32. data/lib/action_view/helpers/form_tag_helper.rb +189 -58
  33. data/lib/action_view/helpers/javascript_helper.rb +4 -5
  34. data/lib/action_view/helpers/number_helper.rb +43 -335
  35. data/lib/action_view/helpers/output_safety_helper.rb +6 -6
  36. data/lib/action_view/helpers/rendering_helper.rb +6 -7
  37. data/lib/action_view/helpers/sanitize_helper.rb +54 -24
  38. data/lib/action_view/helpers/tag_helper.rb +42 -35
  39. data/lib/action_view/helpers/tags/base.rb +16 -77
  40. data/lib/action_view/helpers/tags/check_box.rb +1 -1
  41. data/lib/action_view/helpers/tags/collection_check_boxes.rb +1 -0
  42. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +1 -0
  43. data/lib/action_view/helpers/tags/collection_select.rb +4 -1
  44. data/lib/action_view/helpers/tags/date_field.rb +1 -1
  45. data/lib/action_view/helpers/tags/date_select.rb +2 -0
  46. data/lib/action_view/helpers/tags/datetime_field.rb +14 -6
  47. data/lib/action_view/helpers/tags/datetime_local_field.rb +11 -2
  48. data/lib/action_view/helpers/tags/file_field.rb +16 -0
  49. data/lib/action_view/helpers/tags/grouped_collection_select.rb +3 -0
  50. data/lib/action_view/helpers/tags/month_field.rb +1 -1
  51. data/lib/action_view/helpers/tags/select.rb +4 -1
  52. data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
  53. data/lib/action_view/helpers/tags/time_field.rb +11 -2
  54. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -0
  55. data/lib/action_view/helpers/tags/week_field.rb +1 -1
  56. data/lib/action_view/helpers/tags/weekday_select.rb +31 -0
  57. data/lib/action_view/helpers/tags.rb +5 -2
  58. data/lib/action_view/helpers/text_helper.rb +180 -97
  59. data/lib/action_view/helpers/translation_helper.rb +14 -45
  60. data/lib/action_view/helpers/url_helper.rb +230 -132
  61. data/lib/action_view/helpers.rb +27 -25
  62. data/lib/action_view/layouts.rb +15 -10
  63. data/lib/action_view/log_subscriber.rb +49 -32
  64. data/lib/action_view/lookup_context.rb +58 -61
  65. data/lib/action_view/model_naming.rb +2 -2
  66. data/lib/action_view/path_registry.rb +57 -0
  67. data/lib/action_view/path_set.rb +28 -35
  68. data/lib/action_view/railtie.rb +44 -9
  69. data/lib/action_view/record_identifier.rb +16 -9
  70. data/lib/action_view/render_parser.rb +188 -0
  71. data/lib/action_view/renderer/abstract_renderer.rb +3 -3
  72. data/lib/action_view/renderer/collection_renderer.rb +10 -2
  73. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +21 -3
  74. data/lib/action_view/renderer/partial_renderer.rb +3 -36
  75. data/lib/action_view/renderer/renderer.rb +6 -4
  76. data/lib/action_view/renderer/streaming_template_renderer.rb +6 -5
  77. data/lib/action_view/renderer/template_renderer.rb +9 -4
  78. data/lib/action_view/rendering.rb +25 -7
  79. data/lib/action_view/ripper_ast_parser.rb +198 -0
  80. data/lib/action_view/routing_url_for.rb +8 -5
  81. data/lib/action_view/template/error.rb +122 -14
  82. data/lib/action_view/template/handlers/builder.rb +4 -4
  83. data/lib/action_view/template/handlers/erb/erubi.rb +23 -27
  84. data/lib/action_view/template/handlers/erb.rb +79 -1
  85. data/lib/action_view/template/handlers.rb +4 -4
  86. data/lib/action_view/template/html.rb +4 -4
  87. data/lib/action_view/template/inline.rb +3 -3
  88. data/lib/action_view/template/raw_file.rb +4 -4
  89. data/lib/action_view/template/renderable.rb +1 -1
  90. data/lib/action_view/template/resolver.rb +96 -313
  91. data/lib/action_view/template/text.rb +4 -4
  92. data/lib/action_view/template/types.rb +25 -32
  93. data/lib/action_view/template.rb +245 -41
  94. data/lib/action_view/template_details.rb +66 -0
  95. data/lib/action_view/template_path.rb +66 -0
  96. data/lib/action_view/test_case.rb +182 -23
  97. data/lib/action_view/testing/resolvers.rb +11 -12
  98. data/lib/action_view/unbound_template.rb +43 -7
  99. data/lib/action_view/version.rb +1 -1
  100. data/lib/action_view/view_paths.rb +19 -28
  101. data/lib/action_view.rb +6 -4
  102. data/lib/assets/compiled/rails-ujs.js +36 -5
  103. metadata +32 -25
@@ -0,0 +1,686 @@
1
+ /*
2
+ Unobtrusive JavaScript
3
+ https://github.com/rails/rails/blob/main/actionview/app/javascript
4
+ Released under the MIT license
5
+ */
6
+ const linkClickSelector = "a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]";
7
+
8
+ const buttonClickSelector = {
9
+ selector: "button[data-remote]:not([form]), button[data-confirm]:not([form])",
10
+ exclude: "form button"
11
+ };
12
+
13
+ const inputChangeSelector = "select[data-remote], input[data-remote], textarea[data-remote]";
14
+
15
+ const formSubmitSelector = "form:not([data-turbo=true])";
16
+
17
+ const formInputClickSelector = "form:not([data-turbo=true]) input[type=submit], form:not([data-turbo=true]) input[type=image], form:not([data-turbo=true]) button[type=submit], form:not([data-turbo=true]) button:not([type]), input[type=submit][form], input[type=image][form], button[type=submit][form], button[form]:not([type])";
18
+
19
+ const formDisableSelector = "input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled";
20
+
21
+ const formEnableSelector = "input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled";
22
+
23
+ const fileInputSelector = "input[name][type=file]:not([disabled])";
24
+
25
+ const linkDisableSelector = "a[data-disable-with], a[data-disable]";
26
+
27
+ const buttonDisableSelector = "button[data-remote][data-disable-with], button[data-remote][data-disable]";
28
+
29
+ let nonce = null;
30
+
31
+ const loadCSPNonce = () => {
32
+ const metaTag = document.querySelector("meta[name=csp-nonce]");
33
+ return nonce = metaTag && metaTag.content;
34
+ };
35
+
36
+ const cspNonce = () => nonce || loadCSPNonce();
37
+
38
+ const m = Element.prototype.matches || Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector;
39
+
40
+ const matches = function(element, selector) {
41
+ if (selector.exclude) {
42
+ return m.call(element, selector.selector) && !m.call(element, selector.exclude);
43
+ } else {
44
+ return m.call(element, selector);
45
+ }
46
+ };
47
+
48
+ const EXPANDO = "_ujsData";
49
+
50
+ const getData = (element, key) => element[EXPANDO] ? element[EXPANDO][key] : undefined;
51
+
52
+ const setData = function(element, key, value) {
53
+ if (!element[EXPANDO]) {
54
+ element[EXPANDO] = {};
55
+ }
56
+ return element[EXPANDO][key] = value;
57
+ };
58
+
59
+ const $ = selector => Array.prototype.slice.call(document.querySelectorAll(selector));
60
+
61
+ const isContentEditable = function(element) {
62
+ var isEditable = false;
63
+ do {
64
+ if (element.isContentEditable) {
65
+ isEditable = true;
66
+ break;
67
+ }
68
+ element = element.parentElement;
69
+ } while (element);
70
+ return isEditable;
71
+ };
72
+
73
+ const csrfToken = () => {
74
+ const meta = document.querySelector("meta[name=csrf-token]");
75
+ return meta && meta.content;
76
+ };
77
+
78
+ const csrfParam = () => {
79
+ const meta = document.querySelector("meta[name=csrf-param]");
80
+ return meta && meta.content;
81
+ };
82
+
83
+ const CSRFProtection = xhr => {
84
+ const token = csrfToken();
85
+ if (token) {
86
+ return xhr.setRequestHeader("X-CSRF-Token", token);
87
+ }
88
+ };
89
+
90
+ const refreshCSRFTokens = () => {
91
+ const token = csrfToken();
92
+ const param = csrfParam();
93
+ if (token && param) {
94
+ return $('form input[name="' + param + '"]').forEach((input => input.value = token));
95
+ }
96
+ };
97
+
98
+ const AcceptHeaders = {
99
+ "*": "*/*",
100
+ text: "text/plain",
101
+ html: "text/html",
102
+ xml: "application/xml, text/xml",
103
+ json: "application/json, text/javascript",
104
+ script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
105
+ };
106
+
107
+ const ajax = options => {
108
+ options = prepareOptions(options);
109
+ var xhr = createXHR(options, (function() {
110
+ const response = processResponse(xhr.response != null ? xhr.response : xhr.responseText, xhr.getResponseHeader("Content-Type"));
111
+ if (Math.floor(xhr.status / 100) === 2) {
112
+ if (typeof options.success === "function") {
113
+ options.success(response, xhr.statusText, xhr);
114
+ }
115
+ } else {
116
+ if (typeof options.error === "function") {
117
+ options.error(response, xhr.statusText, xhr);
118
+ }
119
+ }
120
+ return typeof options.complete === "function" ? options.complete(xhr, xhr.statusText) : undefined;
121
+ }));
122
+ if (options.beforeSend && !options.beforeSend(xhr, options)) {
123
+ return false;
124
+ }
125
+ if (xhr.readyState === XMLHttpRequest.OPENED) {
126
+ return xhr.send(options.data);
127
+ }
128
+ };
129
+
130
+ var prepareOptions = function(options) {
131
+ options.url = options.url || location.href;
132
+ options.type = options.type.toUpperCase();
133
+ if (options.type === "GET" && options.data) {
134
+ if (options.url.indexOf("?") < 0) {
135
+ options.url += "?" + options.data;
136
+ } else {
137
+ options.url += "&" + options.data;
138
+ }
139
+ }
140
+ if (!(options.dataType in AcceptHeaders)) {
141
+ options.dataType = "*";
142
+ }
143
+ options.accept = AcceptHeaders[options.dataType];
144
+ if (options.dataType !== "*") {
145
+ options.accept += ", */*; q=0.01";
146
+ }
147
+ return options;
148
+ };
149
+
150
+ var createXHR = function(options, done) {
151
+ const xhr = new XMLHttpRequest;
152
+ xhr.open(options.type, options.url, true);
153
+ xhr.setRequestHeader("Accept", options.accept);
154
+ if (typeof options.data === "string") {
155
+ xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
156
+ }
157
+ if (!options.crossDomain) {
158
+ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
159
+ CSRFProtection(xhr);
160
+ }
161
+ xhr.withCredentials = !!options.withCredentials;
162
+ xhr.onreadystatechange = function() {
163
+ if (xhr.readyState === XMLHttpRequest.DONE) {
164
+ return done(xhr);
165
+ }
166
+ };
167
+ return xhr;
168
+ };
169
+
170
+ var processResponse = function(response, type) {
171
+ if (typeof response === "string" && typeof type === "string") {
172
+ if (type.match(/\bjson\b/)) {
173
+ try {
174
+ response = JSON.parse(response);
175
+ } catch (error) {}
176
+ } else if (type.match(/\b(?:java|ecma)script\b/)) {
177
+ const script = document.createElement("script");
178
+ script.setAttribute("nonce", cspNonce());
179
+ script.text = response;
180
+ document.head.appendChild(script).parentNode.removeChild(script);
181
+ } else if (type.match(/\b(xml|html|svg)\b/)) {
182
+ const parser = new DOMParser;
183
+ type = type.replace(/;.+/, "");
184
+ try {
185
+ response = parser.parseFromString(response, type);
186
+ } catch (error1) {}
187
+ }
188
+ }
189
+ return response;
190
+ };
191
+
192
+ const href = element => element.href;
193
+
194
+ const isCrossDomain = function(url) {
195
+ const originAnchor = document.createElement("a");
196
+ originAnchor.href = location.href;
197
+ const urlAnchor = document.createElement("a");
198
+ try {
199
+ urlAnchor.href = url;
200
+ return !((!urlAnchor.protocol || urlAnchor.protocol === ":") && !urlAnchor.host || originAnchor.protocol + "//" + originAnchor.host === urlAnchor.protocol + "//" + urlAnchor.host);
201
+ } catch (e) {
202
+ return true;
203
+ }
204
+ };
205
+
206
+ let preventDefault;
207
+
208
+ let {CustomEvent: CustomEvent} = window;
209
+
210
+ if (typeof CustomEvent !== "function") {
211
+ CustomEvent = function(event, params) {
212
+ const evt = document.createEvent("CustomEvent");
213
+ evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
214
+ return evt;
215
+ };
216
+ CustomEvent.prototype = window.Event.prototype;
217
+ ({preventDefault: preventDefault} = CustomEvent.prototype);
218
+ CustomEvent.prototype.preventDefault = function() {
219
+ const result = preventDefault.call(this);
220
+ if (this.cancelable && !this.defaultPrevented) {
221
+ Object.defineProperty(this, "defaultPrevented", {
222
+ get() {
223
+ return true;
224
+ }
225
+ });
226
+ }
227
+ return result;
228
+ };
229
+ }
230
+
231
+ const fire = (obj, name, data) => {
232
+ const event = new CustomEvent(name, {
233
+ bubbles: true,
234
+ cancelable: true,
235
+ detail: data
236
+ });
237
+ obj.dispatchEvent(event);
238
+ return !event.defaultPrevented;
239
+ };
240
+
241
+ const stopEverything = e => {
242
+ fire(e.target, "ujs:everythingStopped");
243
+ e.preventDefault();
244
+ e.stopPropagation();
245
+ e.stopImmediatePropagation();
246
+ };
247
+
248
+ const delegate = (element, selector, eventType, handler) => element.addEventListener(eventType, (function(e) {
249
+ let {target: target} = e;
250
+ while (!!(target instanceof Element) && !matches(target, selector)) {
251
+ target = target.parentNode;
252
+ }
253
+ if (target instanceof Element && handler.call(target, e) === false) {
254
+ e.preventDefault();
255
+ e.stopPropagation();
256
+ }
257
+ }));
258
+
259
+ const toArray = e => Array.prototype.slice.call(e);
260
+
261
+ const serializeElement = (element, additionalParam) => {
262
+ let inputs = [ element ];
263
+ if (matches(element, "form")) {
264
+ inputs = toArray(element.elements);
265
+ }
266
+ const params = [];
267
+ inputs.forEach((function(input) {
268
+ if (!input.name || input.disabled) {
269
+ return;
270
+ }
271
+ if (matches(input, "fieldset[disabled] *")) {
272
+ return;
273
+ }
274
+ if (matches(input, "select")) {
275
+ toArray(input.options).forEach((function(option) {
276
+ if (option.selected) {
277
+ params.push({
278
+ name: input.name,
279
+ value: option.value
280
+ });
281
+ }
282
+ }));
283
+ } else if (input.checked || [ "radio", "checkbox", "submit" ].indexOf(input.type) === -1) {
284
+ params.push({
285
+ name: input.name,
286
+ value: input.value
287
+ });
288
+ }
289
+ }));
290
+ if (additionalParam) {
291
+ params.push(additionalParam);
292
+ }
293
+ return params.map((function(param) {
294
+ if (param.name) {
295
+ return `${encodeURIComponent(param.name)}=${encodeURIComponent(param.value)}`;
296
+ } else {
297
+ return param;
298
+ }
299
+ })).join("&");
300
+ };
301
+
302
+ const formElements = (form, selector) => {
303
+ if (matches(form, "form")) {
304
+ return toArray(form.elements).filter((el => matches(el, selector)));
305
+ } else {
306
+ return toArray(form.querySelectorAll(selector));
307
+ }
308
+ };
309
+
310
+ const handleConfirmWithRails = rails => function(e) {
311
+ if (!allowAction(this, rails)) {
312
+ stopEverything(e);
313
+ }
314
+ };
315
+
316
+ const confirm = (message, element) => window.confirm(message);
317
+
318
+ var allowAction = function(element, rails) {
319
+ let callback;
320
+ const message = element.getAttribute("data-confirm");
321
+ if (!message) {
322
+ return true;
323
+ }
324
+ let answer = false;
325
+ if (fire(element, "confirm")) {
326
+ try {
327
+ answer = rails.confirm(message, element);
328
+ } catch (error) {}
329
+ callback = fire(element, "confirm:complete", [ answer ]);
330
+ }
331
+ return answer && callback;
332
+ };
333
+
334
+ const handleDisabledElement = function(e) {
335
+ const element = this;
336
+ if (element.disabled) {
337
+ stopEverything(e);
338
+ }
339
+ };
340
+
341
+ const enableElement = e => {
342
+ let element;
343
+ if (e instanceof Event) {
344
+ if (isXhrRedirect(e)) {
345
+ return;
346
+ }
347
+ element = e.target;
348
+ } else {
349
+ element = e;
350
+ }
351
+ if (isContentEditable(element)) {
352
+ return;
353
+ }
354
+ if (matches(element, linkDisableSelector)) {
355
+ return enableLinkElement(element);
356
+ } else if (matches(element, buttonDisableSelector) || matches(element, formEnableSelector)) {
357
+ return enableFormElement(element);
358
+ } else if (matches(element, formSubmitSelector)) {
359
+ return enableFormElements(element);
360
+ }
361
+ };
362
+
363
+ const disableElement = e => {
364
+ const element = e instanceof Event ? e.target : e;
365
+ if (isContentEditable(element)) {
366
+ return;
367
+ }
368
+ if (matches(element, linkDisableSelector)) {
369
+ return disableLinkElement(element);
370
+ } else if (matches(element, buttonDisableSelector) || matches(element, formDisableSelector)) {
371
+ return disableFormElement(element);
372
+ } else if (matches(element, formSubmitSelector)) {
373
+ return disableFormElements(element);
374
+ }
375
+ };
376
+
377
+ var disableLinkElement = function(element) {
378
+ if (getData(element, "ujs:disabled")) {
379
+ return;
380
+ }
381
+ const replacement = element.getAttribute("data-disable-with");
382
+ if (replacement != null) {
383
+ setData(element, "ujs:enable-with", element.innerHTML);
384
+ element.innerHTML = replacement;
385
+ }
386
+ element.addEventListener("click", stopEverything);
387
+ return setData(element, "ujs:disabled", true);
388
+ };
389
+
390
+ var enableLinkElement = function(element) {
391
+ const originalText = getData(element, "ujs:enable-with");
392
+ if (originalText != null) {
393
+ element.innerHTML = originalText;
394
+ setData(element, "ujs:enable-with", null);
395
+ }
396
+ element.removeEventListener("click", stopEverything);
397
+ return setData(element, "ujs:disabled", null);
398
+ };
399
+
400
+ var disableFormElements = form => formElements(form, formDisableSelector).forEach(disableFormElement);
401
+
402
+ var disableFormElement = function(element) {
403
+ if (getData(element, "ujs:disabled")) {
404
+ return;
405
+ }
406
+ const replacement = element.getAttribute("data-disable-with");
407
+ if (replacement != null) {
408
+ if (matches(element, "button")) {
409
+ setData(element, "ujs:enable-with", element.innerHTML);
410
+ element.innerHTML = replacement;
411
+ } else {
412
+ setData(element, "ujs:enable-with", element.value);
413
+ element.value = replacement;
414
+ }
415
+ }
416
+ element.disabled = true;
417
+ return setData(element, "ujs:disabled", true);
418
+ };
419
+
420
+ var enableFormElements = form => formElements(form, formEnableSelector).forEach((element => enableFormElement(element)));
421
+
422
+ var enableFormElement = function(element) {
423
+ const originalText = getData(element, "ujs:enable-with");
424
+ if (originalText != null) {
425
+ if (matches(element, "button")) {
426
+ element.innerHTML = originalText;
427
+ } else {
428
+ element.value = originalText;
429
+ }
430
+ setData(element, "ujs:enable-with", null);
431
+ }
432
+ element.disabled = false;
433
+ return setData(element, "ujs:disabled", null);
434
+ };
435
+
436
+ var isXhrRedirect = function(event) {
437
+ const xhr = event.detail ? event.detail[0] : undefined;
438
+ return xhr && xhr.getResponseHeader("X-Xhr-Redirect");
439
+ };
440
+
441
+ const handleMethodWithRails = rails => function(e) {
442
+ const link = this;
443
+ const method = link.getAttribute("data-method");
444
+ if (!method) {
445
+ return;
446
+ }
447
+ if (isContentEditable(this)) {
448
+ return;
449
+ }
450
+ const href = rails.href(link);
451
+ const csrfToken$1 = csrfToken();
452
+ const csrfParam$1 = csrfParam();
453
+ const form = document.createElement("form");
454
+ let formContent = `<input name='_method' value='${method}' type='hidden' />`;
455
+ if (csrfParam$1 && csrfToken$1 && !isCrossDomain(href)) {
456
+ formContent += `<input name='${csrfParam$1}' value='${csrfToken$1}' type='hidden' />`;
457
+ }
458
+ formContent += '<input type="submit" />';
459
+ form.method = "post";
460
+ form.action = href;
461
+ form.target = link.target;
462
+ form.innerHTML = formContent;
463
+ form.style.display = "none";
464
+ document.body.appendChild(form);
465
+ form.querySelector('[type="submit"]').click();
466
+ stopEverything(e);
467
+ };
468
+
469
+ const isRemote = function(element) {
470
+ const value = element.getAttribute("data-remote");
471
+ return value != null && value !== "false";
472
+ };
473
+
474
+ const handleRemoteWithRails = rails => function(e) {
475
+ let data, method, url;
476
+ const element = this;
477
+ if (!isRemote(element)) {
478
+ return true;
479
+ }
480
+ if (!fire(element, "ajax:before")) {
481
+ fire(element, "ajax:stopped");
482
+ return false;
483
+ }
484
+ if (isContentEditable(element)) {
485
+ fire(element, "ajax:stopped");
486
+ return false;
487
+ }
488
+ const withCredentials = element.getAttribute("data-with-credentials");
489
+ const dataType = element.getAttribute("data-type") || "script";
490
+ if (matches(element, formSubmitSelector)) {
491
+ const button = getData(element, "ujs:submit-button");
492
+ method = getData(element, "ujs:submit-button-formmethod") || element.getAttribute("method") || "get";
493
+ url = getData(element, "ujs:submit-button-formaction") || element.getAttribute("action") || location.href;
494
+ if (method.toUpperCase() === "GET") {
495
+ url = url.replace(/\?.*$/, "");
496
+ }
497
+ if (element.enctype === "multipart/form-data") {
498
+ data = new FormData(element);
499
+ if (button != null) {
500
+ data.append(button.name, button.value);
501
+ }
502
+ } else {
503
+ data = serializeElement(element, button);
504
+ }
505
+ setData(element, "ujs:submit-button", null);
506
+ setData(element, "ujs:submit-button-formmethod", null);
507
+ setData(element, "ujs:submit-button-formaction", null);
508
+ } else if (matches(element, buttonClickSelector) || matches(element, inputChangeSelector)) {
509
+ method = element.getAttribute("data-method");
510
+ url = element.getAttribute("data-url");
511
+ data = serializeElement(element, element.getAttribute("data-params"));
512
+ } else {
513
+ method = element.getAttribute("data-method");
514
+ url = rails.href(element);
515
+ data = element.getAttribute("data-params");
516
+ }
517
+ ajax({
518
+ type: method || "GET",
519
+ url: url,
520
+ data: data,
521
+ dataType: dataType,
522
+ beforeSend(xhr, options) {
523
+ if (fire(element, "ajax:beforeSend", [ xhr, options ])) {
524
+ return fire(element, "ajax:send", [ xhr ]);
525
+ } else {
526
+ fire(element, "ajax:stopped");
527
+ return false;
528
+ }
529
+ },
530
+ success(...args) {
531
+ return fire(element, "ajax:success", args);
532
+ },
533
+ error(...args) {
534
+ return fire(element, "ajax:error", args);
535
+ },
536
+ complete(...args) {
537
+ return fire(element, "ajax:complete", args);
538
+ },
539
+ crossDomain: isCrossDomain(url),
540
+ withCredentials: withCredentials != null && withCredentials !== "false"
541
+ });
542
+ stopEverything(e);
543
+ };
544
+
545
+ const formSubmitButtonClick = function(e) {
546
+ const button = this;
547
+ const {form: form} = button;
548
+ if (!form) {
549
+ return;
550
+ }
551
+ if (button.name) {
552
+ setData(form, "ujs:submit-button", {
553
+ name: button.name,
554
+ value: button.value
555
+ });
556
+ }
557
+ setData(form, "ujs:formnovalidate-button", button.formNoValidate);
558
+ setData(form, "ujs:submit-button-formaction", button.getAttribute("formaction"));
559
+ return setData(form, "ujs:submit-button-formmethod", button.getAttribute("formmethod"));
560
+ };
561
+
562
+ const preventInsignificantClick = function(e) {
563
+ const link = this;
564
+ const method = (link.getAttribute("data-method") || "GET").toUpperCase();
565
+ const data = link.getAttribute("data-params");
566
+ const metaClick = e.metaKey || e.ctrlKey;
567
+ const insignificantMetaClick = metaClick && method === "GET" && !data;
568
+ const nonPrimaryMouseClick = e.button != null && e.button !== 0;
569
+ if (nonPrimaryMouseClick || insignificantMetaClick) {
570
+ e.stopImmediatePropagation();
571
+ }
572
+ };
573
+
574
+ const Rails = {
575
+ $: $,
576
+ ajax: ajax,
577
+ buttonClickSelector: buttonClickSelector,
578
+ buttonDisableSelector: buttonDisableSelector,
579
+ confirm: confirm,
580
+ cspNonce: cspNonce,
581
+ csrfToken: csrfToken,
582
+ csrfParam: csrfParam,
583
+ CSRFProtection: CSRFProtection,
584
+ delegate: delegate,
585
+ disableElement: disableElement,
586
+ enableElement: enableElement,
587
+ fileInputSelector: fileInputSelector,
588
+ fire: fire,
589
+ formElements: formElements,
590
+ formEnableSelector: formEnableSelector,
591
+ formDisableSelector: formDisableSelector,
592
+ formInputClickSelector: formInputClickSelector,
593
+ formSubmitButtonClick: formSubmitButtonClick,
594
+ formSubmitSelector: formSubmitSelector,
595
+ getData: getData,
596
+ handleDisabledElement: handleDisabledElement,
597
+ href: href,
598
+ inputChangeSelector: inputChangeSelector,
599
+ isCrossDomain: isCrossDomain,
600
+ linkClickSelector: linkClickSelector,
601
+ linkDisableSelector: linkDisableSelector,
602
+ loadCSPNonce: loadCSPNonce,
603
+ matches: matches,
604
+ preventInsignificantClick: preventInsignificantClick,
605
+ refreshCSRFTokens: refreshCSRFTokens,
606
+ serializeElement: serializeElement,
607
+ setData: setData,
608
+ stopEverything: stopEverything
609
+ };
610
+
611
+ const handleConfirm = handleConfirmWithRails(Rails);
612
+
613
+ Rails.handleConfirm = handleConfirm;
614
+
615
+ const handleMethod = handleMethodWithRails(Rails);
616
+
617
+ Rails.handleMethod = handleMethod;
618
+
619
+ const handleRemote = handleRemoteWithRails(Rails);
620
+
621
+ Rails.handleRemote = handleRemote;
622
+
623
+ const start = function() {
624
+ if (window._rails_loaded) {
625
+ throw new Error("rails-ujs has already been loaded!");
626
+ }
627
+ window.addEventListener("pageshow", (function() {
628
+ $(formEnableSelector).forEach((function(el) {
629
+ if (getData(el, "ujs:disabled")) {
630
+ enableElement(el);
631
+ }
632
+ }));
633
+ $(linkDisableSelector).forEach((function(el) {
634
+ if (getData(el, "ujs:disabled")) {
635
+ enableElement(el);
636
+ }
637
+ }));
638
+ }));
639
+ delegate(document, linkDisableSelector, "ajax:complete", enableElement);
640
+ delegate(document, linkDisableSelector, "ajax:stopped", enableElement);
641
+ delegate(document, buttonDisableSelector, "ajax:complete", enableElement);
642
+ delegate(document, buttonDisableSelector, "ajax:stopped", enableElement);
643
+ delegate(document, linkClickSelector, "click", preventInsignificantClick);
644
+ delegate(document, linkClickSelector, "click", handleDisabledElement);
645
+ delegate(document, linkClickSelector, "click", handleConfirm);
646
+ delegate(document, linkClickSelector, "click", disableElement);
647
+ delegate(document, linkClickSelector, "click", handleRemote);
648
+ delegate(document, linkClickSelector, "click", handleMethod);
649
+ delegate(document, buttonClickSelector, "click", preventInsignificantClick);
650
+ delegate(document, buttonClickSelector, "click", handleDisabledElement);
651
+ delegate(document, buttonClickSelector, "click", handleConfirm);
652
+ delegate(document, buttonClickSelector, "click", disableElement);
653
+ delegate(document, buttonClickSelector, "click", handleRemote);
654
+ delegate(document, inputChangeSelector, "change", handleDisabledElement);
655
+ delegate(document, inputChangeSelector, "change", handleConfirm);
656
+ delegate(document, inputChangeSelector, "change", handleRemote);
657
+ delegate(document, formSubmitSelector, "submit", handleDisabledElement);
658
+ delegate(document, formSubmitSelector, "submit", handleConfirm);
659
+ delegate(document, formSubmitSelector, "submit", handleRemote);
660
+ delegate(document, formSubmitSelector, "submit", (e => setTimeout((() => disableElement(e)), 13)));
661
+ delegate(document, formSubmitSelector, "ajax:send", disableElement);
662
+ delegate(document, formSubmitSelector, "ajax:complete", enableElement);
663
+ delegate(document, formInputClickSelector, "click", preventInsignificantClick);
664
+ delegate(document, formInputClickSelector, "click", handleDisabledElement);
665
+ delegate(document, formInputClickSelector, "click", handleConfirm);
666
+ delegate(document, formInputClickSelector, "click", formSubmitButtonClick);
667
+ document.addEventListener("DOMContentLoaded", refreshCSRFTokens);
668
+ document.addEventListener("DOMContentLoaded", loadCSPNonce);
669
+ return window._rails_loaded = true;
670
+ };
671
+
672
+ Rails.start = start;
673
+
674
+ if (typeof jQuery !== "undefined" && jQuery && jQuery.ajax) {
675
+ if (jQuery.rails) {
676
+ throw new Error("If you load both jquery_ujs and rails-ujs, use rails-ujs only.");
677
+ }
678
+ jQuery.rails = Rails;
679
+ jQuery.ajaxPrefilter((function(options, originalOptions, xhr) {
680
+ if (!options.crossDomain) {
681
+ return CSRFProtection(xhr);
682
+ }
683
+ }));
684
+ }
685
+
686
+ export { Rails as default };