Commit bcf8f0808d409c27d8452982681997c8a6e0dfb5
1 parent
6ee575f1
Exists in
master
and in
11 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(){ |