actionview 7.0.8.6 → 7.1.0.beta1

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