From cb7d4df9fe433477a8bf4797403518f7d3e6db66 Mon Sep 17 00:00:00 2001 From: Jason Moon Date: Tue, 27 Aug 2013 22:09:56 -0500 Subject: [PATCH 001/100] Wait to do the form DOM scans until absolutely necessary --- src/rails.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/rails.js b/src/rails.js index 84f9e6bc..2b1e9b4b 100644 --- a/src/rails.js +++ b/src/rails.js @@ -331,17 +331,21 @@ $document.delegate(rails.formSubmitSelector, 'submit.rails', function(e) { var form = $(this), remote = form.data('remote') !== undefined, - blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector), - nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector); + blankRequiredInputs, + nonBlankFileInputs; if (!rails.allowAction(form)) return rails.stopEverything(e); // skip other logic when required values are missing or file upload is present - if (blankRequiredInputs && form.attr("novalidate") == undefined && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) { - return rails.stopEverything(e); + if (form.attr('novalidate') == undefined) { + blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector); + if (blankRequiredInputs && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) { + return rails.stopEverything(e); + } } if (remote) { + nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector); if (nonBlankFileInputs) { // slight timeout so that the submit button gets properly serialized // (make it easy for event handler to serialize form without disabled values) From fbddf674f7f54d8e021c8a813e864088614c4a53 Mon Sep 17 00:00:00 2001 From: Lucas Mazza Date: Mon, 27 Jan 2014 15:28:16 -0200 Subject: [PATCH 002/100] Reorganize test suite custom CSS The versions and CDN links won't be totally mangled by the QUnit header now. --- test/views/layout.erb | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/test/views/layout.erb b/test/views/layout.erb index fe2c5483..eb791b6a 100644 --- a/test/views/layout.erb +++ b/test/views/layout.erb @@ -5,11 +5,18 @@ <%= script_tag jquery_src %> From 1c84b339d3da8157c4ba43def0923cefa592e341 Mon Sep 17 00:00:00 2001 From: Lucas Mazza Date: Fri, 1 Nov 2013 18:35:15 -0200 Subject: [PATCH 003/100] Add support for the `data-disable` attribute. This gives the same behavior as the `data-disable-with` attribute, but instead of using a replacement String from the `data-disable-with` attribute the disabled state will use the origin text/value of the element. --- src/rails.js | 23 ++- test/public/test/data-disable-with.js | 248 ++++++++++++++++++++++++++ test/public/test/data-disable.js | 136 ++++++-------- test/public/test/settings.js | 18 ++ test/views/index.erb | 2 +- 5 files changed, 340 insertions(+), 87 deletions(-) create mode 100644 test/public/test/data-disable-with.js diff --git a/src/rails.js b/src/rails.js index 309d74d6..0c4fa64e 100644 --- a/src/rails.js +++ b/src/rails.js @@ -22,7 +22,7 @@ $.rails = rails = { // Link elements bound by jquery-ujs - linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote], a[data-disable-with]', + linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote], a[data-disable-with], a[data-disable]', // Button elements bound by jquery-ujs buttonClickSelector: 'button[data-remote]', @@ -37,10 +37,10 @@ formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type])', // Form input elements disabled during form submission - disableSelector: 'input[data-disable-with], button[data-disable-with], textarea[data-disable-with]', + disableSelector: 'input[data-disable-with], button[data-disable-with], textarea[data-disable-with], input[data-disable], button[data-disable], textarea[data-disable]', // Form input elements re-enabled after form submission - enableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled', + 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', // Form required input elements requiredInputSelector: 'input[name][required]:not([disabled]),textarea[name][required]:not([disabled])', @@ -49,7 +49,7 @@ fileInputSelector: 'input[type=file]', // Link onClick disable selector with possible reenable after remote submission - linkDisableSelector: 'a[data-disable-with]', + linkDisableSelector: 'a[data-disable-with], a[data-disable]', // Make sure that every Ajax request sends the CSRF token CSRFProtection: function(xhr) { @@ -190,9 +190,13 @@ */ disableFormElements: function(form) { form.find(rails.disableSelector).each(function() { - var element = $(this), method = element.is('button') ? 'html' : 'val'; - element.data('ujs:enable-with', element[method]()); - element[method](element.data('disable-with')); + var element, method, enabledState; + element = $(this); + method = element.is('button') ? 'html' : 'val'; + enabledState = element[method](); + + element.data('ujs:enable-with', enabledState); + element[method](element.data('disable-with') || enabledState); element.prop('disabled', true); }); }, @@ -269,8 +273,9 @@ // replace element's html with the 'data-disable-with' after storing original html // and prevent clicking on it disableElement: function(element) { - element.data('ujs:enable-with', element.html()); // store enabled state - element.html(element.data('disable-with')); // set to disabled state + var enabledState = element.html(); + element.data('ujs:enable-with', enabledState); // store enabled state + element.html(element.data('disable-with') || enabledState); // set to disabled state element.bind('click.railsDisable', function(e) { // prevent further clicking return rails.stopEverything(e); }); diff --git a/test/public/test/data-disable-with.js b/test/public/test/data-disable-with.js new file mode 100644 index 00000000..80a183af --- /dev/null +++ b/test/public/test/data-disable-with.js @@ -0,0 +1,248 @@ +module('data-disable-with', { + setup: function() { + $('#qunit-fixture').append($('
', { + action: '/echo', + 'data-remote': 'true', + method: 'post' + })) + .find('form') + .append($('')); + + $('#qunit-fixture').append($('', { + action: '/echo', + method: 'post' + })) + .find('form:last') + // WEEIRDD: the form won't submit to an iframe if the button is name="submit" (??!) + .append($('')); + + $('#qunit-fixture').append($('', { + text: 'Click me', + href: '/echo', + 'data-disable-with': 'clicking...' + })); + }, + teardown: function() { + $(document).unbind('iframe:loaded'); + } +}); + + +asyncTest('form input field with "data-disable-with" attribute', 7, function() { + var form = $('form[data-remote]'), input = form.find('input[type=text]'); + + App.checkEnabledState(input, 'john'); + + form.bind('ajax:success', function(e, data) { + setTimeout(function() { + App.checkEnabledState(input, 'john'); + equal(data.params.user_name, 'john'); + start(); + }, 13) + }) + form.trigger('submit'); + + App.checkDisabledState(input, 'processing ...'); +}); + +asyncTest('form button with "data-disable-with" attribute', 6, function() { + var form = $('form[data-remote]'), button = $(''); + form.append(button); + + App.checkEnabledState(button, 'Submit'); + + form.bind('ajax:success', function(e, data) { + setTimeout(function() { + App.checkEnabledState(button, 'Submit'); + start(); + }, 13) + }) + form.trigger('submit'); + + App.checkDisabledState(button, 'submitting ...'); +}); + +asyncTest('form input[type=submit][data-disable-with] disables', 6, function(){ + var form = $('form:not([data-remote])'), input = form.find('input[type=submit]'); + + App.checkEnabledState(input, 'Submit'); + + // WEEIRDD: attaching this handler makes the test work in IE7 + $(document).bind('iframe:loading', function(e, form) {}); + + $(document).bind('iframe:loaded', function(e, data) { + setTimeout(function() { + App.checkDisabledState(input, 'submitting ...'); + start(); + }, 30); + }); + form.trigger('submit'); + + setTimeout(function() { + App.checkDisabledState(input, 'submitting ...'); + }, 30); +}); + +asyncTest('form[data-remote] input[type=submit][data-disable-with] is replaced in ajax callback', 2, function(){ + var form = $('form:not([data-remote])').attr('data-remote', 'true'), origFormContents = form.html(); + + form.bind('ajax:success', function(){ + form.html(origFormContents); + + setTimeout(function(){ + var input = form.find('input[type=submit]'); + App.checkEnabledState(input, 'Submit'); + start(); + }, 30); + }).trigger('submit'); +}); + +asyncTest('form[data-remote] input[data-disable-with] is replaced with disabled field in ajax callback', 2, function(){ + var form = $('form:not([data-remote])').attr('data-remote', 'true'), input = form.find('input[type=submit]'), + newDisabledInput = input.clone().attr('disabled', 'disabled'); + + form.bind('ajax:success', function(){ + input.replaceWith(newDisabledInput); + + setTimeout(function(){ + App.checkEnabledState(newDisabledInput, 'Submit'); + start(); + }, 30); + }).trigger('submit'); +}); + +asyncTest('form[data-remote] textarea[data-disable-with] attribute', 3, function() { + var form = $('form[data-remote]'), + textarea = $('').appendTo(form); + + form.bind('ajax:success', function(e, data) { + setTimeout(function() { + equal(data.params.user_bio, 'born, lived, died.'); + start(); + }, 13) + }) + form.trigger('submit'); + + App.checkDisabledState(textarea, 'processing ...'); +}); + +asyncTest('a[data-disable-with] disables', 4, function() { + var link = $('a[data-disable-with]'); + + App.checkEnabledState(link, 'Click me'); + + link.trigger('click'); + App.checkDisabledState(link, 'clicking...'); + start(); +}); + +asyncTest('a[data-remote][data-disable-with] disables and re-enables', 6, function() { + var link = $('a[data-disable-with]').attr('data-remote', true); + + App.checkEnabledState(link, 'Click me'); + + link + .bind('ajax:beforeSend', function() { + App.checkDisabledState(link, 'clicking...'); + }) + .bind('ajax:complete', function() { + setTimeout( function() { + App.checkEnabledState(link, 'Click me'); + start(); + }, 15); + }) + .trigger('click'); +}); + +asyncTest('a[data-remote][data-disable-with] re-enables when `ajax:before` event is cancelled', 6, function() { + var link = $('a[data-disable-with]').attr('data-remote', true); + + App.checkEnabledState(link, 'Click me'); + + link + .bind('ajax:before', function() { + App.checkDisabledState(link, 'clicking...'); + return false; + }) + .trigger('click'); + + setTimeout(function() { + App.checkEnabledState(link, 'Click me'); + start(); + }, 30); +}); + +asyncTest('a[data-remote][data-disable-with] re-enables when `ajax:beforeSend` event is cancelled', 6, function() { + var link = $('a[data-disable-with]').attr('data-remote', true); + + App.checkEnabledState(link, 'Click me'); + + link + .bind('ajax:beforeSend', function() { + App.checkDisabledState(link, 'clicking...'); + return false; + }) + .trigger('click'); + + setTimeout(function() { + App.checkEnabledState(link, 'Click me'); + start(); + }, 30); +}); + +asyncTest('a[data-remote][data-disable-with] re-enables when `ajax:error` event is triggered', 6, function() { + var link = $('a[data-disable-with]').attr('data-remote', true).attr('href', '/error'); + + App.checkEnabledState(link, 'Click me'); + + link + .bind('ajax:beforeSend', function() { + App.checkDisabledState(link, 'clicking...'); + }) + .trigger('click'); + + setTimeout(function() { + App.checkEnabledState(link, 'Click me'); + start(); + }, 30); +}); + +asyncTest('form[data-remote] input|button|textarea[data-disable-with] does not disable when `ajax:beforeSend` event is cancelled', 8, function() { + var form = $('form[data-remote]'), + input = form.find('input:text'), + button = $('').appendTo(form), + textarea = $('').appendTo(form), + submit = $('').appendTo(form); + + form + .bind('ajax:beforeSend', function() { + return false; + }) + .trigger('submit'); + + App.checkEnabledState(input, 'john'); + App.checkEnabledState(button, 'Submit'); + App.checkEnabledState(textarea, 'born, lived, died.'); + App.checkEnabledState(submit, 'Submit'); + + start(); + +}); + +asyncTest('ctrl-clicking on a link does not disables the link', 6, function() { + var link = $('a[data-disable-with]'), e; + e = $.Event('click'); + e.metaKey = true; + + App.checkEnabledState(link, 'Click me'); + + link.trigger(e); + App.checkEnabledState(link, 'Click me'); + + e = $.Event('click'); + e.ctrlKey = true; + + link.trigger(e); + App.checkEnabledState(link, 'Click me'); + start(); +}); diff --git a/test/public/test/data-disable.js b/test/public/test/data-disable.js index ec858787..181e42a1 100644 --- a/test/public/test/data-disable.js +++ b/test/public/test/data-disable.js @@ -6,7 +6,7 @@ module('data-disable', { method: 'post' })) .find('form') - .append($('')); + .append($('')); $('#qunit-fixture').append($('', { action: '/echo', @@ -14,12 +14,12 @@ module('data-disable', { })) .find('form:last') // WEEIRDD: the form won't submit to an iframe if the button is name="submit" (??!) - .append($('')); + .append($('')); $('#qunit-fixture').append($('', { text: 'Click me', href: '/echo', - 'data-disable-with': 'clicking...' + 'data-disable': 'true' })); }, teardown: function() { @@ -27,80 +27,62 @@ module('data-disable', { } }); -function getVal(el) { - return el.is('input,textarea,select') ? el.val() : el.text(); -} - -function disabled(el) { - return el.is('input,textarea,select,button') ? el.is(':disabled') : el.data('ujs:enable-with'); -} - -function checkEnabledState(el, text) { - ok(!disabled(el), el.get(0).tagName + ' should not be disabled'); - equal(getVal(el), text, el.get(0).tagName + ' text should be original value'); -} - -function checkDisabledState(el, text) { - ok(disabled(el), el.get(0).tagName + ' should be disabled'); - equal(getVal(el), text, el.get(0).tagName + ' text should be disabled value'); -} - -asyncTest('form input field with "data-disable-with" attribute', 7, function() { +asyncTest('form input field with "data-disable" attribute', 7, function() { var form = $('form[data-remote]'), input = form.find('input[type=text]'); - checkEnabledState(input, 'john'); + App.checkEnabledState(input, 'john'); form.bind('ajax:success', function(e, data) { setTimeout(function() { - checkEnabledState(input, 'john'); + App.checkEnabledState(input, 'john'); equal(data.params.user_name, 'john'); start(); }, 13) }) form.trigger('submit'); - checkDisabledState(input, 'processing ...'); + App.checkDisabledState(input, 'john'); }); -asyncTest('form button with "data-disable-with" attribute', 6, function() { - var form = $('form[data-remote]'), button = $(''); +asyncTest('form button with "data-disable" attribute', 6, function() { + var form = $('form[data-remote]'), button = $(''); form.append(button); - checkEnabledState(button, 'Submit'); + App.checkEnabledState(button, 'Submit'); form.bind('ajax:success', function(e, data) { setTimeout(function() { - checkEnabledState(button, 'Submit'); + App.checkEnabledState(button, 'Submit'); start(); }, 13) }) form.trigger('submit'); - checkDisabledState(button, 'submitting ...'); + App.checkDisabledState(button, 'Submit'); }); -asyncTest('form input[type=submit][data-disable-with] disables', 6, function(){ +asyncTest('form input[type=submit][data-disable] disables', 6, function(){ var form = $('form:not([data-remote])'), input = form.find('input[type=submit]'); - checkEnabledState(input, 'Submit'); + App.checkEnabledState(input, 'Submit'); // WEEIRDD: attaching this handler makes the test work in IE7 $(document).bind('iframe:loading', function(e, form) {}); $(document).bind('iframe:loaded', function(e, data) { setTimeout(function() { - checkDisabledState(input, 'submitting ...'); + App.checkDisabledState(input, 'Submit'); start(); }, 30); }); form.trigger('submit'); setTimeout(function() { - checkDisabledState(input, 'submitting ...'); + App.checkDisabledState(input, 'Submit'); }, 30); }); -asyncTest('form[data-remote] input[type=submit][data-disable-with] is replaced in ajax callback', 2, function(){ +asyncTest('form[data-remote] input[type=submit][data-disable] is replaced in ajax callback', 2, function(){ var form = $('form:not([data-remote])').attr('data-remote', 'true'), origFormContents = form.html(); form.bind('ajax:success', function(){ @@ -108,13 +90,13 @@ asyncTest('form[data-remote] input[type=submit][data-disable-with] is replaced i setTimeout(function(){ var input = form.find('input[type=submit]'); - checkEnabledState(input, 'Submit'); + App.checkEnabledState(input, 'Submit'); start(); }, 30); }).trigger('submit'); }); -asyncTest('form[data-remote] input[data-disable-with] is replaced with disabled field in ajax callback', 2, function(){ +asyncTest('form[data-remote] input[data-disable] is replaced with disabled field in ajax callback', 2, function(){ var form = $('form:not([data-remote])').attr('data-remote', 'true'), input = form.find('input[type=submit]'), newDisabledInput = input.clone().attr('disabled', 'disabled'); @@ -122,15 +104,15 @@ asyncTest('form[data-remote] input[data-disable-with] is replaced with disabled input.replaceWith(newDisabledInput); setTimeout(function(){ - checkEnabledState(newDisabledInput, 'Submit'); + App.checkEnabledState(newDisabledInput, 'Submit'); start(); }, 30); }).trigger('submit'); }); -asyncTest('form[data-remote] textarea[data-disable-with] attribute', 3, function() { +asyncTest('form[data-remote] textarea[data-disable] attribute', 3, function() { var form = $('form[data-remote]'), - textarea = $('').appendTo(form); + textarea = $('').appendTo(form); form.bind('ajax:success', function(e, data) { setTimeout(function() { @@ -140,96 +122,96 @@ asyncTest('form[data-remote] textarea[data-disable-with] attribute', 3, function }) form.trigger('submit'); - checkDisabledState(textarea, 'processing ...'); + App.checkDisabledState(textarea, 'born, lived, died.'); }); -asyncTest('a[data-disable-with] disables', 4, function() { - var link = $('a[data-disable-with]'); +asyncTest('a[data-disable] disables', 4, function() { + var link = $('a[data-disable]'); - checkEnabledState(link, 'Click me'); + App.checkEnabledState(link, 'Click me'); link.trigger('click'); - checkDisabledState(link, 'clicking...'); + App.checkDisabledState(link, 'Click me'); start(); }); -asyncTest('a[data-remote][data-disable-with] disables and re-enables', 6, function() { - var link = $('a[data-disable-with]').attr('data-remote', true); +asyncTest('a[data-remote][data-disable] disables and re-enables', 6, function() { + var link = $('a[data-disable]').attr('data-remote', true); - checkEnabledState(link, 'Click me'); + App.checkEnabledState(link, 'Click me'); link .bind('ajax:beforeSend', function() { - checkDisabledState(link, 'clicking...'); + App.checkDisabledState(link, 'Click me'); }) .bind('ajax:complete', function() { setTimeout( function() { - checkEnabledState(link, 'Click me'); + App.checkEnabledState(link, 'Click me'); start(); }, 15); }) .trigger('click'); }); -asyncTest('a[data-remote][data-disable-with] re-enables when `ajax:before` event is cancelled', 6, function() { - var link = $('a[data-disable-with]').attr('data-remote', true); +asyncTest('a[data-remote][data-disable] re-enables when `ajax:before` event is cancelled', 6, function() { + var link = $('a[data-disable]').attr('data-remote', true); - checkEnabledState(link, 'Click me'); + App.checkEnabledState(link, 'Click me'); link .bind('ajax:before', function() { - checkDisabledState(link, 'clicking...'); + App.checkDisabledState(link, 'Click me'); return false; }) .trigger('click'); setTimeout(function() { - checkEnabledState(link, 'Click me'); + App.checkEnabledState(link, 'Click me'); start(); }, 30); }); -asyncTest('a[data-remote][data-disable-with] re-enables when `ajax:beforeSend` event is cancelled', 6, function() { - var link = $('a[data-disable-with]').attr('data-remote', true); +asyncTest('a[data-remote][data-disable] re-enables when `ajax:beforeSend` event is cancelled', 6, function() { + var link = $('a[data-disable]').attr('data-remote', true); - checkEnabledState(link, 'Click me'); + App.checkEnabledState(link, 'Click me'); link .bind('ajax:beforeSend', function() { - checkDisabledState(link, 'clicking...'); + App.checkDisabledState(link, 'Click me'); return false; }) .trigger('click'); setTimeout(function() { - checkEnabledState(link, 'Click me'); + App.checkEnabledState(link, 'Click me'); start(); }, 30); }); -asyncTest('a[data-remote][data-disable-with] re-enables when `ajax:error` event is triggered', 6, function() { - var link = $('a[data-disable-with]').attr('data-remote', true).attr('href', '/error'); +asyncTest('a[data-remote][data-disable] re-enables when `ajax:error` event is triggered', 6, function() { + var link = $('a[data-disable]').attr('data-remote', true).attr('href', '/error'); - checkEnabledState(link, 'Click me'); + App.checkEnabledState(link, 'Click me'); link .bind('ajax:beforeSend', function() { - checkDisabledState(link, 'clicking...'); + App.checkDisabledState(link, 'Click me'); }) .trigger('click'); setTimeout(function() { - checkEnabledState(link, 'Click me'); + App.checkEnabledState(link, 'Click me'); start(); }, 30); }); -asyncTest('form[data-remote] input|button|textarea[data-disable-with] does not disable when `ajax:beforeSend` event is cancelled', 8, function() { +asyncTest('form[data-remote] input|button|textarea[data-disable] does not disable when `ajax:beforeSend` event is cancelled', 8, function() { var form = $('form[data-remote]'), input = form.find('input:text'), - button = $('').appendTo(form), - textarea = $('').appendTo(form), - submit = $('').appendTo(form); + button = $('').appendTo(form), + textarea = $('').appendTo(form), + submit = $('').appendTo(form); form .bind('ajax:beforeSend', function() { @@ -237,29 +219,29 @@ asyncTest('form[data-remote] input|button|textarea[data-disable-with] does not d }) .trigger('submit'); - checkEnabledState(input, 'john'); - checkEnabledState(button, 'Submit'); - checkEnabledState(textarea, 'born, lived, died.'); - checkEnabledState(submit, 'Submit'); + App.checkEnabledState(input, 'john'); + App.checkEnabledState(button, 'Submit'); + App.checkEnabledState(textarea, 'born, lived, died.'); + App.checkEnabledState(submit, 'Submit'); start(); }); asyncTest('ctrl-clicking on a link does not disables the link', 6, function() { - var link = $('a[data-disable-with]'), e; + var link = $('a[data-disable]'), e; e = $.Event('click'); e.metaKey = true; - checkEnabledState(link, 'Click me'); + App.checkEnabledState(link, 'Click me'); link.trigger(e); - checkEnabledState(link, 'Click me'); + App.checkEnabledState(link, 'Click me'); e = $.Event('click'); e.ctrlKey = true; link.trigger(e); - checkEnabledState(link, 'Click me'); + App.checkEnabledState(link, 'Click me'); start(); }); diff --git a/test/public/test/settings.js b/test/public/test/settings.js index 1eddc251..606f2900 100644 --- a/test/public/test/settings.js +++ b/test/public/test/settings.js @@ -20,6 +20,24 @@ App.assertRequestPath = function(requestEnv, path) { equal(requestEnv['PATH_INFO'], path, 'request should be sent to right url'); }; +App.getVal = function(el) { + return el.is('input,textarea,select') ? el.val() : el.text(); +}; + +App.disabled = function(el) { + return el.is('input,textarea,select,button') ? el.is(':disabled') : el.data('ujs:enable-with'); +}; + +App.checkEnabledState = function(el, text) { + ok(!App.disabled(el), el.get(0).tagName + ' should not be disabled'); + equal(App.getVal(el), text, el.get(0).tagName + ' text should be original value'); +}; + +App.checkDisabledState = function(el, text) { + ok(App.disabled(el), el.get(0).tagName + ' should be disabled'); + equal(App.getVal(el), text, el.get(0).tagName + ' text should be disabled value'); +}; + // hijacks normal form submit; lets it submit to an iframe to prevent // navigating away from the test suite $(document).bind('submit', function(e) { diff --git a/test/views/index.erb b/test/views/index.erb index 6caf6f03..f153cd12 100644 --- a/test/views/index.erb +++ b/test/views/index.erb @@ -1,6 +1,6 @@ <% @title = "jquery-ujs test" %> -<%= test 'data-confirm', 'data-remote', 'data-disable', 'call-remote', 'call-remote-callbacks', 'data-method', 'override', 'csrf-refresh' %> +<%= test 'data-confirm', 'data-remote', 'data-disable', 'data-disable-with', 'call-remote', 'call-remote-callbacks', 'data-method', 'override', 'csrf-refresh' %>

<%= @title %>

From d61894b7b17f89440b08cb0b742a2a589533124f Mon Sep 17 00:00:00 2001 From: Lucas Mazza Date: Mon, 27 Jan 2014 16:53:02 -0200 Subject: [PATCH 004/100] Tidy up `data-disable-with` test source a bit. --- test/public/test/data-disable-with.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/test/public/test/data-disable-with.js b/test/public/test/data-disable-with.js index 80a183af..116f398b 100644 --- a/test/public/test/data-disable-with.js +++ b/test/public/test/data-disable-with.js @@ -27,7 +27,6 @@ module('data-disable-with', { } }); - asyncTest('form input field with "data-disable-with" attribute', 7, function() { var form = $('form[data-remote]'), input = form.find('input[type=text]'); @@ -38,8 +37,8 @@ asyncTest('form input field with "data-disable-with" attribute', 7, function() { App.checkEnabledState(input, 'john'); equal(data.params.user_name, 'john'); start(); - }, 13) - }) + }, 13); + }); form.trigger('submit'); App.checkDisabledState(input, 'processing ...'); @@ -55,8 +54,8 @@ asyncTest('form button with "data-disable-with" attribute', 6, function() { setTimeout(function() { App.checkEnabledState(button, 'Submit'); start(); - }, 13) - }) + }, 13); + }); form.trigger('submit'); App.checkDisabledState(button, 'submitting ...'); @@ -119,8 +118,8 @@ asyncTest('form[data-remote] textarea[data-disable-with] attribute', 3, function setTimeout(function() { equal(data.params.user_bio, 'born, lived, died.'); start(); - }, 13) - }) + }, 13); + }); form.trigger('submit'); App.checkDisabledState(textarea, 'processing ...'); From f160fa2f4615f93e1a0d75e49de59d19c18c8728 Mon Sep 17 00:00:00 2001 From: Steve Schwartz Date: Wed, 29 Jan 2014 01:01:45 -0500 Subject: [PATCH 005/100] Added jquery 1.11.0 support. Removed jquery 1.7.x support. --- test/server.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/server.rb b/test/server.rb index 270999a2..3e97a1a1 100644 --- a/test/server.rb +++ b/test/server.rb @@ -1,7 +1,7 @@ require 'sinatra' require 'json' -JQUERY_VERSIONS = %w[ 1.7 1.7.1 1.7.2 1.8.0 1.8.1 1.8.2 1.8.3 1.9.0 1.9.1 1.10.0 1.10.1 ].freeze +JQUERY_VERSIONS = %w[ 1.8.0 1.8.1 1.8.2 1.8.3 1.9.0 1.9.1 1.10.0 1.10.1 1.11.0 ].freeze use Rack::Static, :urls => ["/src"], :root => File.expand_path('..', settings.root) @@ -48,7 +48,7 @@ def jquery_versions end get '/' do - params[:version] ||= '1.10.1' + params[:version] ||= '1.11.0' params[:cdn] ||= 'jquery' erb :index end From 737273923b67a1895a74abcaaa94d4289b0340e3 Mon Sep 17 00:00:00 2001 From: Steve Schwartz Date: Wed, 29 Jan 2014 01:27:11 -0500 Subject: [PATCH 006/100] Added older jquery 1.10.2 support. --- test/server.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/server.rb b/test/server.rb index 3e97a1a1..f15a91bf 100644 --- a/test/server.rb +++ b/test/server.rb @@ -1,7 +1,7 @@ require 'sinatra' require 'json' -JQUERY_VERSIONS = %w[ 1.8.0 1.8.1 1.8.2 1.8.3 1.9.0 1.9.1 1.10.0 1.10.1 1.11.0 ].freeze +JQUERY_VERSIONS = %w[ 1.8.0 1.8.1 1.8.2 1.8.3 1.9.0 1.9.1 1.10.0 1.10.1 1.10.2 1.11.0 ].freeze use Rack::Static, :urls => ["/src"], :root => File.expand_path('..', settings.root) From 980e1c5b4ba566d463a7f97bd3ac9a8bcab40ae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Sun, 16 Feb 2014 15:15:04 -0300 Subject: [PATCH 007/100] Update the jQuery supported versions --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bc488b72..d2442a5e 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Full [documentation is on the wiki][wiki], including the [list of published Ajax Requirements ------------ -- [jQuery 1.7.x or higher][jquery]; +- [jQuery 1.8.x or higher and less than 2.0][jquery]; - HTML5 doctype (optional). If you don't use HTML5, adding "data" attributes to your HTML4 or XHTML pages might make them fail [W3C markup validation][validator]. However, this shouldn't create any issues for web browsers or other user agents. From 21e128661f848d0351196218e044b1466ff25720 Mon Sep 17 00:00:00 2001 From: Christian Pekeler Date: Sun, 2 Mar 2014 16:14:27 -0700 Subject: [PATCH 008/100] added rake to Gemfile so we can actually use the tasks from Rakefile --- Gemfile | 1 + Gemfile.lock | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Gemfile b/Gemfile index 8d6aa8d1..aca39a6b 100644 --- a/Gemfile +++ b/Gemfile @@ -3,3 +3,4 @@ source 'https://rubygems.org' gem 'sinatra', '~> 1.0' gem 'shotgun', :group => :reloadable gem 'thin', :group => :reloadable +gem 'rake' diff --git a/Gemfile.lock b/Gemfile.lock index f4f2623d..aa541a84 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,6 +4,7 @@ GEM daemons (1.1.0) eventmachine (0.12.10) rack (1.2.1) + rake (10.1.1) shotgun (0.8) rack (>= 1.0) sinatra (1.1.2) @@ -19,6 +20,7 @@ PLATFORMS ruby DEPENDENCIES + rake shotgun sinatra (~> 1.0) thin From 432191b799d9e7eaf8b98e77ca08dbed24651cee Mon Sep 17 00:00:00 2001 From: Matthew O'Riordan Date: Tue, 4 Mar 2014 14:13:17 +0000 Subject: [PATCH 009/100] Include instructions for installation via Bower --- README.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d2442a5e..31b2905c 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Requirements If you don't use HTML5, adding "data" attributes to your HTML4 or XHTML pages might make them fail [W3C markup validation][validator]. However, this shouldn't create any issues for web browsers or other user agents. -Installation +Installation using the jQuery-Rails Gem ------------ For automated installation in Rails, use the "jquery-rails" gem. Place this in your Gemfile: @@ -52,6 +52,29 @@ Choose to overwrite jquery_ujs.js if prompted.* c. For Rails 2.x and for manual installation follow [this wiki](https://github.com/rails/jquery-ujs/wiki/Manual-installing-and-Rails-2) . +Installation using Bower +------------ + +Modify your bower.json file and add jQuery-UJS as a dependency as follows: + +```javascript +{ + "dependencies": { + /* include jQuery-UJS as below */ + "jquery-ujs": "git@github.com:rails/jquery-ujs.git" + } +} +``` + +Then run `bower install jquery-ujs` to install the jQuery-UJS package. + +For Rails 3.1 and above, add these lines to the top of your app/assets/javascripts/application.js file: + +```javascript +//= require jquery +//= require jquery-ujs/src/rails +``` + How to run tests ------------ From abfd48517a080bc8ed146bce8cc480cea53f9ff9 Mon Sep 17 00:00:00 2001 From: Jason Moon Date: Tue, 4 Mar 2014 15:50:44 -0600 Subject: [PATCH 010/100] Don't fire ajax:send if ajax:beforeSend was cancelled. Don't disable form elements until ajax:send. --- src/rails.js | 12 +++++++----- test/public/test/call-remote-callbacks.js | 20 +++++++++++++++++--- test/public/test/data-disable.js | 4 ++-- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/rails.js b/src/rails.js index 309d74d6..79ef1a94 100644 --- a/src/rails.js +++ b/src/rails.js @@ -129,7 +129,11 @@ if (settings.dataType === undefined) { xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script); } - return rails.fire(element, 'ajax:beforeSend', [xhr, settings]); + if (rails.fire(element, 'ajax:beforeSend', [xhr, settings])) { + element.trigger('ajax:send', xhr); + } else { + return false; + } }, success: function(data, status, xhr) { element.trigger('ajax:success', [data, status, xhr]); @@ -154,9 +158,7 @@ // Only pass url to `ajax` options if not blank if (url) { options.url = url; } - var jqxhr = rails.ajax(options); - element.trigger('ajax:send', jqxhr); - return jqxhr; + return rails.ajax(options); } else { return false; } @@ -382,7 +384,7 @@ button.closest('form').data('ujs:submit-button', data); }); - $document.delegate(rails.formSubmitSelector, 'ajax:beforeSend.rails', function(event) { + $document.delegate(rails.formSubmitSelector, 'ajax:send.rails', function(event) { if (this == event.target) rails.disableFormElements($(this)); }); diff --git a/test/public/test/call-remote-callbacks.js b/test/public/test/call-remote-callbacks.js index b4c4c7c7..fa785be9 100644 --- a/test/public/test/call-remote-callbacks.js +++ b/test/public/test/call-remote-callbacks.js @@ -9,6 +9,7 @@ module('call-remote-callbacks', { teardown: function() { $(document).undelegate('form[data-remote]', 'ajax:beforeSend'); $(document).undelegate('form[data-remote]', 'ajax:before'); + $(document).undelegate('form[data-remote]', 'ajax:send'); $(document).undelegate('form[data-remote]', 'ajax:complete'); $(document).undelegate('form[data-remote]', 'ajax:success'); $(document).unbind('ajaxStop'); @@ -97,6 +98,9 @@ asyncTest('stopping the "ajax:beforeSend" event aborts the request', 1, function ok(true, 'aborting request in ajax:beforeSend'); return false; }); + form.unbind('ajax:send').bind('ajax:send', function() { + ok(false, 'ajax:send should not run'); + }); form.unbind('ajax:complete').bind('ajax:complete', function() { ok(false, 'ajax:complete should not run'); }); @@ -315,6 +319,9 @@ asyncTest('"ajax:beforeSend" can be observed and stopped with event delegation', }); submit(function(form) { + form.unbind('ajax:send').bind('ajax:send', function() { + ok(false, 'ajax:send should not run'); + }); form.unbind('ajax:complete').bind('ajax:complete', function() { ok(false, 'ajax:complete should not run'); }); @@ -324,12 +331,15 @@ asyncTest('"ajax:beforeSend" can be observed and stopped with event delegation', }); }); -asyncTest('"ajax:beforeSend", "ajax:success" and "ajax:complete" are triggered', 8, function() { +asyncTest('"ajax:beforeSend", "ajax:send", "ajax:success" and "ajax:complete" are triggered', 9, function() { submit(function(form) { form.bind('ajax:beforeSend', function(e, xhr, settings) { ok(xhr.setRequestHeader, 'first argument to "ajax:beforeSend" should be an XHR object'); equal(settings.url, '/echo', 'second argument to "ajax:beforeSend" should be a settings object'); }); + form.bind('ajax:send', function(e, xhr) { + ok(xhr.abort, 'first argument to "ajax:send" should be an XHR object'); + }); form.bind('ajax:success', function(e, data, status, xhr) { ok(data.REQUEST_METHOD, 'first argument to ajax:success should be a data object'); equal(status, 'success', 'second argument to ajax:success should be a status string'); @@ -342,10 +352,11 @@ asyncTest('"ajax:beforeSend", "ajax:success" and "ajax:complete" are triggered', }); }); -asyncTest('"ajax:beforeSend", "ajax:error" and "ajax:complete" are triggered on error', 6, function() { +asyncTest('"ajax:beforeSend", "ajax:send", "ajax:error" and "ajax:complete" are triggered on error', 7, function() { submit(function(form) { form.attr('action', '/error'); form.bind('ajax:beforeSend', function(arg) { ok(true, 'ajax:beforeSend') }); + form.bind('ajax:send', function(arg) { ok(true, 'ajax:send') }); form.bind('ajax:error', function(e, xhr, status, error) { ok(xhr.getResponseHeader, 'first argument to "ajax:error" should be an XHR object'); equal(status, 'error', 'second argument to ajax:error should be a status string'); @@ -358,11 +369,14 @@ asyncTest('"ajax:beforeSend", "ajax:error" and "ajax:complete" are triggered on }); // IF THIS TEST IS FAILING, TRY INCREASING THE TIMEOUT AT THE BOTTOM TO > 100 -asyncTest('binding to ajax callbacks via .delegate() triggers handlers properly', 3, function() { +asyncTest('binding to ajax callbacks via .delegate() triggers handlers properly', 4, function() { $(document) .delegate('form[data-remote]', 'ajax:beforeSend', function() { ok(true, 'ajax:beforeSend handler is triggered'); }) + .delegate('form[data-remote]', 'ajax:send', function() { + ok(true, 'ajax:send handler is triggered'); + }) .delegate('form[data-remote]', 'ajax:complete', function() { ok(true, 'ajax:complete handler is triggered'); }) diff --git a/test/public/test/data-disable.js b/test/public/test/data-disable.js index ec858787..b73b3035 100644 --- a/test/public/test/data-disable.js +++ b/test/public/test/data-disable.js @@ -159,7 +159,7 @@ asyncTest('a[data-remote][data-disable-with] disables and re-enables', 6, functi checkEnabledState(link, 'Click me'); link - .bind('ajax:beforeSend', function() { + .bind('ajax:send', function() { checkDisabledState(link, 'clicking...'); }) .bind('ajax:complete', function() { @@ -213,7 +213,7 @@ asyncTest('a[data-remote][data-disable-with] re-enables when `ajax:error` event checkEnabledState(link, 'Click me'); link - .bind('ajax:beforeSend', function() { + .bind('ajax:send', function() { checkDisabledState(link, 'clicking...'); }) .trigger('click'); From 7886f4f494937b0baad126b36cb9a217cb672593 Mon Sep 17 00:00:00 2001 From: Paulo Pereira Date: Wed, 5 Mar 2014 19:12:16 +0100 Subject: [PATCH 011/100] Add :enabled to disableSelector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensures elements aren’t disabled twice. --- src/rails.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rails.js b/src/rails.js index 309d74d6..f6f1e2af 100644 --- a/src/rails.js +++ b/src/rails.js @@ -37,7 +37,7 @@ formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type])', // Form input elements disabled during form submission - disableSelector: 'input[data-disable-with], button[data-disable-with], textarea[data-disable-with]', + disableSelector: 'input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled', // Form input elements re-enabled after form submission enableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled', From 18769f566103dbe867108a24c5adab911d7624ea Mon Sep 17 00:00:00 2001 From: Thomas Walpole Date: Wed, 5 Mar 2014 12:32:43 -0800 Subject: [PATCH 012/100] support use of disable-with on elements using the "form" attribute --- src/rails.js | 7 +++++-- test/public/test/data-disable.js | 32 +++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/rails.js b/src/rails.js index 309d74d6..60de2d2d 100644 --- a/src/rails.js +++ b/src/rails.js @@ -189,7 +189,8 @@ - Sets disabled property to true */ disableFormElements: function(form) { - form.find(rails.disableSelector).each(function() { + var elements = form.is('form') ? $(form[0].elements).filter(rails.disableSelector) : form.find(rails.disableSelector); + elements.each(function() { var element = $(this), method = element.is('button') ? 'html' : 'val'; element.data('ujs:enable-with', element[method]()); element[method](element.data('disable-with')); @@ -202,7 +203,9 @@ - Sets disabled property to false */ enableFormElements: function(form) { - form.find(rails.enableSelector).each(function() { + var elements = form.is('form') ? $(form[0].elements).filter(rails.enableSelector) : form.find(rails.enableSelector); + elements.each(function() { + // form.find(rails.enableSelector).each(function() { var element = $(this), method = element.is('button') ? 'html' : 'val'; if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with')); element.prop('disabled', false); diff --git a/test/public/test/data-disable.js b/test/public/test/data-disable.js index ec858787..0f579506 100644 --- a/test/public/test/data-disable.js +++ b/test/public/test/data-disable.js @@ -10,7 +10,8 @@ module('data-disable', { $('#qunit-fixture').append($('', { action: '/echo', - method: 'post' + method: 'post', + id: 'not_remote' })) .find('form:last') // WEEIRDD: the form won't submit to an iframe if the button is name="submit" (??!) @@ -21,6 +22,14 @@ module('data-disable', { href: '/echo', 'data-disable-with': 'clicking...' })); + + $('#qunit-fixture').append($('', { + type: 'submit', + form: 'not_remote', + 'data-disable-with': 'form attr submitting', + name: 'submit3', + value: 'Form Attr Submit' + })); }, teardown: function() { $(document).unbind('iframe:loaded'); @@ -100,6 +109,27 @@ asyncTest('form input[type=submit][data-disable-with] disables', 6, function(){ }, 30); }); +asyncTest('form input[type=submit][data-disable-with] using "form" attribute disables', 6, function() { + var form = $('#not_remote'), input = $('input[form=not_remote]'); + checkEnabledState(input, 'Form Attr Submit'); + + // WEEIRDD: attaching this handler makes the test work in IE7 + $(document).bind('iframe:loading', function(e, form) {}); + + $(document).bind('iframe:loaded', function(e, data) { + setTimeout(function() { + checkDisabledState(input, 'form attr submitting'); + start(); + }, 30); + }); + form.trigger('submit'); + + setTimeout(function() { + checkDisabledState(input, 'form attr submitting'); + }, 30); + +}); + asyncTest('form[data-remote] input[type=submit][data-disable-with] is replaced in ajax callback', 2, function(){ var form = $('form:not([data-remote])').attr('data-remote', 'true'), origFormContents = form.html(); From d3b65a4c38d8e9f1ecf509c93b622c7b7df56ee0 Mon Sep 17 00:00:00 2001 From: Thomas Walpole Date: Wed, 5 Mar 2014 13:22:07 -0800 Subject: [PATCH 013/100] extraneous commented line --- src/rails.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rails.js b/src/rails.js index 60de2d2d..08919b95 100644 --- a/src/rails.js +++ b/src/rails.js @@ -205,7 +205,6 @@ enableFormElements: function(form) { var elements = form.is('form') ? $(form[0].elements).filter(rails.enableSelector) : form.find(rails.enableSelector); elements.each(function() { - // form.find(rails.enableSelector).each(function() { var element = $(this), method = element.is('button') ? 'html' : 'val'; if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with')); element.prop('disabled', false); From ed1fb395e98055e948071b18c360a0843c7d1b06 Mon Sep 17 00:00:00 2001 From: Lucas Mazza Date: Tue, 11 Mar 2014 22:52:43 -0300 Subject: [PATCH 014/100] Trim empty newlines --- test/public/test/data-disable-with.js | 1 - test/public/test/data-disable.js | 1 - 2 files changed, 2 deletions(-) diff --git a/test/public/test/data-disable-with.js b/test/public/test/data-disable-with.js index 116f398b..39a30172 100644 --- a/test/public/test/data-disable-with.js +++ b/test/public/test/data-disable-with.js @@ -225,7 +225,6 @@ asyncTest('form[data-remote] input|button|textarea[data-disable-with] does not d App.checkEnabledState(submit, 'Submit'); start(); - }); asyncTest('ctrl-clicking on a link does not disables the link', 6, function() { diff --git a/test/public/test/data-disable.js b/test/public/test/data-disable.js index 8d070352..60b8fd42 100644 --- a/test/public/test/data-disable.js +++ b/test/public/test/data-disable.js @@ -225,7 +225,6 @@ asyncTest('form[data-remote] input|button|textarea[data-disable] does not disabl App.checkEnabledState(submit, 'Submit'); start(); - }); asyncTest('ctrl-clicking on a link does not disables the link', 6, function() { From be6f905231b5c8163fcae2d8ff39590351d0be14 Mon Sep 17 00:00:00 2001 From: Lucas Mazza Date: Tue, 11 Mar 2014 23:13:34 -0300 Subject: [PATCH 015/100] Add support for `data-confirm` attribute on `button` elements. This should be supported as the `button_to` rails helper produces the expected `data-*` attributes. Closes #352. --- src/rails.js | 2 +- test/public/test/data-confirm.js | 96 +++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 3 deletions(-) diff --git a/src/rails.js b/src/rails.js index 79ef1a94..8919aa4d 100644 --- a/src/rails.js +++ b/src/rails.js @@ -25,7 +25,7 @@ linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote], a[data-disable-with]', // Button elements bound by jquery-ujs - buttonClickSelector: 'button[data-remote]', + buttonClickSelector: 'button[data-remote], button[data-confirm]', // Select elements bound by jquery-ujs inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]', diff --git a/test/public/test/data-confirm.js b/test/public/test/data-confirm.js index 50735e17..cf95b20b 100644 --- a/test/public/test/data-confirm.js +++ b/test/public/test/data-confirm.js @@ -7,6 +7,13 @@ module('data-confirm', { text: 'my social security number' })); + $('#qunit-fixture').append($('