actionview 7.0.8.6 → 7.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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