|
22 | 22 |
|
23 | 23 | $.rails = rails = { |
24 | 24 | // Link elements bound by jquery-ujs |
25 | | - linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote], a[data-disable-with]', |
| 25 | + linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote], a[data-disable-with], a[data-disable]', |
26 | 26 |
|
27 | 27 | // Button elements bound by jquery-ujs |
28 | | - buttonClickSelector: 'button[data-remote]', |
| 28 | + buttonClickSelector: 'button[data-remote], button[data-confirm]', |
29 | 29 |
|
30 | 30 | // Select elements bound by jquery-ujs |
31 | 31 | inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]', |
|
37 | 37 | formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type])', |
38 | 38 |
|
39 | 39 | // Form input elements disabled during form submission |
40 | | - disableSelector: 'input[data-disable-with], button[data-disable-with], textarea[data-disable-with]', |
| 40 | + 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 | 41 |
|
42 | 42 | // 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', |
| 43 | + 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 | 44 |
|
45 | 45 | // Form required input elements |
46 | 46 | requiredInputSelector: 'input[name][required]:not([disabled]),textarea[name][required]:not([disabled])', |
|
49 | 49 | fileInputSelector: 'input[type=file]', |
50 | 50 |
|
51 | 51 | // Link onClick disable selector with possible reenable after remote submission |
52 | | - linkDisableSelector: 'a[data-disable-with]', |
| 52 | + linkDisableSelector: 'a[data-disable-with], a[data-disable]', |
| 53 | + |
| 54 | + // Button onClick disable selector with possible reenable after remote submission |
| 55 | + buttonDisableSelector: 'button[data-remote][data-disable-with], button[data-remote][data-disable]', |
53 | 56 |
|
54 | 57 | // Make sure that every Ajax request sends the CSRF token |
55 | 58 | CSRFProtection: function(xhr) { |
|
129 | 132 | if (settings.dataType === undefined) { |
130 | 133 | xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script); |
131 | 134 | } |
132 | | - return rails.fire(element, 'ajax:beforeSend', [xhr, settings]); |
| 135 | + if (rails.fire(element, 'ajax:beforeSend', [xhr, settings])) { |
| 136 | + element.trigger('ajax:send', xhr); |
| 137 | + } else { |
| 138 | + return false; |
| 139 | + } |
133 | 140 | }, |
134 | 141 | success: function(data, status, xhr) { |
135 | 142 | element.trigger('ajax:success', [data, status, xhr]); |
|
154 | 161 | // Only pass url to `ajax` options if not blank |
155 | 162 | if (url) { options.url = url; } |
156 | 163 |
|
157 | | - var jqxhr = rails.ajax(options); |
158 | | - element.trigger('ajax:send', jqxhr); |
159 | | - return jqxhr; |
| 164 | + return rails.ajax(options); |
160 | 165 | } else { |
161 | 166 | return false; |
162 | 167 | } |
|
183 | 188 | form.submit(); |
184 | 189 | }, |
185 | 190 |
|
| 191 | + // Helper function that returns form elements that match the specified CSS selector |
| 192 | + // If form is actually a "form" element this will return associated elements outside the from that have |
| 193 | + // the html form attribute set |
| 194 | + formElements: function(form, selector) { |
| 195 | + return form.is('form') ? $(form[0].elements).filter(selector) : form.find(selector); |
| 196 | + }, |
| 197 | + |
186 | 198 | /* Disables form elements: |
187 | 199 | - Caches element value in 'ujs:enable-with' data store |
188 | 200 | - Replaces element text with value of 'data-disable-with' attribute |
189 | 201 | - Sets disabled property to true |
190 | 202 | */ |
191 | 203 | 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); |
| 204 | + rails.formElements(form, rails.disableSelector).each(function() { |
| 205 | + rails.disableFormElement($(this)); |
197 | 206 | }); |
198 | 207 | }, |
199 | 208 |
|
| 209 | + disableFormElement: function(element) { |
| 210 | + var method, replacement; |
| 211 | + |
| 212 | + method = element.is('button') ? 'html' : 'val'; |
| 213 | + replacement = element.data('disable-with'); |
| 214 | + |
| 215 | + element.data('ujs:enable-with', element[method]()); |
| 216 | + if (replacement !== undefined) { |
| 217 | + element[method](replacement); |
| 218 | + } |
| 219 | + |
| 220 | + element.prop('disabled', true); |
| 221 | + }, |
| 222 | + |
200 | 223 | /* Re-enables disabled form elements: |
201 | 224 | - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`) |
202 | 225 | - Sets disabled property to false |
203 | 226 | */ |
204 | 227 | 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); |
| 228 | + rails.formElements(form, rails.enableSelector).each(function() { |
| 229 | + rails.enableFormElement($(this)); |
209 | 230 | }); |
210 | 231 | }, |
211 | 232 |
|
| 233 | + enableFormElement: function(element) { |
| 234 | + var method = element.is('button') ? 'html' : 'val'; |
| 235 | + if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with')); |
| 236 | + element.prop('disabled', false); |
| 237 | + }, |
| 238 | + |
212 | 239 | /* For 'data-confirm' attribute: |
213 | 240 | - Fires `confirm` event |
214 | 241 | - Shows the confirmation dialog |
|
269 | 296 | // replace element's html with the 'data-disable-with' after storing original html |
270 | 297 | // and prevent clicking on it |
271 | 298 | disableElement: function(element) { |
| 299 | + var replacement = element.data('disable-with'); |
| 300 | + |
272 | 301 | element.data('ujs:enable-with', element.html()); // store enabled state |
273 | | - element.html(element.data('disable-with')); // set to disabled state |
| 302 | + if (replacement !== undefined) { |
| 303 | + element.html(replacement); |
| 304 | + } |
| 305 | + |
274 | 306 | element.bind('click.railsDisable', function(e) { // prevent further clicking |
275 | 307 | return rails.stopEverything(e); |
276 | 308 | }); |
|
284 | 316 | } |
285 | 317 | element.unbind('click.railsDisable'); // enable element |
286 | 318 | } |
287 | | - |
288 | 319 | }; |
289 | 320 |
|
290 | 321 | if (rails.fire($document, 'rails:attachBindings')) { |
|
295 | 326 | rails.enableElement($(this)); |
296 | 327 | }); |
297 | 328 |
|
| 329 | + $document.delegate(rails.buttonDisableSelector, 'ajax:complete', function() { |
| 330 | + rails.enableFormElement($(this)); |
| 331 | + }); |
| 332 | + |
298 | 333 | $document.delegate(rails.linkClickSelector, 'click.rails', function(e) { |
299 | 334 | var link = $(this), method = link.data('method'), data = link.data('params'), metaClick = e.metaKey || e.ctrlKey; |
300 | 335 | if (!rails.allowAction(link)) return rails.stopEverything(e); |
|
323 | 358 | var button = $(this); |
324 | 359 | if (!rails.allowAction(button)) return rails.stopEverything(e); |
325 | 360 |
|
326 | | - rails.handleRemote(button); |
| 361 | + if (button.is(rails.buttonDisableSelector)) rails.disableFormElement(button); |
| 362 | + |
| 363 | + var handleRemote = rails.handleRemote(button); |
| 364 | + // response from rails.handleRemote() will either be false or a deferred object promise. |
| 365 | + if (handleRemote === false) { |
| 366 | + rails.enableFormElement(button); |
| 367 | + } else { |
| 368 | + handleRemote.error( function() { rails.enableFormElement(button); } ); |
| 369 | + } |
327 | 370 | return false; |
328 | 371 | }); |
329 | 372 |
|
|
338 | 381 | $document.delegate(rails.formSubmitSelector, 'submit.rails', function(e) { |
339 | 382 | var form = $(this), |
340 | 383 | remote = form.data('remote') !== undefined, |
341 | | - blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector), |
342 | | - nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector); |
| 384 | + blankRequiredInputs, |
| 385 | + nonBlankFileInputs; |
343 | 386 |
|
344 | 387 | if (!rails.allowAction(form)) return rails.stopEverything(e); |
345 | 388 |
|
346 | 389 | // 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); |
| 390 | + if (form.attr('novalidate') == undefined) { |
| 391 | + blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector); |
| 392 | + if (blankRequiredInputs && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) { |
| 393 | + return rails.stopEverything(e); |
| 394 | + } |
349 | 395 | } |
350 | 396 |
|
351 | 397 | if (remote) { |
| 398 | + nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector); |
352 | 399 | if (nonBlankFileInputs) { |
353 | 400 | // slight timeout so that the submit button gets properly serialized |
354 | 401 | // (make it easy for event handler to serialize form without disabled values) |
|
382 | 429 | button.closest('form').data('ujs:submit-button', data); |
383 | 430 | }); |
384 | 431 |
|
385 | | - $document.delegate(rails.formSubmitSelector, 'ajax:beforeSend.rails', function(event) { |
| 432 | + $document.delegate(rails.formSubmitSelector, 'ajax:send.rails', function(event) { |
386 | 433 | if (this == event.target) rails.disableFormElements($(this)); |
387 | 434 | }); |
388 | 435 |
|
|
0 commit comments