Commit bcf8f0808d409c27d8452982681997c8a6e0dfb5
1 parent
6ee575f1
Exists in
master
and in
20 other branches
rails4: update jquery-ujs' rails.js
Showing
1 changed file
with
162 additions
and
54 deletions
Show diff stats
public/javascripts/rails.js
@@ -4,7 +4,7 @@ | @@ -4,7 +4,7 @@ | ||
4 | * Unobtrusive scripting adapter for jQuery | 4 | * Unobtrusive scripting adapter for jQuery |
5 | * https://github.com/rails/jquery-ujs | 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 | * Released under the MIT license | 9 | * Released under the MIT license |
10 | * | 10 | * |
@@ -12,6 +12,8 @@ | @@ -12,6 +12,8 @@ | ||
12 | 12 | ||
13 | // Cut down on the number of issues from people inadvertently including jquery_ujs twice | 13 | // Cut down on the number of issues from people inadvertently including jquery_ujs twice |
14 | // by detecting and raising an error when it happens. | 14 | // by detecting and raising an error when it happens. |
15 | + 'use strict'; | ||
16 | + | ||
15 | if ( $.rails !== undefined ) { | 17 | if ( $.rails !== undefined ) { |
16 | $.error('jquery-ujs has already been loaded!'); | 18 | $.error('jquery-ujs has already been loaded!'); |
17 | } | 19 | } |
@@ -22,10 +24,10 @@ | @@ -22,10 +24,10 @@ | ||
22 | 24 | ||
23 | $.rails = rails = { | 25 | $.rails = rails = { |
24 | // Link elements bound by jquery-ujs | 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], a[data-disable-with], a[data-disable]', |
26 | 28 | ||
27 | // Button elements bound by jquery-ujs | 29 | // Button elements bound by jquery-ujs |
28 | - buttonClickSelector: 'button[data-remote]', | 30 | + buttonClickSelector: 'button[data-remote]:not(form button), button[data-confirm]:not(form button)', |
29 | 31 | ||
30 | // Select elements bound by jquery-ujs | 32 | // Select elements bound by jquery-ujs |
31 | inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]', | 33 | inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]', |
@@ -34,13 +36,13 @@ | @@ -34,13 +36,13 @@ | ||
34 | formSubmitSelector: 'form', | 36 | formSubmitSelector: 'form', |
35 | 37 | ||
36 | // Form input elements bound by jquery-ujs | 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 | // Form input elements disabled during form submission | 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 | // Form input elements re-enabled after form submission | 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 | // Form required input elements | 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])', |
@@ -49,19 +51,30 @@ | @@ -49,19 +51,30 @@ | ||
49 | fileInputSelector: 'input[type=file]', | 51 | fileInputSelector: 'input[type=file]', |
50 | 52 | ||
51 | // Link onClick disable selector with possible reenable after remote submission | 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 | // Make sure that every Ajax request sends the CSRF token | 69 | // Make sure that every Ajax request sends the CSRF token |
55 | CSRFProtection: function(xhr) { | 70 | CSRFProtection: function(xhr) { |
56 | - var token = $('meta[name="csrf-token"]').attr('content'); | 71 | + var token = rails.csrfToken(); |
57 | if (token) xhr.setRequestHeader('X-CSRF-Token', token); | 72 | if (token) xhr.setRequestHeader('X-CSRF-Token', token); |
58 | }, | 73 | }, |
59 | 74 | ||
60 | // making sure that all forms have actual up-to-date token(cached forms contain old one) | 75 | // making sure that all forms have actual up-to-date token(cached forms contain old one) |
61 | refreshCSRFTokens: function(){ | 76 | refreshCSRFTokens: function(){ |
62 | - var csrfToken = $('meta[name=csrf-token]').attr('content'); | ||
63 | - var csrfParam = $('meta[name=csrf-param]').attr('content'); | ||
64 | - $('form input[name="' + csrfParam + '"]').val(csrfToken); | 77 | + $('form input[name="' + rails.csrfParam() + '"]').val(rails.csrfToken()); |
65 | }, | 78 | }, |
66 | 79 | ||
67 | // Triggers an event on an element and returns false if the event result is false | 80 | // Triggers an event on an element and returns false if the event result is false |
@@ -83,16 +96,19 @@ | @@ -83,16 +96,19 @@ | ||
83 | 96 | ||
84 | // Default way to get an element's href. May be overridden at $.rails.href. | 97 | // Default way to get an element's href. May be overridden at $.rails.href. |
85 | href: function(element) { | 98 | href: function(element) { |
86 | - 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; | ||
87 | }, | 105 | }, |
88 | 106 | ||
89 | // Submits "remote" forms and links with ajax | 107 | // Submits "remote" forms and links with ajax |
90 | handleRemote: function(element) { | 108 | handleRemote: function(element) { |
91 | - var method, url, data, elCrossDomain, crossDomain, withCredentials, dataType, options; | 109 | + var method, url, data, withCredentials, dataType, options; |
92 | 110 | ||
93 | if (rails.fire(element, 'ajax:before')) { | 111 | if (rails.fire(element, 'ajax:before')) { |
94 | - elCrossDomain = element.data('cross-domain'); | ||
95 | - crossDomain = elCrossDomain === undefined ? null : elCrossDomain; | ||
96 | withCredentials = element.data('with-credentials') || null; | 112 | withCredentials = element.data('with-credentials') || null; |
97 | dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType); | 113 | dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType); |
98 | 114 | ||
@@ -110,12 +126,12 @@ | @@ -110,12 +126,12 @@ | ||
110 | method = element.data('method'); | 126 | method = element.data('method'); |
111 | url = element.data('url'); | 127 | url = element.data('url'); |
112 | data = element.serialize(); | 128 | data = element.serialize(); |
113 | - if (element.data('params')) data = data + "&" + element.data('params'); | 129 | + if (element.data('params')) data = data + '&' + element.data('params'); |
114 | } else if (element.is(rails.buttonClickSelector)) { | 130 | } else if (element.is(rails.buttonClickSelector)) { |
115 | method = element.data('method') || 'get'; | 131 | method = element.data('method') || 'get'; |
116 | url = element.data('url'); | 132 | url = element.data('url'); |
117 | data = element.serialize(); | 133 | data = element.serialize(); |
118 | - if (element.data('params')) data = data + "&" + element.data('params'); | 134 | + if (element.data('params')) data = data + '&' + element.data('params'); |
119 | } else { | 135 | } else { |
120 | method = element.data('method'); | 136 | method = element.data('method'); |
121 | url = rails.href(element); | 137 | url = rails.href(element); |
@@ -129,7 +145,11 @@ | @@ -129,7 +145,11 @@ | ||
129 | if (settings.dataType === undefined) { | 145 | if (settings.dataType === undefined) { |
130 | xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script); | 146 | xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script); |
131 | } | 147 | } |
132 | - return rails.fire(element, 'ajax:beforeSend', [xhr, settings]); | 148 | + if (rails.fire(element, 'ajax:beforeSend', [xhr, settings])) { |
149 | + element.trigger('ajax:send', xhr); | ||
150 | + } else { | ||
151 | + return false; | ||
152 | + } | ||
133 | }, | 153 | }, |
134 | success: function(data, status, xhr) { | 154 | success: function(data, status, xhr) { |
135 | element.trigger('ajax:success', [data, status, xhr]); | 155 | element.trigger('ajax:success', [data, status, xhr]); |
@@ -140,7 +160,7 @@ | @@ -140,7 +160,7 @@ | ||
140 | error: function(xhr, status, error) { | 160 | error: function(xhr, status, error) { |
141 | element.trigger('ajax:error', [xhr, status, error]); | 161 | element.trigger('ajax:error', [xhr, status, error]); |
142 | }, | 162 | }, |
143 | - crossDomain: crossDomain | 163 | + crossDomain: rails.isCrossDomain(url) |
144 | }; | 164 | }; |
145 | 165 | ||
146 | // There is no withCredentials for IE6-8 when | 166 | // There is no withCredentials for IE6-8 when |
@@ -154,26 +174,45 @@ | @@ -154,26 +174,45 @@ | ||
154 | // Only pass url to `ajax` options if not blank | 174 | // Only pass url to `ajax` options if not blank |
155 | if (url) { options.url = url; } | 175 | if (url) { options.url = url; } |
156 | 176 | ||
157 | - var jqxhr = rails.ajax(options); | ||
158 | - element.trigger('ajax:send', jqxhr); | ||
159 | - return jqxhr; | 177 | + return rails.ajax(options); |
160 | } else { | 178 | } else { |
161 | return false; | 179 | return false; |
162 | } | 180 | } |
163 | }, | 181 | }, |
164 | 182 | ||
183 | + // Determines if the request is a cross domain request. | ||
184 | + isCrossDomain: function(url) { | ||
185 | + var originAnchor = document.createElement('a'); | ||
186 | + originAnchor.href = location.href; | ||
187 | + var urlAnchor = document.createElement('a'); | ||
188 | + | ||
189 | + try { | ||
190 | + urlAnchor.href = url; | ||
191 | + // This is a workaround to a IE bug. | ||
192 | + urlAnchor.href = urlAnchor.href; | ||
193 | + | ||
194 | + // Make sure that the browser parses the URL and that the protocols and hosts match. | ||
195 | + return !urlAnchor.protocol || !urlAnchor.host || | ||
196 | + (originAnchor.protocol + '//' + originAnchor.host !== | ||
197 | + urlAnchor.protocol + '//' + urlAnchor.host); | ||
198 | + } catch (e) { | ||
199 | + // If there is an error parsing the URL, assume it is crossDomain. | ||
200 | + return true; | ||
201 | + } | ||
202 | + }, | ||
203 | + | ||
165 | // Handles "data-method" on links such as: | 204 | // Handles "data-method" on links such as: |
166 | // <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a> | 205 | // <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a> |
167 | handleMethod: function(link) { | 206 | handleMethod: function(link) { |
168 | var href = rails.href(link), | 207 | var href = rails.href(link), |
169 | method = link.data('method'), | 208 | method = link.data('method'), |
170 | target = link.attr('target'), | 209 | target = link.attr('target'), |
171 | - csrfToken = $('meta[name=csrf-token]').attr('content'), | ||
172 | - csrfParam = $('meta[name=csrf-param]').attr('content'), | 210 | + csrfToken = rails.csrfToken(), |
211 | + csrfParam = rails.csrfParam(), | ||
173 | form = $('<form method="post" action="' + href + '"></form>'), | 212 | form = $('<form method="post" action="' + href + '"></form>'), |
174 | metadataInput = '<input name="_method" value="' + method + '" type="hidden" />'; | 213 | metadataInput = '<input name="_method" value="' + method + '" type="hidden" />'; |
175 | 214 | ||
176 | - if (csrfParam !== undefined && csrfToken !== undefined) { | 215 | + if (csrfParam !== undefined && csrfToken !== undefined && !rails.isCrossDomain(href)) { |
177 | metadataInput += '<input name="' + csrfParam + '" value="' + csrfToken + '" type="hidden" />'; | 216 | metadataInput += '<input name="' + csrfParam + '" value="' + csrfToken + '" type="hidden" />'; |
178 | } | 217 | } |
179 | 218 | ||
@@ -183,32 +222,54 @@ | @@ -183,32 +222,54 @@ | ||
183 | form.submit(); | 222 | form.submit(); |
184 | }, | 223 | }, |
185 | 224 | ||
225 | + // Helper function that returns form elements that match the specified CSS selector | ||
226 | + // If form is actually a "form" element this will return associated elements outside the from that have | ||
227 | + // the html form attribute set | ||
228 | + formElements: function(form, selector) { | ||
229 | + return form.is('form') ? $(form[0].elements).filter(selector) : form.find(selector); | ||
230 | + }, | ||
231 | + | ||
186 | /* Disables form elements: | 232 | /* Disables form elements: |
187 | - Caches element value in 'ujs:enable-with' data store | 233 | - Caches element value in 'ujs:enable-with' data store |
188 | - Replaces element text with value of 'data-disable-with' attribute | 234 | - Replaces element text with value of 'data-disable-with' attribute |
189 | - Sets disabled property to true | 235 | - Sets disabled property to true |
190 | */ | 236 | */ |
191 | disableFormElements: function(form) { | 237 | disableFormElements: function(form) { |
192 | - form.find(rails.disableSelector).each(function() { | ||
193 | - var element = $(this), method = element.is('button') ? 'html' : 'val'; | ||
194 | - element.data('ujs:enable-with', element[method]()); | ||
195 | - element[method](element.data('disable-with')); | ||
196 | - element.prop('disabled', true); | 238 | + rails.formElements(form, rails.disableSelector).each(function() { |
239 | + rails.disableFormElement($(this)); | ||
197 | }); | 240 | }); |
198 | }, | 241 | }, |
199 | 242 | ||
243 | + disableFormElement: function(element) { | ||
244 | + var method, replacement; | ||
245 | + | ||
246 | + method = element.is('button') ? 'html' : 'val'; | ||
247 | + replacement = element.data('disable-with'); | ||
248 | + | ||
249 | + element.data('ujs:enable-with', element[method]()); | ||
250 | + if (replacement !== undefined) { | ||
251 | + element[method](replacement); | ||
252 | + } | ||
253 | + | ||
254 | + element.prop('disabled', true); | ||
255 | + }, | ||
256 | + | ||
200 | /* Re-enables disabled form elements: | 257 | /* Re-enables disabled form elements: |
201 | - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`) | 258 | - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`) |
202 | - Sets disabled property to false | 259 | - Sets disabled property to false |
203 | */ | 260 | */ |
204 | enableFormElements: function(form) { | 261 | enableFormElements: function(form) { |
205 | - form.find(rails.enableSelector).each(function() { | ||
206 | - var element = $(this), method = element.is('button') ? 'html' : 'val'; | ||
207 | - if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with')); | ||
208 | - element.prop('disabled', false); | 262 | + rails.formElements(form, rails.enableSelector).each(function() { |
263 | + rails.enableFormElement($(this)); | ||
209 | }); | 264 | }); |
210 | }, | 265 | }, |
211 | 266 | ||
267 | + enableFormElement: function(element) { | ||
268 | + var method = element.is('button') ? 'html' : 'val'; | ||
269 | + if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with')); | ||
270 | + element.prop('disabled', false); | ||
271 | + }, | ||
272 | + | ||
212 | /* For 'data-confirm' attribute: | 273 | /* For 'data-confirm' attribute: |
213 | - Fires `confirm` event | 274 | - Fires `confirm` event |
214 | - Shows the confirmation dialog | 275 | - Shows the confirmation dialog |
@@ -225,7 +286,11 @@ | @@ -225,7 +286,11 @@ | ||
225 | if (!message) { return true; } | 286 | if (!message) { return true; } |
226 | 287 | ||
227 | if (rails.fire(element, 'confirm')) { | 288 | if (rails.fire(element, 'confirm')) { |
228 | - answer = rails.confirm(message); | 289 | + try { |
290 | + answer = rails.confirm(message); | ||
291 | + } catch (e) { | ||
292 | + (console.error || console.log).call(console, e.stack || e); | ||
293 | + } | ||
229 | callback = rails.fire(element, 'confirm:complete', [answer]); | 294 | callback = rails.fire(element, 'confirm:complete', [answer]); |
230 | } | 295 | } |
231 | return answer && callback; | 296 | return answer && callback; |
@@ -239,9 +304,8 @@ | @@ -239,9 +304,8 @@ | ||
239 | 304 | ||
240 | allInputs.each(function() { | 305 | allInputs.each(function() { |
241 | input = $(this); | 306 | input = $(this); |
242 | - valueToCheck = input.is('input[type=checkbox],input[type=radio]') ? input.is(':checked') : input.val(); | ||
243 | - // If nonBlank and valueToCheck are both truthy, or nonBlank and valueToCheck are both falsey | ||
244 | - if (!valueToCheck === !nonBlank) { | 307 | + valueToCheck = input.is('input[type=checkbox],input[type=radio]') ? input.is(':checked') : !!input.val(); |
308 | + if (valueToCheck === nonBlank) { | ||
245 | 309 | ||
246 | // Don't count unchecked required radio if other radio with same name is checked | 310 | // Don't count unchecked required radio if other radio with same name is checked |
247 | if (input.is('input[type=radio]') && allInputs.filter('input[type=radio]:checked[name="' + input.attr('name') + '"]').length) { | 311 | if (input.is('input[type=radio]') && allInputs.filter('input[type=radio]:checked[name="' + input.attr('name') + '"]').length) { |
@@ -269,8 +333,13 @@ | @@ -269,8 +333,13 @@ | ||
269 | // replace element's html with the 'data-disable-with' after storing original html | 333 | // replace element's html with the 'data-disable-with' after storing original html |
270 | // and prevent clicking on it | 334 | // and prevent clicking on it |
271 | disableElement: function(element) { | 335 | disableElement: function(element) { |
336 | + var replacement = element.data('disable-with'); | ||
337 | + | ||
272 | element.data('ujs:enable-with', element.html()); // store enabled state | 338 | element.data('ujs:enable-with', element.html()); // store enabled state |
273 | - element.html(element.data('disable-with')); // set to disabled state | 339 | + if (replacement !== undefined) { |
340 | + element.html(replacement); | ||
341 | + } | ||
342 | + | ||
274 | element.bind('click.railsDisable', function(e) { // prevent further clicking | 343 | element.bind('click.railsDisable', function(e) { // prevent further clicking |
275 | return rails.stopEverything(e); | 344 | return rails.stopEverything(e); |
276 | }); | 345 | }); |
@@ -284,24 +353,50 @@ | @@ -284,24 +353,50 @@ | ||
284 | } | 353 | } |
285 | element.unbind('click.railsDisable'); // enable element | 354 | element.unbind('click.railsDisable'); // enable element |
286 | } | 355 | } |
287 | - | ||
288 | }; | 356 | }; |
289 | 357 | ||
290 | if (rails.fire($document, 'rails:attachBindings')) { | 358 | if (rails.fire($document, 'rails:attachBindings')) { |
291 | 359 | ||
292 | $.ajaxPrefilter(function(options, originalOptions, xhr){ if ( !options.crossDomain ) { rails.CSRFProtection(xhr); }}); | 360 | $.ajaxPrefilter(function(options, originalOptions, xhr){ if ( !options.crossDomain ) { rails.CSRFProtection(xhr); }}); |
293 | 361 | ||
362 | + // This event works the same as the load event, except that it fires every | ||
363 | + // time the page is loaded. | ||
364 | + // | ||
365 | + // See https://github.com/rails/jquery-ujs/issues/357 | ||
366 | + // See https://developer.mozilla.org/en-US/docs/Using_Firefox_1.5_caching | ||
367 | + $(window).on('pageshow.rails', function () { | ||
368 | + $($.rails.enableSelector).each(function () { | ||
369 | + var element = $(this); | ||
370 | + | ||
371 | + if (element.data('ujs:enable-with')) { | ||
372 | + $.rails.enableFormElement(element); | ||
373 | + } | ||
374 | + }); | ||
375 | + | ||
376 | + $($.rails.linkDisableSelector).each(function () { | ||
377 | + var element = $(this); | ||
378 | + | ||
379 | + if (element.data('ujs:enable-with')) { | ||
380 | + $.rails.enableElement(element); | ||
381 | + } | ||
382 | + }); | ||
383 | + }); | ||
384 | + | ||
294 | $document.delegate(rails.linkDisableSelector, 'ajax:complete', function() { | 385 | $document.delegate(rails.linkDisableSelector, 'ajax:complete', function() { |
295 | rails.enableElement($(this)); | 386 | rails.enableElement($(this)); |
296 | }); | 387 | }); |
297 | 388 | ||
389 | + $document.delegate(rails.buttonDisableSelector, 'ajax:complete', function() { | ||
390 | + rails.enableFormElement($(this)); | ||
391 | + }); | ||
392 | + | ||
298 | $document.delegate(rails.linkClickSelector, 'click.rails', function(e) { | 393 | $document.delegate(rails.linkClickSelector, 'click.rails', function(e) { |
299 | var link = $(this), method = link.data('method'), data = link.data('params'), metaClick = e.metaKey || e.ctrlKey; | 394 | var link = $(this), method = link.data('method'), data = link.data('params'), metaClick = e.metaKey || e.ctrlKey; |
300 | if (!rails.allowAction(link)) return rails.stopEverything(e); | 395 | if (!rails.allowAction(link)) return rails.stopEverything(e); |
301 | 396 | ||
302 | if (!metaClick && link.is(rails.linkDisableSelector)) rails.disableElement(link); | 397 | if (!metaClick && link.is(rails.linkDisableSelector)) rails.disableElement(link); |
303 | 398 | ||
304 | - if (link.data('remote') !== undefined) { | 399 | + if (rails.isRemote(link)) { |
305 | if (metaClick && (!method || method === 'GET') && !data) { return true; } | 400 | if (metaClick && (!method || method === 'GET') && !data) { return true; } |
306 | 401 | ||
307 | var handleRemote = rails.handleRemote(link); | 402 | var handleRemote = rails.handleRemote(link); |
@@ -309,11 +404,11 @@ | @@ -309,11 +404,11 @@ | ||
309 | if (handleRemote === false) { | 404 | if (handleRemote === false) { |
310 | rails.enableElement(link); | 405 | rails.enableElement(link); |
311 | } else { | 406 | } else { |
312 | - handleRemote.error( function() { rails.enableElement(link); } ); | 407 | + handleRemote.fail( function() { rails.enableElement(link); } ); |
313 | } | 408 | } |
314 | return false; | 409 | return false; |
315 | 410 | ||
316 | - } else if (link.data('method')) { | 411 | + } else if (method) { |
317 | rails.handleMethod(link); | 412 | rails.handleMethod(link); |
318 | return false; | 413 | return false; |
319 | } | 414 | } |
@@ -321,15 +416,24 @@ | @@ -321,15 +416,24 @@ | ||
321 | 416 | ||
322 | $document.delegate(rails.buttonClickSelector, 'click.rails', function(e) { | 417 | $document.delegate(rails.buttonClickSelector, 'click.rails', function(e) { |
323 | var button = $(this); | 418 | var button = $(this); |
324 | - if (!rails.allowAction(button)) return rails.stopEverything(e); | ||
325 | 419 | ||
326 | - rails.handleRemote(button); | 420 | + if (!rails.allowAction(button) || !rails.isRemote(button)) return rails.stopEverything(e); |
421 | + | ||
422 | + if (button.is(rails.buttonDisableSelector)) rails.disableFormElement(button); | ||
423 | + | ||
424 | + var handleRemote = rails.handleRemote(button); | ||
425 | + // response from rails.handleRemote() will either be false or a deferred object promise. | ||
426 | + if (handleRemote === false) { | ||
427 | + rails.enableFormElement(button); | ||
428 | + } else { | ||
429 | + handleRemote.fail( function() { rails.enableFormElement(button); } ); | ||
430 | + } | ||
327 | return false; | 431 | return false; |
328 | }); | 432 | }); |
329 | 433 | ||
330 | $document.delegate(rails.inputChangeSelector, 'change.rails', function(e) { | 434 | $document.delegate(rails.inputChangeSelector, 'change.rails', function(e) { |
331 | var link = $(this); | 435 | var link = $(this); |
332 | - if (!rails.allowAction(link)) return rails.stopEverything(e); | 436 | + if (!rails.allowAction(link) || !rails.isRemote(link)) return rails.stopEverything(e); |
333 | 437 | ||
334 | rails.handleRemote(link); | 438 | rails.handleRemote(link); |
335 | return false; | 439 | return false; |
@@ -337,18 +441,22 @@ | @@ -337,18 +441,22 @@ | ||
337 | 441 | ||
338 | $document.delegate(rails.formSubmitSelector, 'submit.rails', function(e) { | 442 | $document.delegate(rails.formSubmitSelector, 'submit.rails', function(e) { |
339 | var form = $(this), | 443 | var form = $(this), |
340 | - remote = form.data('remote') !== undefined, | ||
341 | - blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector), | ||
342 | - nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector); | 444 | + remote = rails.isRemote(form), |
445 | + blankRequiredInputs, | ||
446 | + nonBlankFileInputs; | ||
343 | 447 | ||
344 | if (!rails.allowAction(form)) return rails.stopEverything(e); | 448 | if (!rails.allowAction(form)) return rails.stopEverything(e); |
345 | 449 | ||
346 | // skip other logic when required values are missing or file upload is present | 450 | // skip other logic when required values are missing or file upload is present |
347 | - if (blankRequiredInputs && form.attr("novalidate") == undefined && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) { | ||
348 | - return rails.stopEverything(e); | 451 | + if (form.attr('novalidate') === undefined) { |
452 | + blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector, false); | ||
453 | + if (blankRequiredInputs && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) { | ||
454 | + return rails.stopEverything(e); | ||
455 | + } | ||
349 | } | 456 | } |
350 | 457 | ||
351 | if (remote) { | 458 | if (remote) { |
459 | + nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector); | ||
352 | if (nonBlankFileInputs) { | 460 | if (nonBlankFileInputs) { |
353 | // slight timeout so that the submit button gets properly serialized | 461 | // slight timeout so that the submit button gets properly serialized |
354 | // (make it easy for event handler to serialize form without disabled values) | 462 | // (make it easy for event handler to serialize form without disabled values) |
@@ -382,12 +490,12 @@ | @@ -382,12 +490,12 @@ | ||
382 | button.closest('form').data('ujs:submit-button', data); | 490 | button.closest('form').data('ujs:submit-button', data); |
383 | }); | 491 | }); |
384 | 492 | ||
385 | - $document.delegate(rails.formSubmitSelector, 'ajax:beforeSend.rails', function(event) { | ||
386 | - if (this == event.target) rails.disableFormElements($(this)); | 493 | + $document.delegate(rails.formSubmitSelector, 'ajax:send.rails', function(event) { |
494 | + if (this === event.target) rails.disableFormElements($(this)); | ||
387 | }); | 495 | }); |
388 | 496 | ||
389 | $document.delegate(rails.formSubmitSelector, 'ajax:complete.rails', function(event) { | 497 | $document.delegate(rails.formSubmitSelector, 'ajax:complete.rails', function(event) { |
390 | - if (this == event.target) rails.enableFormElements($(this)); | 498 | + if (this === event.target) rails.enableFormElements($(this)); |
391 | }); | 499 | }); |
392 | 500 | ||
393 | $(function(){ | 501 | $(function(){ |