actionview 7.0.8.7 → 7.1.5.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +309 -321
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/app/assets/javascripts/rails-ujs.esm.js +686 -0
- data/app/assets/javascripts/rails-ujs.js +630 -0
- data/lib/action_view/base.rb +34 -14
- data/lib/action_view/buffers.rb +106 -8
- data/lib/action_view/cache_expiry.rb +40 -43
- data/lib/action_view/context.rb +1 -1
- data/lib/action_view/deprecator.rb +7 -0
- data/lib/action_view/digestor.rb +1 -1
- data/lib/action_view/gem_version.rb +4 -4
- data/lib/action_view/helpers/active_model_helper.rb +1 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +136 -52
- data/lib/action_view/helpers/asset_url_helper.rb +6 -5
- data/lib/action_view/helpers/atom_feed_helper.rb +5 -5
- data/lib/action_view/helpers/cache_helper.rb +7 -13
- data/lib/action_view/helpers/capture_helper.rb +30 -10
- data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
- data/lib/action_view/helpers/controller_helper.rb +6 -0
- data/lib/action_view/helpers/csp_helper.rb +2 -2
- data/lib/action_view/helpers/csrf_helper.rb +2 -2
- data/lib/action_view/helpers/date_helper.rb +17 -19
- data/lib/action_view/helpers/debug_helper.rb +3 -3
- data/lib/action_view/helpers/form_helper.rb +54 -25
- data/lib/action_view/helpers/form_options_helper.rb +2 -1
- data/lib/action_view/helpers/form_tag_helper.rb +49 -15
- data/lib/action_view/helpers/javascript_helper.rb +1 -0
- data/lib/action_view/helpers/number_helper.rb +37 -330
- data/lib/action_view/helpers/output_safety_helper.rb +2 -2
- data/lib/action_view/helpers/rendering_helper.rb +1 -1
- data/lib/action_view/helpers/sanitize_helper.rb +51 -21
- data/lib/action_view/helpers/tag_helper.rb +5 -27
- data/lib/action_view/helpers/tags/base.rb +11 -52
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +1 -0
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +1 -0
- data/lib/action_view/helpers/tags/collection_select.rb +3 -0
- data/lib/action_view/helpers/tags/date_field.rb +1 -1
- data/lib/action_view/helpers/tags/date_select.rb +2 -0
- data/lib/action_view/helpers/tags/datetime_field.rb +14 -6
- data/lib/action_view/helpers/tags/datetime_local_field.rb +11 -2
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +3 -0
- data/lib/action_view/helpers/tags/month_field.rb +1 -1
- data/lib/action_view/helpers/tags/select.rb +3 -0
- data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
- data/lib/action_view/helpers/tags/time_field.rb +1 -1
- data/lib/action_view/helpers/tags/time_zone_select.rb +3 -0
- data/lib/action_view/helpers/tags/week_field.rb +1 -1
- data/lib/action_view/helpers/tags/weekday_select.rb +3 -0
- data/lib/action_view/helpers/tags.rb +2 -0
- data/lib/action_view/helpers/text_helper.rb +156 -84
- data/lib/action_view/helpers/translation_helper.rb +3 -3
- data/lib/action_view/helpers/url_helper.rb +46 -17
- data/lib/action_view/helpers.rb +2 -0
- data/lib/action_view/layouts.rb +8 -6
- data/lib/action_view/log_subscriber.rb +49 -32
- data/lib/action_view/lookup_context.rb +29 -13
- data/lib/action_view/path_registry.rb +57 -0
- data/lib/action_view/path_set.rb +13 -14
- data/lib/action_view/railtie.rb +26 -3
- data/lib/action_view/record_identifier.rb +15 -8
- data/lib/action_view/renderer/abstract_renderer.rb +1 -1
- data/lib/action_view/renderer/collection_renderer.rb +10 -2
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +2 -1
- data/lib/action_view/renderer/partial_renderer.rb +2 -1
- data/lib/action_view/renderer/renderer.rb +2 -0
- data/lib/action_view/renderer/streaming_template_renderer.rb +3 -2
- data/lib/action_view/renderer/template_renderer.rb +3 -2
- data/lib/action_view/rendering.rb +22 -4
- data/lib/action_view/ripper_ast_parser.rb +5 -5
- data/lib/action_view/template/error.rb +14 -1
- data/lib/action_view/template/handlers/builder.rb +4 -4
- data/lib/action_view/template/handlers/erb/erubi.rb +23 -27
- data/lib/action_view/template/handlers/erb.rb +73 -1
- data/lib/action_view/template/handlers.rb +1 -1
- data/lib/action_view/template/html.rb +1 -1
- data/lib/action_view/template/raw_file.rb +1 -1
- data/lib/action_view/template/renderable.rb +1 -1
- data/lib/action_view/template/resolver.rb +10 -2
- data/lib/action_view/template/text.rb +1 -1
- data/lib/action_view/template/types.rb +25 -34
- data/lib/action_view/template.rb +249 -54
- data/lib/action_view/template_path.rb +2 -0
- data/lib/action_view/test_case.rb +176 -21
- data/lib/action_view/unbound_template.rb +17 -7
- data/lib/action_view/version.rb +1 -1
- data/lib/action_view/view_paths.rb +15 -24
- data/lib/action_view.rb +4 -1
- metadata +22 -23
- data/lib/assets/compiled/rails-ujs.js +0 -777
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -5,7 +5,7 @@ view helpers that assist when building HTML forms, Atom feeds and more.
|
|
5
5
|
Template formats that Action View handles are ERB (embedded Ruby, typically
|
6
6
|
used to inline short Ruby snippets inside HTML), and XML Builder.
|
7
7
|
|
8
|
-
You can read more about Action View in the {Action View Overview}[https://
|
8
|
+
You can read more about Action View in the {Action View Overview}[https://guides.rubyonrails.org/action_view_overview.html] guide.
|
9
9
|
|
10
10
|
== Download and installation
|
11
11
|
|
@@ -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 };
|