jquery-rails 3.0.4 → 4.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,7 +4,7 @@
4
4
  * Unobtrusive scripting adapter for jQuery
5
5
  * https://github.com/rails/jquery-ujs
6
6
  *
7
- * Requires jQuery 1.7.0 or later.
7
+ * Requires jQuery 1.8.0 or later.
8
8
  *
9
9
  * Released under the MIT license
10
10
  *
@@ -12,6 +12,8 @@
12
12
 
13
13
  // Cut down on the number of issues from people inadvertently including jquery_ujs twice
14
14
  // by detecting and raising an error when it happens.
15
+ 'use strict';
16
+
15
17
  if ( $.rails !== undefined ) {
16
18
  $.error('jquery-ujs has already been loaded!');
17
19
  }
@@ -22,10 +24,10 @@
22
24
 
23
25
  $.rails = rails = {
24
26
  // Link elements bound by jquery-ujs
25
- linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote], a[data-disable-with]',
27
+ linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]',
26
28
 
27
- // Button elements boud jquery-ujs
28
- buttonClickSelector: 'button[data-remote]',
29
+ // Button elements bound by jquery-ujs
30
+ buttonClickSelector: 'button[data-remote]:not([form]):not(form button), button[data-confirm]:not([form]):not(form button)',
29
31
 
30
32
  // Select elements bound by jquery-ujs
31
33
  inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]',
@@ -34,29 +36,47 @@
34
36
  formSubmitSelector: 'form',
35
37
 
36
38
  // Form input elements bound by jquery-ujs
37
- formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type])',
39
+ formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type]), input[type=submit][form], input[type=image][form], button[type=submit][form], button[form]:not([type])',
38
40
 
39
41
  // Form input elements disabled during form submission
40
- disableSelector: 'input[data-disable-with], button[data-disable-with], textarea[data-disable-with]',
42
+ disableSelector: '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',
41
43
 
42
44
  // Form input elements re-enabled after form submission
43
- enableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled',
45
+ enableSelector: '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',
44
46
 
45
47
  // Form required input elements
46
- requiredInputSelector: 'input[name][required]:not([disabled]),textarea[name][required]:not([disabled])',
48
+ requiredInputSelector: 'input[name][required]:not([disabled]), textarea[name][required]:not([disabled])',
47
49
 
48
50
  // Form file input elements
49
- fileInputSelector: 'input[type=file]',
51
+ fileInputSelector: 'input[name][type=file]:not([disabled])',
50
52
 
51
53
  // Link onClick disable selector with possible reenable after remote submission
52
- linkDisableSelector: 'a[data-disable-with]',
54
+ linkDisableSelector: 'a[data-disable-with], a[data-disable]',
55
+
56
+ // Button onClick disable selector with possible reenable after remote submission
57
+ buttonDisableSelector: 'button[data-remote][data-disable-with], button[data-remote][data-disable]',
58
+
59
+ // Up-to-date Cross-Site Request Forgery token
60
+ csrfToken: function() {
61
+ return $('meta[name=csrf-token]').attr('content');
62
+ },
63
+
64
+ // URL param that must contain the CSRF token
65
+ csrfParam: function() {
66
+ return $('meta[name=csrf-param]').attr('content');
67
+ },
53
68
 
54
69
  // Make sure that every Ajax request sends the CSRF token
55
70
  CSRFProtection: function(xhr) {
56
- var token = $('meta[name="csrf-token"]').attr('content');
71
+ var token = rails.csrfToken();
57
72
  if (token) xhr.setRequestHeader('X-CSRF-Token', token);
58
73
  },
59
74
 
75
+ // Make sure that all forms have actual up-to-date tokens (cached forms contain old ones)
76
+ refreshCSRFTokens: function(){
77
+ $('form input[name="' + rails.csrfParam() + '"]').val(rails.csrfToken());
78
+ },
79
+
60
80
  // Triggers an event on an element and returns false if the event result is false
61
81
  fire: function(obj, name, data) {
62
82
  var event = $.Event(name);
@@ -76,39 +96,44 @@
76
96
 
77
97
  // Default way to get an element's href. May be overridden at $.rails.href.
78
98
  href: function(element) {
79
- return element.attr('href');
99
+ return element[0].href;
100
+ },
101
+
102
+ // Checks "data-remote" if true to handle the request through a XHR request.
103
+ isRemote: function(element) {
104
+ return element.data('remote') !== undefined && element.data('remote') !== false;
80
105
  },
81
106
 
82
107
  // Submits "remote" forms and links with ajax
83
108
  handleRemote: function(element) {
84
- var method, url, data, elCrossDomain, crossDomain, withCredentials, dataType, options;
109
+ var method, url, data, withCredentials, dataType, options;
85
110
 
86
111
  if (rails.fire(element, 'ajax:before')) {
87
- elCrossDomain = element.data('cross-domain');
88
- crossDomain = elCrossDomain === undefined ? null : elCrossDomain;
89
112
  withCredentials = element.data('with-credentials') || null;
90
113
  dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType);
91
114
 
92
115
  if (element.is('form')) {
93
- method = element.attr('method');
94
- url = element.attr('action');
95
- data = element.serializeArray();
116
+ method = element.data('ujs:submit-button-formmethod') || element.attr('method');
117
+ url = element.data('ujs:submit-button-formaction') || element.attr('action');
118
+ data = $(element[0]).serializeArray();
96
119
  // memoized value from clicked submit button
97
120
  var button = element.data('ujs:submit-button');
98
121
  if (button) {
99
122
  data.push(button);
100
123
  element.data('ujs:submit-button', null);
101
124
  }
125
+ element.data('ujs:submit-button-formmethod', null);
126
+ element.data('ujs:submit-button-formaction', null);
102
127
  } else if (element.is(rails.inputChangeSelector)) {
103
128
  method = element.data('method');
104
129
  url = element.data('url');
105
130
  data = element.serialize();
106
- if (element.data('params')) data = data + "&" + element.data('params');
131
+ if (element.data('params')) data = data + '&' + element.data('params');
107
132
  } else if (element.is(rails.buttonClickSelector)) {
108
133
  method = element.data('method') || 'get';
109
134
  url = element.data('url');
110
135
  data = element.serialize();
111
- if (element.data('params')) data = data + "&" + element.data('params');
136
+ if (element.data('params')) data = data + '&' + element.data('params');
112
137
  } else {
113
138
  method = element.data('method');
114
139
  url = rails.href(element);
@@ -122,7 +147,11 @@
122
147
  if (settings.dataType === undefined) {
123
148
  xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script);
124
149
  }
125
- return rails.fire(element, 'ajax:beforeSend', [xhr, settings]);
150
+ if (rails.fire(element, 'ajax:beforeSend', [xhr, settings])) {
151
+ element.trigger('ajax:send', xhr);
152
+ } else {
153
+ return false;
154
+ }
126
155
  },
127
156
  success: function(data, status, xhr) {
128
157
  element.trigger('ajax:success', [data, status, xhr]);
@@ -133,7 +162,7 @@
133
162
  error: function(xhr, status, error) {
134
163
  element.trigger('ajax:error', [xhr, status, error]);
135
164
  },
136
- crossDomain: crossDomain
165
+ crossDomain: rails.isCrossDomain(url)
137
166
  };
138
167
 
139
168
  // There is no withCredentials for IE6-8 when
@@ -147,61 +176,111 @@
147
176
  // Only pass url to `ajax` options if not blank
148
177
  if (url) { options.url = url; }
149
178
 
150
- var jqxhr = rails.ajax(options);
151
- element.trigger('ajax:send', jqxhr);
152
- return jqxhr;
179
+ return rails.ajax(options);
153
180
  } else {
154
181
  return false;
155
182
  }
156
183
  },
157
184
 
185
+ // Determines if the request is a cross domain request.
186
+ isCrossDomain: function(url) {
187
+ var originAnchor = document.createElement('a');
188
+ originAnchor.href = location.href;
189
+ var urlAnchor = document.createElement('a');
190
+
191
+ try {
192
+ urlAnchor.href = url;
193
+ // This is a workaround to a IE bug.
194
+ urlAnchor.href = urlAnchor.href;
195
+
196
+ // If URL protocol is false or is a string containing a single colon
197
+ // *and* host are false, assume it is not a cross-domain request
198
+ // (should only be the case for IE7 and IE compatibility mode).
199
+ // Otherwise, evaluate protocol and host of the URL against the origin
200
+ // protocol and host.
201
+ return !(((!urlAnchor.protocol || urlAnchor.protocol === ':') && !urlAnchor.host) ||
202
+ (originAnchor.protocol + '//' + originAnchor.host ===
203
+ urlAnchor.protocol + '//' + urlAnchor.host));
204
+ } catch (e) {
205
+ // If there is an error parsing the URL, assume it is crossDomain.
206
+ return true;
207
+ }
208
+ },
209
+
158
210
  // Handles "data-method" on links such as:
159
211
  // <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
160
212
  handleMethod: function(link) {
161
213
  var href = rails.href(link),
162
214
  method = link.data('method'),
163
215
  target = link.attr('target'),
164
- csrf_token = $('meta[name=csrf-token]').attr('content'),
165
- csrf_param = $('meta[name=csrf-param]').attr('content'),
216
+ csrfToken = rails.csrfToken(),
217
+ csrfParam = rails.csrfParam(),
166
218
  form = $('<form method="post" action="' + href + '"></form>'),
167
- metadata_input = '<input name="_method" value="' + method + '" type="hidden" />';
219
+ metadataInput = '<input name="_method" value="' + method + '" type="hidden" />';
168
220
 
169
- if (csrf_param !== undefined && csrf_token !== undefined) {
170
- metadata_input += '<input name="' + csrf_param + '" value="' + csrf_token + '" type="hidden" />';
221
+ if (csrfParam !== undefined && csrfToken !== undefined && !rails.isCrossDomain(href)) {
222
+ metadataInput += '<input name="' + csrfParam + '" value="' + csrfToken + '" type="hidden" />';
171
223
  }
172
224
 
173
225
  if (target) { form.attr('target', target); }
174
226
 
175
- form.hide().append(metadata_input).appendTo('body');
227
+ form.hide().append(metadataInput).appendTo('body');
176
228
  form.submit();
177
229
  },
178
230
 
231
+ // Helper function that returns form elements that match the specified CSS selector
232
+ // If form is actually a "form" element this will return associated elements outside the from that have
233
+ // the html form attribute set
234
+ formElements: function(form, selector) {
235
+ return form.is('form') ? $(form[0].elements).filter(selector) : form.find(selector);
236
+ },
237
+
179
238
  /* Disables form elements:
180
239
  - Caches element value in 'ujs:enable-with' data store
181
240
  - Replaces element text with value of 'data-disable-with' attribute
182
241
  - Sets disabled property to true
183
242
  */
184
243
  disableFormElements: function(form) {
185
- form.find(rails.disableSelector).each(function() {
186
- var element = $(this), method = element.is('button') ? 'html' : 'val';
187
- element.data('ujs:enable-with', element[method]());
188
- element[method](element.data('disable-with'));
189
- element.prop('disabled', true);
244
+ rails.formElements(form, rails.disableSelector).each(function() {
245
+ rails.disableFormElement($(this));
190
246
  });
191
247
  },
192
248
 
249
+ disableFormElement: function(element) {
250
+ var method, replacement;
251
+
252
+ method = element.is('button') ? 'html' : 'val';
253
+ replacement = element.data('disable-with');
254
+
255
+ if (replacement !== undefined) {
256
+ element.data('ujs:enable-with', element[method]());
257
+ element[method](replacement);
258
+ }
259
+
260
+ element.prop('disabled', true);
261
+ element.data('ujs:disabled', true);
262
+ },
263
+
193
264
  /* Re-enables disabled form elements:
194
265
  - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`)
195
266
  - Sets disabled property to false
196
267
  */
197
268
  enableFormElements: function(form) {
198
- form.find(rails.enableSelector).each(function() {
199
- var element = $(this), method = element.is('button') ? 'html' : 'val';
200
- if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with'));
201
- element.prop('disabled', false);
269
+ rails.formElements(form, rails.enableSelector).each(function() {
270
+ rails.enableFormElement($(this));
202
271
  });
203
272
  },
204
273
 
274
+ enableFormElement: function(element) {
275
+ var method = element.is('button') ? 'html' : 'val';
276
+ if (element.data('ujs:enable-with') !== undefined) {
277
+ element[method](element.data('ujs:enable-with'));
278
+ element.removeData('ujs:enable-with'); // clean up cache
279
+ }
280
+ element.prop('disabled', false);
281
+ element.removeData('ujs:disabled');
282
+ },
283
+
205
284
  /* For 'data-confirm' attribute:
206
285
  - Fires `confirm` event
207
286
  - Shows the confirmation dialog
@@ -218,7 +297,11 @@
218
297
  if (!message) { return true; }
219
298
 
220
299
  if (rails.fire(element, 'confirm')) {
221
- answer = rails.confirm(message);
300
+ try {
301
+ answer = rails.confirm(message);
302
+ } catch (e) {
303
+ (console.error || console.log).call(console, e.stack || e);
304
+ }
222
305
  callback = rails.fire(element, 'confirm:complete', [answer]);
223
306
  }
224
307
  return answer && callback;
@@ -226,25 +309,45 @@
226
309
 
227
310
  // Helper function which checks for blank inputs in a form that match the specified CSS selector
228
311
  blankInputs: function(form, specifiedSelector, nonBlank) {
229
- var inputs = $(), input, valueToCheck,
230
- selector = specifiedSelector || 'input,textarea',
231
- allInputs = form.find(selector);
232
-
233
- allInputs.each(function() {
312
+ var foundInputs = $(),
313
+ input,
314
+ valueToCheck,
315
+ radiosForNameWithNoneSelected,
316
+ radioName,
317
+ selector = specifiedSelector || 'input,textarea',
318
+ requiredInputs = form.find(selector),
319
+ checkedRadioButtonNames = {};
320
+
321
+ requiredInputs.each(function() {
234
322
  input = $(this);
235
- valueToCheck = input.is('input[type=checkbox],input[type=radio]') ? input.is(':checked') : input.val();
236
- // If nonBlank and valueToCheck are both truthy, or nonBlank and valueToCheck are both falsey
237
- if (!valueToCheck === !nonBlank) {
323
+ if (input.is('input[type=radio]')) {
238
324
 
239
- // Don't count unchecked required radio if other radio with same name is checked
240
- if (input.is('input[type=radio]') && allInputs.filter('input[type=radio]:checked[name="' + input.attr('name') + '"]').length) {
241
- return true; // Skip to next input
242
- }
325
+ // Don't count unchecked required radio as blank if other radio with same name is checked,
326
+ // regardless of whether same-name radio input has required attribute or not. The spec
327
+ // states https://www.w3.org/TR/html5/forms.html#the-required-attribute
328
+ radioName = input.attr('name');
329
+
330
+ // Skip if we've already seen the radio with this name.
331
+ if (!checkedRadioButtonNames[radioName]) {
243
332
 
244
- inputs = inputs.add(input);
333
+ // If none checked
334
+ if (form.find('input[type=radio]:checked[name="' + radioName + '"]').length === 0) {
335
+ radiosForNameWithNoneSelected = form.find(
336
+ 'input[type=radio][name="' + radioName + '"]');
337
+ foundInputs = foundInputs.add(radiosForNameWithNoneSelected);
338
+ }
339
+
340
+ // We only need to check each name once.
341
+ checkedRadioButtonNames[radioName] = radioName;
342
+ }
343
+ } else {
344
+ valueToCheck = input.is('input[type=checkbox],input[type=radio]') ? input.is(':checked') : !!input.val();
345
+ if (valueToCheck === nonBlank) {
346
+ foundInputs = foundInputs.add(input);
347
+ }
245
348
  }
246
349
  });
247
- return inputs.length ? inputs : false;
350
+ return foundInputs.length ? foundInputs : false;
248
351
  },
249
352
 
250
353
  // Helper function which checks for non-blank inputs in a form that match the specified CSS selector
@@ -259,96 +362,148 @@
259
362
  return false;
260
363
  },
261
364
 
262
- // replace element's html with the 'data-disable-with' after storing original html
365
+ // Replace element's html with the 'data-disable-with' after storing original html
263
366
  // and prevent clicking on it
264
367
  disableElement: function(element) {
265
- element.data('ujs:enable-with', element.html()); // store enabled state
266
- element.html(element.data('disable-with')); // set to disabled state
368
+ var replacement = element.data('disable-with');
369
+
370
+ if (replacement !== undefined) {
371
+ element.data('ujs:enable-with', element.html()); // store enabled state
372
+ element.html(replacement);
373
+ }
374
+
267
375
  element.bind('click.railsDisable', function(e) { // prevent further clicking
268
376
  return rails.stopEverything(e);
269
377
  });
378
+ element.data('ujs:disabled', true);
270
379
  },
271
380
 
272
- // restore element to its original state which was disabled by 'disableElement' above
381
+ // Restore element to its original state which was disabled by 'disableElement' above
273
382
  enableElement: function(element) {
274
383
  if (element.data('ujs:enable-with') !== undefined) {
275
384
  element.html(element.data('ujs:enable-with')); // set to old enabled state
276
385
  element.removeData('ujs:enable-with'); // clean up cache
277
386
  }
278
387
  element.unbind('click.railsDisable'); // enable element
388
+ element.removeData('ujs:disabled');
279
389
  }
280
-
281
390
  };
282
391
 
283
392
  if (rails.fire($document, 'rails:attachBindings')) {
284
393
 
285
394
  $.ajaxPrefilter(function(options, originalOptions, xhr){ if ( !options.crossDomain ) { rails.CSRFProtection(xhr); }});
286
395
 
287
- $document.delegate(rails.linkDisableSelector, 'ajax:complete', function() {
396
+ // This event works the same as the load event, except that it fires every
397
+ // time the page is loaded.
398
+ //
399
+ // See https://github.com/rails/jquery-ujs/issues/357
400
+ // See https://developer.mozilla.org/en-US/docs/Using_Firefox_1.5_caching
401
+ $(window).on('pageshow.rails', function () {
402
+ $($.rails.enableSelector).each(function () {
403
+ var element = $(this);
404
+
405
+ if (element.data('ujs:disabled')) {
406
+ $.rails.enableFormElement(element);
407
+ }
408
+ });
409
+
410
+ $($.rails.linkDisableSelector).each(function () {
411
+ var element = $(this);
412
+
413
+ if (element.data('ujs:disabled')) {
414
+ $.rails.enableElement(element);
415
+ }
416
+ });
417
+ });
418
+
419
+ $document.on('ajax:complete', rails.linkDisableSelector, function() {
288
420
  rails.enableElement($(this));
289
421
  });
290
422
 
291
- $document.delegate(rails.linkClickSelector, 'click.rails', function(e) {
292
- var link = $(this), method = link.data('method'), data = link.data('params');
423
+ $document.on('ajax:complete', rails.buttonDisableSelector, function() {
424
+ rails.enableFormElement($(this));
425
+ });
426
+
427
+ $document.on('click.rails', rails.linkClickSelector, function(e) {
428
+ var link = $(this), method = link.data('method'), data = link.data('params'), metaClick = e.metaKey || e.ctrlKey;
293
429
  if (!rails.allowAction(link)) return rails.stopEverything(e);
294
430
 
295
- if (link.is(rails.linkDisableSelector)) rails.disableElement(link);
431
+ if (!metaClick && link.is(rails.linkDisableSelector)) rails.disableElement(link);
296
432
 
297
- if (link.data('remote') !== undefined) {
298
- if ( (e.metaKey || e.ctrlKey) && (!method || method === 'GET') && !data ) { return true; }
433
+ if (rails.isRemote(link)) {
434
+ if (metaClick && (!method || method === 'GET') && !data) { return true; }
299
435
 
300
436
  var handleRemote = rails.handleRemote(link);
301
- // response from rails.handleRemote() will either be false or a deferred object promise.
437
+ // Response from rails.handleRemote() will either be false or a deferred object promise.
302
438
  if (handleRemote === false) {
303
439
  rails.enableElement(link);
304
440
  } else {
305
- handleRemote.error( function() { rails.enableElement(link); } );
441
+ handleRemote.fail( function() { rails.enableElement(link); } );
306
442
  }
307
443
  return false;
308
444
 
309
- } else if (link.data('method')) {
445
+ } else if (method) {
310
446
  rails.handleMethod(link);
311
447
  return false;
312
448
  }
313
449
  });
314
450
 
315
- $document.delegate(rails.buttonClickSelector, 'click.rails', function(e) {
451
+ $document.on('click.rails', rails.buttonClickSelector, function(e) {
316
452
  var button = $(this);
317
- if (!rails.allowAction(button)) return rails.stopEverything(e);
318
453
 
319
- rails.handleRemote(button);
454
+ if (!rails.allowAction(button) || !rails.isRemote(button)) return rails.stopEverything(e);
455
+
456
+ if (button.is(rails.buttonDisableSelector)) rails.disableFormElement(button);
457
+
458
+ var handleRemote = rails.handleRemote(button);
459
+ // Response from rails.handleRemote() will either be false or a deferred object promise.
460
+ if (handleRemote === false) {
461
+ rails.enableFormElement(button);
462
+ } else {
463
+ handleRemote.fail( function() { rails.enableFormElement(button); } );
464
+ }
320
465
  return false;
321
466
  });
322
467
 
323
- $document.delegate(rails.inputChangeSelector, 'change.rails', function(e) {
468
+ $document.on('change.rails', rails.inputChangeSelector, function(e) {
324
469
  var link = $(this);
325
- if (!rails.allowAction(link)) return rails.stopEverything(e);
470
+ if (!rails.allowAction(link) || !rails.isRemote(link)) return rails.stopEverything(e);
326
471
 
327
472
  rails.handleRemote(link);
328
473
  return false;
329
474
  });
330
475
 
331
- $document.delegate(rails.formSubmitSelector, 'submit.rails', function(e) {
476
+ $document.on('submit.rails', rails.formSubmitSelector, function(e) {
332
477
  var form = $(this),
333
- remote = form.data('remote') !== undefined,
334
- blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector),
335
- nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector);
478
+ remote = rails.isRemote(form),
479
+ blankRequiredInputs,
480
+ nonBlankFileInputs;
336
481
 
337
482
  if (!rails.allowAction(form)) return rails.stopEverything(e);
338
483
 
339
- // skip other logic when required values are missing or file upload is present
340
- if (blankRequiredInputs && form.attr("novalidate") == undefined && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) {
341
- return rails.stopEverything(e);
484
+ // Skip other logic when required values are missing or file upload is present
485
+ if (form.attr('novalidate') === undefined) {
486
+ if (form.data('ujs:formnovalidate-button') === undefined) {
487
+ blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector, false);
488
+ if (blankRequiredInputs && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) {
489
+ return rails.stopEverything(e);
490
+ }
491
+ } else {
492
+ // Clear the formnovalidate in case the next button click is not on a formnovalidate button
493
+ // Not strictly necessary to do here, since it is also reset on each button click, but just to be certain
494
+ form.data('ujs:formnovalidate-button', undefined);
495
+ }
342
496
  }
343
497
 
344
498
  if (remote) {
499
+ nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector);
345
500
  if (nonBlankFileInputs) {
346
- // slight timeout so that the submit button gets properly serialized
501
+ // Slight timeout so that the submit button gets properly serialized
347
502
  // (make it easy for event handler to serialize form without disabled values)
348
503
  setTimeout(function(){ rails.disableFormElements(form); }, 13);
349
504
  var aborted = rails.fire(form, 'ajax:aborted:file', [nonBlankFileInputs]);
350
505
 
351
- // re-enable form elements if event bindings return false (canceling normal form submission)
506
+ // Re-enable form elements if event bindings return false (canceling normal form submission)
352
507
  if (!aborted) { setTimeout(function(){ rails.enableFormElements(form); }, 13); }
353
508
 
354
509
  return aborted;
@@ -358,36 +513,42 @@
358
513
  return false;
359
514
 
360
515
  } else {
361
- // slight timeout so that the submit button gets properly serialized
516
+ // Slight timeout so that the submit button gets properly serialized
362
517
  setTimeout(function(){ rails.disableFormElements(form); }, 13);
363
518
  }
364
519
  });
365
520
 
366
- $document.delegate(rails.formInputClickSelector, 'click.rails', function(event) {
521
+ $document.on('click.rails', rails.formInputClickSelector, function(event) {
367
522
  var button = $(this);
368
523
 
369
524
  if (!rails.allowAction(button)) return rails.stopEverything(event);
370
525
 
371
- // register the pressed submit button
526
+ // Register the pressed submit button
372
527
  var name = button.attr('name'),
373
528
  data = name ? {name:name, value:button.val()} : null;
374
529
 
375
- button.closest('form').data('ujs:submit-button', data);
530
+ var form = button.closest('form');
531
+ if (form.length === 0) {
532
+ form = $('#' + button.attr('form'));
533
+ }
534
+ form.data('ujs:submit-button', data);
535
+
536
+ // Save attributes from button
537
+ form.data('ujs:formnovalidate-button', button.attr('formnovalidate'));
538
+ form.data('ujs:submit-button-formaction', button.attr('formaction'));
539
+ form.data('ujs:submit-button-formmethod', button.attr('formmethod'));
376
540
  });
377
541
 
378
- $document.delegate(rails.formSubmitSelector, 'ajax:beforeSend.rails', function(event) {
379
- if (this == event.target) rails.disableFormElements($(this));
542
+ $document.on('ajax:send.rails', rails.formSubmitSelector, function(event) {
543
+ if (this === event.target) rails.disableFormElements($(this));
380
544
  });
381
545
 
382
- $document.delegate(rails.formSubmitSelector, 'ajax:complete.rails', function(event) {
383
- if (this == event.target) rails.enableFormElements($(this));
546
+ $document.on('ajax:complete.rails', rails.formSubmitSelector, function(event) {
547
+ if (this === event.target) rails.enableFormElements($(this));
384
548
  });
385
549
 
386
550
  $(function(){
387
- // making sure that all forms have actual up-to-date token(cached forms contain old one)
388
- var csrf_token = $('meta[name=csrf-token]').attr('content');
389
- var csrf_param = $('meta[name=csrf-param]').attr('content');
390
- $('form input[name="' + csrf_param + '"]').val(csrf_token);
551
+ rails.refreshCSRFTokens();
391
552
  });
392
553
  }
393
554