jquery-rails 3.0.4 → 4.4.0

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.
@@ -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