From 8fc44057482460793052d8ff2e0ccb74448dd702 Mon Sep 17 00:00:00 2001 From: Krzysztof Kotlarski Date: Fri, 5 Dec 2014 14:29:53 +0100 Subject: [PATCH 01/52] Add support form HTML5 "formaction" and "formmethod" attributes in remote forms --- src/rails.js | 11 ++++++++--- test/public/test/call-remote.js | 26 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/rails.js b/src/rails.js index c0f45238..3bec1074 100644 --- a/src/rails.js +++ b/src/rails.js @@ -100,8 +100,8 @@ dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType); if (element.is('form')) { - method = element.attr('method'); - url = element.attr('action'); + method = element.data('ujs:submit-button-formmethod') || element.attr('method'); + url = element.data('ujs:submit-button-formaction') || element.attr('action'); data = element.serializeArray(); // memoized value from clicked submit button var button = element.data('ujs:submit-button'); @@ -109,6 +109,8 @@ data.push(button); element.data('ujs:submit-button', null); } + element.data('ujs:submit-button-formmethod', null) + element.data('ujs:submit-button-formaction', null) } else if (element.is(rails.inputChangeSelector)) { method = element.data('method'); url = element.data('url'); @@ -450,7 +452,10 @@ var name = button.attr('name'), data = name ? {name:name, value:button.val()} : null; - button.closest('form').data('ujs:submit-button', data); + button.closest('form') + .data('ujs:submit-button', data) + .data('ujs:submit-button-formaction', button.attr('formaction')) + .data('ujs:submit-button-formmethod', button.attr('formmethod')); }); $document.delegate(rails.formSubmitSelector, 'ajax:send.rails', function(event) { diff --git a/test/public/test/call-remote.js b/test/public/test/call-remote.js index d78ce565..8bd09bc2 100644 --- a/test/public/test/call-remote.js +++ b/test/public/test/call-remote.js @@ -32,6 +32,19 @@ asyncTest('form method is not read from "data-method" attribute in case of missi }); }); +asyncTest('form method is read from submit button "formmethod" if submit is triggered by that button', 1, function() { + var submitButton = $('') + buildForm({ method: 'post' }); + + $('#qunit-fixture').find('form').append(submitButton) + .bind('ajax:success', function(e, data, status, xhr) { + App.assertGetRequest(data); + }) + .bind('ajax:complete', function() { start() }); + + submitButton.trigger('click'); +}); + asyncTest('form default method is GET', 1, function() { buildForm(); @@ -56,6 +69,19 @@ asyncTest('form url is read from "action" not "href"', 1, function() { }); }); +asyncTest('form url is read from submit button "formaction" if submit is triggered by that button', 1, function() { + var submitButton = $('') + buildForm({ method: 'post', href: '/echo2' }); + + $('#qunit-fixture').find('form').append(submitButton) + .bind('ajax:success', function(e, data, status, xhr) { + App.assertRequestPath(data, '/echo'); + }) + .bind('ajax:complete', function() { start() }); + + submitButton.trigger('click'); +}); + asyncTest('prefer JS, but accept any format', 1, function() { buildForm({ method: 'post' }); From 96561a2ac0fab58e3e248458e19003e09f106ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 25 Mar 2015 17:23:33 -0300 Subject: [PATCH 02/52] Properly check if the request is cross domain Fix CVE-2015-1840 --- src/rails.js | 31 +++++++++++++++++----- test/public/test/call-remote-callbacks.js | 14 ---------- test/public/test/call-remote.js | 32 ----------------------- test/public/test/data-method.js | 26 ++++++++++++++++++ test/public/test/override.js | 2 +- 5 files changed, 52 insertions(+), 53 deletions(-) diff --git a/src/rails.js b/src/rails.js index a4fb0bed..1ee8859e 100644 --- a/src/rails.js +++ b/src/rails.js @@ -86,16 +86,14 @@ // Default way to get an element's href. May be overridden at $.rails.href. href: function(element) { - return element.attr('href'); + return element[0].href; }, // Submits "remote" forms and links with ajax handleRemote: function(element) { - var method, url, data, elCrossDomain, crossDomain, withCredentials, dataType, options; + var method, url, data, withCredentials, dataType, options; if (rails.fire(element, 'ajax:before')) { - elCrossDomain = element.data('cross-domain'); - crossDomain = elCrossDomain === undefined ? null : elCrossDomain; withCredentials = element.data('with-credentials') || null; dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType); @@ -147,7 +145,7 @@ error: function(xhr, status, error) { element.trigger('ajax:error', [xhr, status, error]); }, - crossDomain: crossDomain + crossDomain: rails.isCrossDomain(url) }; // There is no withCredentials for IE6-8 when @@ -167,6 +165,27 @@ } }, + // Determines if the request is a cross domain request. + isCrossDomain: function(url) { + var originAnchor = document.createElement("a"); + originAnchor.href = location.href; + var urlAnchor = document.createElement("a"); + + try { + urlAnchor.href = url; + // This is a workaround to a IE bug. + urlAnchor.href = urlAnchor.href; + + // Make sure that the browser parses the URL and that the protocols and hosts match. + return !urlAnchor.protocol || !urlAnchor.host || + (originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host); + } catch (e) { + // If there is an error parsing the URL, assume it is crossDomain. + return true; + } + }, + // Handles "data-method" on links such as: // Delete handleMethod: function(link) { @@ -178,7 +197,7 @@ form = $('
'), metadataInput = ''; - if (csrfParam !== undefined && csrfToken !== undefined) { + if (csrfParam !== undefined && csrfToken !== undefined && !rails.isCrossDomain(href)) { metadataInput += ''; } diff --git a/test/public/test/call-remote-callbacks.js b/test/public/test/call-remote-callbacks.js index c1791f6b..ad306a3e 100644 --- a/test/public/test/call-remote-callbacks.js +++ b/test/public/test/call-remote-callbacks.js @@ -64,20 +64,6 @@ asyncTest('modifying data("type") with "ajax:before" requests new dataType in re }); }); -asyncTest('setting data("cross-domain",true) with "ajax:before" uses new setting in request', 2, function(){ - $('form[data-remote]').data('cross-domain',false) - .bind('ajax:before', function() { - var form = $(this); - form.data('cross-domain',true); - }); - - submit(function(form) { - form.bind('ajax:beforeSend', function(e, xhr, settings) { - equal(settings.crossDomain, true, 'setting modified in ajax:before should have forced cross-domain request'); - }); - }); -}); - asyncTest('setting data("with-credentials",true) with "ajax:before" uses new setting in request', 2, function(){ $('form[data-remote]').data('with-credentials',false) .bind('ajax:before', function() { diff --git a/test/public/test/call-remote.js b/test/public/test/call-remote.js index d78ce565..94316e88 100644 --- a/test/public/test/call-remote.js +++ b/test/public/test/call-remote.js @@ -122,22 +122,6 @@ asyncTest('sends CSRF token in custom header', 1, function() { }); }); -asyncTest('does not send CSRF token in custom header if crossDomain', 1, function() { - buildForm({ 'data-cross-domain': 'true' }); - $('#qunit-fixture').append(''); - - // Manually set request header to be XHR, since setting crossDomain: true in .ajax() - // causes jQuery to skip setting the request header, to prevent our test/server.rb from - // raising an an error (when request.xhr? is false). - $('#qunit-fixture').find('form').bind('ajax:beforeSend', function(e, xhr) { - xhr.setRequestHeader('X-Requested-With', "XMLHttpRequest"); - }); - - submit(function(e, data, status, xhr) { - equal(data.HTTP_X_CSRF_TOKEN, undefined, 'X-CSRF-Token header should NOT be sent'); - }); -}); - asyncTest('intelligently guesses crossDomain behavior when target URL is a different domain', 1, function(e, xhr) { // Don't set data-cross-domain here, just set action to be a different domain than localhost @@ -156,20 +140,4 @@ asyncTest('intelligently guesses crossDomain behavior when target URL is a diffe setTimeout(function() { start(); }, 13); }); - -asyncTest('does not set crossDomain if explicitly set to false on element', 1, function() { - buildForm({ action: 'http://www.alfajango.com', 'data-cross-domain': false }); - $('#qunit-fixture').append(''); - - $('#qunit-fixture').find('form') - .bind('ajax:beforeSend', function(e, xhr, settings) { - equal(settings.crossDomain, false, 'crossDomain should be set to false'); - // prevent request from actually getting sent off-domain - return false; - }) - .trigger('submit'); - - setTimeout(function() { start(); }, 13); -}); - })(); diff --git a/test/public/test/data-method.js b/test/public/test/data-method.js index c4426624..57528377 100644 --- a/test/public/test/data-method.js +++ b/test/public/test/data-method.js @@ -46,4 +46,30 @@ asyncTest('link "target" should be carried over to generated form', 1, function( }); }); +asyncTest('link with "data-method" and cross origin', 1, function() { + var data = {}; + + $('#qunit-fixture') + .append('') + .append(''); + + $(document).on('submit', 'form', function(e) { + $(e.currentTarget).serializeArray().map(function(item) { + data[item.name] = item.value; + }); + + return false; + }); + + var link = $('#qunit-fixture').find('a'); + + link.attr('href', 'http://www.alfajango.com'); + + link.trigger('click'); + + start(); + + notEqual(data.authenticity_token, 'cf50faa3fe97702ca1ae'); +}); + })(); diff --git a/test/public/test/override.js b/test/public/test/override.js index ba84b6dc..0dca60cc 100644 --- a/test/public/test/override.js +++ b/test/public/test/override.js @@ -32,7 +32,7 @@ asyncTest("the getter for an element's href is overridable", 1, function() { asyncTest("the getter for an element's href works normally if not overridden", 1, function() { $.rails.ajax = function(options) { - equal('/real/href', options.url); + equal(location.protocol + '//' + location.host + '/real/href', options.url); } $.rails.handleRemote($('#qunit-fixture').find('a')); start(); From c50b05ab46a655a32c0ee348576d2465e183f9de Mon Sep 17 00:00:00 2001 From: "M. Saiqul Haq" Date: Fri, 3 Apr 2015 23:43:49 +0700 Subject: [PATCH 03/52] Refactor handleMethod method Replacing old code with new method (csrfToken, and csrfParam) --- src/rails.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rails.js b/src/rails.js index 211c7226..39cf9158 100644 --- a/src/rails.js +++ b/src/rails.js @@ -181,8 +181,8 @@ var href = rails.href(link), method = link.data('method'), target = link.attr('target'), - csrfToken = $('meta[name=csrf-token]').attr('content'), - csrfParam = $('meta[name=csrf-param]').attr('content'), + csrfToken = rails.csrfToken(), + csrfParam = rails.csrfParam(), form = $('
'), metadataInput = ''; From f042bf886efc59c36fb9cdb7b878bdb6fb285600 Mon Sep 17 00:00:00 2001 From: Pier-Olivier Thibault Date: Fri, 17 Apr 2015 11:39:15 -0400 Subject: [PATCH 04/52] Links with [disabled] attribute set will not trigger AJAX --- src/rails.js | 2 +- test/public/test/data-remote.js | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/rails.js b/src/rails.js index 39cf9158..94fda15e 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], a[data-disable]', + linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]', // Button elements bound by jquery-ujs buttonClickSelector: 'button[data-remote]:not(form button), button[data-confirm]:not(form button)', diff --git a/test/public/test/data-remote.js b/test/public/test/data-remote.js index f8c6b2a8..f678f0bb 100644 --- a/test/public/test/data-remote.js +++ b/test/public/test/data-remote.js @@ -18,6 +18,12 @@ module('data-remote', { 'data-remote': 'true', method: 'post' })) + .append($('', { + href: '/echo', + 'data-remote': 'true', + disabled: 'disabled', + text: 'Disabed link' + })) .find('form').append($('')); } @@ -84,6 +90,19 @@ asyncTest('clicking on a link with data-remote attribute', 5, function() { .trigger('click'); }); +asyncTest('clicking on a link with disabled attribute', 0, function() { + $('a[disabled]') + .bind("ajax:before", function(e, data, status, xhr) { + App.assertCallbackNotInvoked('ajax:success') + }) + .bind('ajax:complete', function() { start() }) + .trigger('click') + + setTimeout(function() { + start(); + }, 13); +}); + asyncTest('clicking on a button with data-remote attribute', 5, function() { $('button[data-remote]') .bind('ajax:success', function(e, data, status, xhr) { From 9aa01e157af0efa1b49ccc2b8f8bd350d8e629a8 Mon Sep 17 00:00:00 2001 From: Can Edremitoglu Date: Sun, 3 May 2015 04:08:15 +0200 Subject: [PATCH 05/52] change wrong filename --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 29ea432d..41d050f5 100644 --- a/README.md +++ b/README.md @@ -50,11 +50,11 @@ Run `bower install jquery-ujs --save` to install the jquery-ujs package. Usage ------------ -Require both `jquery` and `jquery-ujs` into your application.js manifest. +Require both `jquery` and `jquery_ujs` into your application.js manifest. ```javascript //= require jquery -//= require jquery-ujs +//= require jquery_ujs ``` How to run tests From 77567e773fed2ebd33f875b531589b2babe0dc37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Sun, 3 May 2015 20:28:42 -0300 Subject: [PATCH 06/52] Revert "Merge pull request #415 from cantonic/patch-1" This reverts commit ef734cc4e9fdbbdb3bb7800b327834fb02fe65bc, reversing changes made to 75f7094dc6827585e0ab19d8777b9133b69895af. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 41d050f5..29ea432d 100644 --- a/README.md +++ b/README.md @@ -50,11 +50,11 @@ Run `bower install jquery-ujs --save` to install the jquery-ujs package. Usage ------------ -Require both `jquery` and `jquery_ujs` into your application.js manifest. +Require both `jquery` and `jquery-ujs` into your application.js manifest. ```javascript //= require jquery -//= require jquery_ujs +//= require jquery-ujs ``` How to run tests From 14b5763e73eb96f3068e004c87d5579c0b89f565 Mon Sep 17 00:00:00 2001 From: Alex Ianus Date: Mon, 4 May 2015 17:09:28 -0400 Subject: [PATCH 07/52] Add test for when $.rails.confirm throws --- test/public/test/data-confirm.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/public/test/data-confirm.js b/test/public/test/data-confirm.js index da040b9a..8add713c 100644 --- a/test/public/test/data-confirm.js +++ b/test/public/test/data-confirm.js @@ -117,6 +117,27 @@ asyncTest('clicking on a button with data-confirm attribute. Confirm No.', 3, fu }, 50); }); +asyncTest('clicking on a button with data-confirm attribute. Confirm error.', 3, function() { + var message; + // auto-decline: + window.confirm = function(msg) { message = msg; throw "some random error"; }; + + $('button[data-confirm]') + .bind('confirm:complete', function(e, data) { + App.assertCallbackInvoked('confirm:complete'); + ok(data == false, 'confirm:complete passes in confirm answer (false)'); + }) + .bind('ajax:beforeSend', function(e, data, status, xhr) { + App.assertCallbackNotInvoked('ajax:beforeSend'); + }) + .trigger('click'); + + setTimeout(function() { + equal(message, 'Are you absolutely sure?'); + start(); + }, 50); +}); + asyncTest('clicking on a submit button with form and data-confirm attributes. Confirm No.', 3, function() { var message; // auto-decline: From ad97eac726cc80272324420d4ae8829e445dfd5e Mon Sep 17 00:00:00 2001 From: Alex Ianus Date: Mon, 4 May 2015 16:56:38 -0400 Subject: [PATCH 08/52] Treat exception in $.rails.confirm as a false answer --- src/rails.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/rails.js b/src/rails.js index 39cf9158..67a55003 100644 --- a/src/rails.js +++ b/src/rails.js @@ -260,7 +260,11 @@ if (!message) { return true; } if (rails.fire(element, 'confirm')) { - answer = rails.confirm(message); + try { + answer = rails.confirm(message); + } catch (e) { + (console.error || console.log).call(console, e.stack || e); + } callback = rails.fire(element, 'confirm:complete', [answer]); } return answer && callback; From 0b195730213c54c363546a309ae41d9d80d621a3 Mon Sep 17 00:00:00 2001 From: Kevin Kirsche Date: Thu, 11 Jun 2015 20:56:49 -0400 Subject: [PATCH 09/52] Remove moot `version` property from bower.json Per bower/bower.json-spec@a325da3 Also their maintainer says they probably won't ever use it: http://stackoverflow.com/questions/24844901/bowers-bower-json-file-version-property --- bower.json | 1 - 1 file changed, 1 deletion(-) diff --git a/bower.json b/bower.json index 9b5d5df6..14f93cf3 100644 --- a/bower.json +++ b/bower.json @@ -5,7 +5,6 @@ "description": "Ruby on Rails unobtrusive scripting adapter for jQuery", "main": "src/rails.js", "license": "MIT", - "version": "1.0.3", "dependencies": { "jquery": ">1.8.*" }, From 7146dc96a71fda9b713f1f60fb414c699db0b3a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Tue, 16 Jun 2015 12:58:24 -0300 Subject: [PATCH 10/52] Release 1.0.4 --- CHANGELOG.md | 8 ++++++++ bower.json | 1 - 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e4116fb..2a90470c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## v1.0.4 + +* Fix CSP bypass vulnerability. + + CVE-2015-1840. + + *Rafael Mendonça França* + ## v1.0.3 * Replace deprecated `deferred.error()` with `fail()`. diff --git a/bower.json b/bower.json index 9b5d5df6..14f93cf3 100644 --- a/bower.json +++ b/bower.json @@ -5,7 +5,6 @@ "description": "Ruby on Rails unobtrusive scripting adapter for jQuery", "main": "src/rails.js", "license": "MIT", - "version": "1.0.3", "dependencies": { "jquery": ">1.8.*" }, From 86625cf5a4873330d6ced6f40befea64d76ff0f7 Mon Sep 17 00:00:00 2001 From: Alexander Kaupanin Date: Fri, 20 Sep 2013 16:11:56 +0400 Subject: [PATCH 11/52] Don't fire ajaxyness if "data-remote"="false", because `jQuery("#my_id").data("remote") === false` if input's data-remote is set to string "false" (``) --- src/rails.js | 13 +++-- test/public/test/data-remote.js | 93 +++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 4 deletions(-) diff --git a/src/rails.js b/src/rails.js index 152391bf..b8636b96 100644 --- a/src/rails.js +++ b/src/rails.js @@ -97,6 +97,11 @@ return element[0].href; }, + // Checks "data-remote" if true to handle the request through a XHR request. + isRemote: function(element) { + return element.data('remote') !== undefined && element.data('remote') !== false; + }, + // Submits "remote" forms and links with ajax handleRemote: function(element) { var method, url, data, withCredentials, dataType, options; @@ -390,7 +395,7 @@ if (!metaClick && link.is(rails.linkDisableSelector)) rails.disableElement(link); - if (link.data('remote') !== undefined) { + if (rails.isRemote(link)) { if (metaClick && (!method || method === 'GET') && !data) { return true; } var handleRemote = rails.handleRemote(link); @@ -411,7 +416,7 @@ $document.delegate(rails.buttonClickSelector, 'click.rails', function(e) { var button = $(this); - if (!rails.allowAction(button)) return rails.stopEverything(e); + if (!rails.allowAction(button) || !rails.isRemote(button)) return rails.stopEverything(e); if (button.is(rails.buttonDisableSelector)) rails.disableFormElement(button); @@ -427,7 +432,7 @@ $document.delegate(rails.inputChangeSelector, 'change.rails', function(e) { var link = $(this); - if (!rails.allowAction(link)) return rails.stopEverything(e); + if (!rails.allowAction(link) || !rails.isRemote(link)) return rails.stopEverything(e); rails.handleRemote(link); return false; @@ -435,7 +440,7 @@ $document.delegate(rails.formSubmitSelector, 'submit.rails', function(e) { var form = $(this), - remote = form.data('remote') !== undefined, + remote = rails.isRemote(form), blankRequiredInputs, nonBlankFileInputs; diff --git a/test/public/test/data-remote.js b/test/public/test/data-remote.js index f8c6b2a8..c0b47586 100644 --- a/test/public/test/data-remote.js +++ b/test/public/test/data-remote.js @@ -186,3 +186,96 @@ asyncTest('returning false in form\'s submit bindings in non-submit-bubbling bro setTimeout(function(){ start(); }, 13); }); + +asyncTest('clicking on a link with falsy "data-remote" attribute does not fire ajaxyness', 0, function() { + $('a[data-remote]') + .attr('data-remote', 'false') + .bind('ajax:beforeSend', function() { + ok(false, 'ajax should not be triggered'); + }) + .bind('click', function() { + return false; + }) + .trigger('click'); + + setTimeout(function(){ start(); }, 20); +}); + +asyncTest('ctrl-clicking on a link with falsy "data-remote" attribute does not fire ajaxyness even if "data-params" present', 0, function() { + var link = $('a[data-remote]'), e; + e = $.Event('click'); + e.metaKey = true; + + link + .removeAttr('data-params') + .attr('data-remote', 'false') + .attr('data-method', 'POST') + .bind('ajax:beforeSend', function() { + ok(false, 'ajax should not be triggered'); + }) + .bind('click', function() { + return false; + }) + .trigger(e); + + e = $.Event('click'); + e.metaKey = true; + + link + .removeAttr('data-method') + .attr('data-params', 'name=steve') + .trigger(e); + + setTimeout(function(){ start(); }, 20); +}); + +asyncTest('clicking on a button with falsy "data-remote" attribute', 0, function() { + $('button[data-remote]:first') + .attr('data-remote', 'false') + .bind('ajax:beforeSend', function() { + ok(false, 'ajax should not be triggered'); + }) + .bind('click', function() { + return false; + }) + .trigger('click'); + + setTimeout(function(){ start(); }, 20); +}); + +asyncTest('submitting a form with falsy "data-remote" attribute', 0, function() { + $('form[data-remote]:first') + .attr('data-remote', 'false') + .bind('ajax:beforeSend', function() { + ok(false, 'ajax should not be triggered'); + }) + .bind('submit', function() { + return false; + }) + .trigger('submit'); + + setTimeout(function(){ start(); }, 20); +}); + +asyncTest('changing a select option with falsy "data-remote" attribute', 0, function() { + $('form') + .append( + $('')); @@ -135,6 +136,51 @@ asyncTest('submitting form with data-remote attribute', 4, function() { .trigger('submit'); }); +asyncTest('submitting form with data-remote attribute submits input with matching [form] attribute', 5, function() { + $('#qunit-fixture') + .append($('')); + + $('form[data-remote]') + .bind('ajax:success', function(e, data, status, xhr) { + App.assertCallbackInvoked('ajax:success'); + App.assertRequestPath(data, '/echo'); + equal(data.params.user_name, 'john', 'ajax arguments should have key user_name with right value'); + equal(data.params.user_data, 'value1', 'ajax arguments should have key user_data with right value'); + App.assertPostRequest(data); + }) + .bind('ajax:complete', function() { start() }) + .trigger('submit'); +}); + +asyncTest('submitting form with data-remote attribute by clicking button with matching [form] attribute', 5, function() { + $('form[data-remote]') + .bind('ajax:success', function(e, data, status, xhr) { + App.assertCallbackInvoked('ajax:success'); + App.assertRequestPath(data, '/echo'); + equal(data.params.user_name, 'john', 'ajax arguments should have key user_name with right value'); + equal(data.params.user_data, 'value2', 'ajax arguments should have key user_data with right value'); + App.assertPostRequest(data); + }) + .bind('ajax:complete', function() { start() }); + + $(''); form.append(button); @@ -66,6 +66,7 @@ asyncTest('form button with "data-disable" attribute', 6, function() { form.trigger('submit'); App.checkDisabledState(button, 'Submit'); + equal(button.data('ujs:enable-with'), undefined); }); asyncTest('form input[type=submit][data-disable] disables', 6, function(){ @@ -132,13 +133,14 @@ asyncTest('form[data-remote] textarea[data-disable] attribute', 3, function() { App.checkDisabledState(textarea, 'born, lived, died.'); }); -asyncTest('a[data-disable] disables', 4, function() { +asyncTest('a[data-disable] disables', 5, function() { var link = $('a[data-disable]'); App.checkEnabledState(link, 'Click me'); link.trigger('click'); App.checkDisabledState(link, 'Click me'); + equal(link.data('ujs:enable-with'), undefined); start(); }); diff --git a/test/public/test/settings.js b/test/public/test/settings.js index 85a33ac1..6fbbdab3 100644 --- a/test/public/test/settings.js +++ b/test/public/test/settings.js @@ -25,7 +25,7 @@ App.getVal = function(el) { }; App.disabled = function(el) { - return el.is('input,textarea,select,button') ? el.is(':disabled') : el.data('ujs:enable-with'); + return el.is('input,textarea,select,button') ? (el.is(':disabled') && el.data('ujs:disabled')) : el.data('ujs:disabled'); }; App.checkEnabledState = function(el, text) { From 709b59b7001ef8a5d311a1c99606c5125323d0a2 Mon Sep 17 00:00:00 2001 From: "Harry V. Kiselev" Date: Mon, 7 Dec 2015 01:59:17 +0300 Subject: [PATCH 31/52] Update rails.js Add space into requiredInputSelector selector. --- src/rails.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rails.js b/src/rails.js index e9ac784e..59f35e86 100644 --- a/src/rails.js +++ b/src/rails.js @@ -45,7 +45,7 @@ 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])', + requiredInputSelector: 'input[name][required]:not([disabled]), textarea[name][required]:not([disabled])', // Form file input elements fileInputSelector: 'input[type=file]:not([disabled])', From b9bb7f56b00043a566c43255cc98ff5791f2d5ec Mon Sep 17 00:00:00 2001 From: Jeroen Visser Date: Fri, 1 Jan 2016 02:16:15 +0100 Subject: [PATCH 32/52] Update copyright year Happy new year! --- MIT-LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIT-LICENSE b/MIT-LICENSE index ed4c3901..9c7e3c0b 100644 --- a/MIT-LICENSE +++ b/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2007-2015 Contributors at http://github.com/rails/jquery-ujs/contributors +Copyright (c) 2007-2016 Contributors at http://github.com/rails/jquery-ujs/contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the From 4297a15d263e6cfabe9b0fbc998c441c069db7b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Tue, 12 Jan 2016 21:07:42 -0200 Subject: [PATCH 33/52] Fix jshint warnings --- src/rails.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rails.js b/src/rails.js index d7386608..012d5092 100644 --- a/src/rails.js +++ b/src/rails.js @@ -122,8 +122,8 @@ data.push(button); element.data('ujs:submit-button', null); } - element.data('ujs:submit-button-formmethod', null) - element.data('ujs:submit-button-formaction', null) + element.data('ujs:submit-button-formmethod', null); + element.data('ujs:submit-button-formaction', null); } else if (element.is(rails.inputChangeSelector)) { method = element.data('method'); url = element.data('url'); @@ -514,7 +514,7 @@ // Save attributes from button form.data('ujs:formnovalidate-button', button.attr('formnovalidate')); - form.data('ujs:submit-button-formaction', button.attr('formaction')) + form.data('ujs:submit-button-formaction', button.attr('formaction')); form.data('ujs:submit-button-formmethod', button.attr('formmethod')); }); From b3d633fadfe4abad10e0e8a0c9454c4f30bd7dbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Tue, 12 Jan 2016 21:07:51 -0200 Subject: [PATCH 34/52] Test with jQuery 1.12.0 and 2.2.0 --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 21eb4736..98158148 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,8 +15,10 @@ env: - JQUERY_VERSION: 1.11.0 - JQUERY_VERSION: 1.11.1 - JQUERY_VERSION: 1.11.2 + - JQUERY_VERSION: 1.12.0 - JQUERY_VERSION: 2.0.0 - JQUERY_VERSION: 2.1.0 - JQUERY_VERSION: 2.1.1 - JQUERY_VERSION: 2.1.2 - JQUERY_VERSION: 2.1.3 + - JQUERY_VERSION: 2.2.0 From 20fde22d4960c2aea11711f4f8ca4209b2685ccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Tue, 12 Jan 2016 21:14:05 -0200 Subject: [PATCH 35/52] Use new build infrastructure --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 98158148..5d25a09a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,7 @@ language: ruby +sudo: false +cache: + - bundler script: ./script/cibuild before_install: - "npm install jshint -g" From 15515b20722b955224db00caffaa8f28ac845875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Tue, 12 Jan 2016 21:16:53 -0200 Subject: [PATCH 36/52] Add npm to the travis cache --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 5d25a09a..7e6cb080 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,8 @@ language: ruby sudo: false cache: - bundler + - directories: + - $HOME/.npm script: ./script/cibuild before_install: - "npm install jshint -g" From 6f6c1e73e441c1fa6a10da7363acbfe7704aa4f1 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 14 Feb 2016 23:11:55 -0800 Subject: [PATCH 37/52] Update CONTRIBUTING.md --- CONTRIBUTING.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 64e6864d..6827438d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,12 +27,11 @@ git checkout -b my-feature-branch #### Bundle Install and Test -Ensure that you can build the project and run tests. Run rake test first, and then run the web tests. Visit [[http://localhost:4567]] in your browser. Click the version links at the top right of the page to run the test suite with the different supported versions of jQuery. +Ensure that you can build the project and run tests. Run `rake test:server` first, and then run the web tests by visiting [[http://localhost:4567]] in your browser. Click the version links at the top right of the page to run the test suite with the different supported versions of jQuery. ``` bundle install -rake test -ruby test/server.rb +rake test:server ``` #### Write Tests From e1a87651b8c1fcfefd0a347e47ee8742d7f45533 Mon Sep 17 00:00:00 2001 From: Achilleas Buisman Date: Mon, 22 Feb 2016 12:14:00 +0100 Subject: [PATCH 38/52] Prevent fieldsets from resulting in double data entries --- src/rails.js | 2 +- test/public/test/data-remote.js | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/rails.js b/src/rails.js index 012d5092..b51db23e 100644 --- a/src/rails.js +++ b/src/rails.js @@ -115,7 +115,7 @@ if (element.is('form')) { method = element.data('ujs:submit-button-formmethod') || element.attr('method'); url = element.data('ujs:submit-button-formaction') || element.attr('action'); - data = $(element[0].elements).serializeArray(); + data = $(element[0]).serializeArray(); // memoized value from clicked submit button var button = element.data('ujs:submit-button'); if (button) { diff --git a/test/public/test/data-remote.js b/test/public/test/data-remote.js index d4dc0955..e751c8e0 100644 --- a/test/public/test/data-remote.js +++ b/test/public/test/data-remote.js @@ -155,6 +155,21 @@ asyncTest('submitting form with data-remote attribute', 4, function() { .trigger('submit'); }); +asyncTest('submitting form with data-remote attribute should include inputs in a fieldset only once', 3, function() { + $('form[data-remote]') + .append('
') + .bind('ajax:success', function(e, data, status, xhr) { + App.assertCallbackInvoked('ajax:success'); + equal(data.params.items.length, 1, 'ajax arguments should only have the item once') + App.assertPostRequest(data); + }) + .bind('ajax:complete', function() { + $('form[data-remote], fieldset').remove() + start() + }) + .trigger('submit'); +}); + asyncTest('submitting form with data-remote attribute submits input with matching [form] attribute', 5, function() { $('#qunit-fixture') .append($('')); From 0fa8410cbf6b78c0bcb0652c4cffc74a44a7a4fd Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Wed, 10 Feb 2016 23:15:36 -0800 Subject: [PATCH 39/52] Bug where one radio button in group required The HTML spec does not seemd to clearly state what "required" does when only some of a radio button set are required. This answer on StackOverflow states that only one of the set is required: http://stackoverflow.com/a/8287947/1009332 Per the comments added in the fix: We cannot just compare allInputs for radio buttons, as the spec for required radio buttons states that only one of a radio button set needs to be required. If you have 2 radio buttons, and one is checked, but it's not required, then we get the other required radio button and see that there are no other radio buttons checked for the "required" ones. Thus, if the input control is a radio button, then filter for all radio buttons with the same name on the form. This will get all radio buttons of a given name that are checked and see that at least one is checked. Don't count unchecked required radio if other radio with same name is checked. But do count checked non-required radio of the same name. Note, each radio button name that is required is checked only once. --- src/rails.js | 47 ++++++++++++++++------- test/public/test/call-remote-callbacks.js | 29 +++++++++++++- 2 files changed, 61 insertions(+), 15 deletions(-) diff --git a/src/rails.js b/src/rails.js index 012d5092..86cfee1e 100644 --- a/src/rails.js +++ b/src/rails.js @@ -309,24 +309,45 @@ // Helper function which checks for blank inputs in a form that match the specified CSS selector blankInputs: function(form, specifiedSelector, nonBlank) { - var inputs = $(), input, valueToCheck, - selector = specifiedSelector || 'input,textarea', - allInputs = form.find(selector); - - allInputs.each(function() { + var foundInputs = $(), + input, + valueToCheck, + radiosForNameWithNoneSelected, + radioName, + selector = specifiedSelector || 'input,textarea', + requiredInputs = form.find(selector), + checkedRadioButtonNames = {}; + + requiredInputs.each(function() { input = $(this); - valueToCheck = input.is('input[type=checkbox],input[type=radio]') ? input.is(':checked') : !!input.val(); - if (valueToCheck === nonBlank) { + if (input.is('input[type=radio]')) { - // Don't count unchecked required radio if other radio with same name is checked - if (input.is('input[type=radio]') && allInputs.filter('input[type=radio]:checked[name="' + input.attr('name') + '"]').length) { - return true; // Skip to next input - } + // Don't count unchecked required radio as blank if other radio with same name is checked, + // regardless of whether same-name radio input has required attribute or not. The spec + // states https://www.w3.org/TR/html5/forms.html#the-required-attribute + radioName = input.attr('name'); + + // Skip if we've already seen the radio with this name. + if (!checkedRadioButtonNames[radioName]) { + + // If none checked + if (form.find('input[type=radio]:checked[name="' + radioName + '"]').length === 0) { + radiosForNameWithNoneSelected = form.find( + 'input[type=radio][name="' + radioName + '"]'); + foundInputs = foundInputs.add(radiosForNameWithNoneSelected); + } - inputs = inputs.add(input); + // We only need to check each name once. + checkedRadioButtonNames[radioName] = radioName; + } + } else { + valueToCheck = input.is('input[type=checkbox],input[type=radio]') ? input.is(':checked') : !!input.val(); + if (valueToCheck === nonBlank) { + foundInputs = foundInputs.add(input); + } } }); - return inputs.length ? inputs : false; + return foundInputs.length ? foundInputs : false; }, // Helper function which checks for non-blank inputs in a form that match the specified CSS selector diff --git a/test/public/test/call-remote-callbacks.js b/test/public/test/call-remote-callbacks.js index beee0a0c..11acaf18 100644 --- a/test/public/test/call-remote-callbacks.js +++ b/test/public/test/call-remote-callbacks.js @@ -241,8 +241,8 @@ asyncTest('unchecked required checkbox should abort form submission', 1, functio asyncTest('unchecked required radio should abort form submission', 1, function() { var form = $('form[data-remote]') - .append($('')) - .append($('')) + .append($('')) + .append($('')) .removeAttr('data-remote') .bind('ujs:everythingStopped', function() { ok(true, 'ujs:everythingStopped should run'); @@ -275,6 +275,31 @@ asyncTest('required radio should only require one to be checked', 1, function() }, 13); }); +asyncTest('required radio should only require one to be checked if not all radios are required', 1, function() { + $(document).bind('iframe:loading', function() { + ok(true, 'form should get submitted'); + }); + + var form = $('form[data-remote]') + // Check the radio that is not required + .append($('')) + // Check the radio that is not required + .append($('')) + // Only one needs to be required + .append($('')) + .removeAttr('data-remote') + .bind('ujs:everythingStopped', function() { + ok(false, 'ujs:everythingStopped should not run'); + }) + .find('#checkme').prop('checked', true) + .end() + .trigger('submit'); + + setTimeout(function() { + start(); + }, 13); +}); + function skipIt() { // This test cannot work due to the security feature in browsers which makes the value // attribute of file input fields readonly, so it cannot be set with default value. From 627913fc067399c26e5663fbd0a1719fc196af6b Mon Sep 17 00:00:00 2001 From: Mason Hale Date: Sun, 6 Mar 2016 10:32:37 -0600 Subject: [PATCH 40/52] Ignore file input fields without name attribute.\nThis is important to provide compatibility with the refile file upload ruby gem.\nSee: https://github.com/refile/refile/issues/58 --- src/rails.js | 2 +- test/public/test/call-remote-callbacks.js | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/rails.js b/src/rails.js index bc9fceb3..9e192a2e 100644 --- a/src/rails.js +++ b/src/rails.js @@ -48,7 +48,7 @@ requiredInputSelector: 'input[name][required]:not([disabled]), textarea[name][required]:not([disabled])', // Form file input elements - fileInputSelector: 'input[type=file]:not([disabled])', + fileInputSelector: 'input[name][type=file]:not([disabled])', // Link onClick disable selector with possible reenable after remote submission linkDisableSelector: 'a[data-disable-with], a[data-disable]', diff --git a/test/public/test/call-remote-callbacks.js b/test/public/test/call-remote-callbacks.js index 11acaf18..e7ee0dab 100644 --- a/test/public/test/call-remote-callbacks.js +++ b/test/public/test/call-remote-callbacks.js @@ -327,6 +327,27 @@ function skipIt() { }, 13); }); + asyncTest('file form input field should not abort remote request if file form input does not have a name attribute', 5, function() { + var form = $('form[data-remote]') + .append($('')) + .bind('ajax:beforeSend', function() { + ok(true, 'ajax:beforeSend should run'); + }) + .bind('iframe:loading', function() { + ok(true, 'form should get submitted'); + }) + .bind('ajax:aborted:file', function(e,data) { + ok(false, 'ajax:aborted:file should not run'); + }) + .trigger('submit'); + + setTimeout(function() { + form.find('input[type="file"]').val(''); + form.unbind('ajax:beforeSend'); + submit(); + }, 13); + }); + asyncTest('blank file input field should abort request entirely if handler bound to "ajax:aborted:file" event that returns false', 1, function() { var form = $('form[data-remote]') .append($('')) From f6471b2d8117de8939b656c0b5e5222d448fbfef Mon Sep 17 00:00:00 2001 From: Evgeniy Sukhanov Date: Wed, 15 Jun 2016 20:49:40 +0500 Subject: [PATCH 41/52] ready for jQuery 3 --- src/rails.js | 18 +++++++++--------- test/server.rb | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/rails.js b/src/rails.js index 9e192a2e..1370a65c 100644 --- a/src/rails.js +++ b/src/rails.js @@ -416,15 +416,15 @@ }); }); - $document.delegate(rails.linkDisableSelector, 'ajax:complete', function() { + $document.on('ajax:complete', rails.linkDisableSelector, function() { rails.enableElement($(this)); }); - $document.delegate(rails.buttonDisableSelector, 'ajax:complete', function() { + $document.on('ajax:complete', rails.buttonDisableSelector, function() { rails.enableFormElement($(this)); }); - $document.delegate(rails.linkClickSelector, 'click.rails', function(e) { + $document.on('click.rails', rails.linkClickSelector, function(e) { var link = $(this), method = link.data('method'), data = link.data('params'), metaClick = e.metaKey || e.ctrlKey; if (!rails.allowAction(link)) return rails.stopEverything(e); @@ -448,7 +448,7 @@ } }); - $document.delegate(rails.buttonClickSelector, 'click.rails', function(e) { + $document.on('click.rails', rails.buttonClickSelector, function(e) { var button = $(this); if (!rails.allowAction(button) || !rails.isRemote(button)) return rails.stopEverything(e); @@ -465,7 +465,7 @@ return false; }); - $document.delegate(rails.inputChangeSelector, 'change.rails', function(e) { + $document.on('change.rails', rails.inputChangeSelector, function(e) { var link = $(this); if (!rails.allowAction(link) || !rails.isRemote(link)) return rails.stopEverything(e); @@ -473,7 +473,7 @@ return false; }); - $document.delegate(rails.formSubmitSelector, 'submit.rails', function(e) { + $document.on('submit.rails', rails.formSubmitSelector, function(e) { var form = $(this), remote = rails.isRemote(form), blankRequiredInputs, @@ -518,7 +518,7 @@ } }); - $document.delegate(rails.formInputClickSelector, 'click.rails', function(event) { + $document.on('click.rails', rails.formInputClickSelector, function(event) { var button = $(this); if (!rails.allowAction(button)) return rails.stopEverything(event); @@ -539,11 +539,11 @@ form.data('ujs:submit-button-formmethod', button.attr('formmethod')); }); - $document.delegate(rails.formSubmitSelector, 'ajax:send.rails', function(event) { + $document.on('ajax:send.rails', rails.formSubmitSelector, function(event) { if (this === event.target) rails.disableFormElements($(this)); }); - $document.delegate(rails.formSubmitSelector, 'ajax:complete.rails', function(event) { + $document.on('ajax:complete.rails', rails.formSubmitSelector, function(event) { if (this === event.target) rails.enableFormElements($(this)); }); diff --git a/test/server.rb b/test/server.rb index 85f11db2..db94368a 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.10.2 1.11.0 2.0.0 2.1.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 2.0.0 2.1.0 3.0.0].freeze use Rack::Static, :urls => ["/src"], :root => File.expand_path('..', settings.root) From baccefdb5674d0d8964f3c93f5265a7d2db3a601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 3 Aug 2016 04:59:25 -0300 Subject: [PATCH 42/52] Add package.json to release jquery-ujs in npm. --- package.json | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 package.json diff --git a/package.json b/package.json new file mode 100644 index 00000000..fcebfa56 --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "jquery-ujs", + "version": "1.2.1", + "description": "Unobtrusive scripting adapter for jQuery", + "main": "src/rails.js", + "directories": { + "test": "test" + }, + "scripts": { + "test": "echo \"See the wiki: https://github.com/rails/jquery-ujs/wiki/Running-Tests-and-Contributing\" && exit 1", + "postversion": "git push && git push --tags && npm publish" + }, + "repository": { + "type": "git", + "url": "https://github.com/rails/jquery-ujs.git" + }, + "author": [ + "Stephen St. Martin", + "Steve Schwartz" + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/rails/jquery-ujs/issues" + }, + "homepage": "https://github.com/rails/jquery-ujs#readme", + "dependencies": { + "jquery": ">=1.8.0" + } +} From 26c9c77fb6f769fbfd7d321319c5780be2f90872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 3 Aug 2016 05:23:39 -0300 Subject: [PATCH 43/52] Add instructions about npm --- README.md | 5 +++++ RELEASE.md | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 RELEASE.md diff --git a/README.md b/README.md index a9fd2ab4..df6aee68 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,11 @@ Require both `jquery` and `jquery_ujs` into your application.js manifest. //= require jquery_ujs ``` +Installation using npm. +------------ + +Run `npm install --save jquery-ujs` to install the jquery-ujs package. + Installation using Bower ------------ diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 00000000..59316f77 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,23 @@ +## Releasing jquery-ujs + +### Releasing to npm + +Make sure npm's configuration `sign-git-tag` is set to true. + +``` +npm config set sign-git-tag true +``` + +Release it to npm using the [npm version command](https://docs.npmjs.com/cli/version). Like: + +``` +npm version patch +``` + +This will: + +* Bump a patch version +* Commit the change +* Generate the tag +* Push the commit and the tag to the repository +* Publish the package in https://www.npmjs.com From 33ba167d25f4e3ff696d7fec39fea24935811b77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Fri, 19 Aug 2016 13:20:32 -0300 Subject: [PATCH 44/52] Ignore Ruby files in the npm generated package --- .npmignore | 4 ++++ package.json | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 .npmignore diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..3af83847 --- /dev/null +++ b/.npmignore @@ -0,0 +1,4 @@ +script/ +test/ +Rakefile +Gemfile* diff --git a/package.json b/package.json index fcebfa56..67ca7a12 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,6 @@ "version": "1.2.1", "description": "Unobtrusive scripting adapter for jQuery", "main": "src/rails.js", - "directories": { - "test": "test" - }, "scripts": { "test": "echo \"See the wiki: https://github.com/rails/jquery-ujs/wiki/Running-Tests-and-Contributing\" && exit 1", "postversion": "git push && git push --tags && npm publish" From a4da3b4909b2ca081de3c6f99064403623bfe1fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Fri, 19 Aug 2016 13:26:52 -0300 Subject: [PATCH 45/52] 1.2.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 67ca7a12..08a5a245 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jquery-ujs", - "version": "1.2.1", + "version": "1.2.2", "description": "Unobtrusive scripting adapter for jQuery", "main": "src/rails.js", "scripts": { From 267b70d8a15514b084627aac6facd5bbdca8dd20 Mon Sep 17 00:00:00 2001 From: Sergey Date: Fri, 30 Dec 2016 14:08:12 +0800 Subject: [PATCH 46/52] Allow to init Jquery UJS manually --- src/rails.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/rails.js b/src/rails.js index 1370a65c..e5f581fd 100644 --- a/src/rails.js +++ b/src/rails.js @@ -1,4 +1,4 @@ -(function($, undefined) { +/* jshint node: true */ /** * Unobtrusive scripting adapter for jQuery @@ -10,10 +10,13 @@ * */ - // Cut down on the number of issues from people inadvertently including jquery_ujs twice - // by detecting and raising an error when it happens. +(function() { 'use strict'; + var jqueryUjsInit = function($, undefined) { + + // Cut down on the number of issues from people inadvertently including jquery_ujs twice + // by detecting and raising an error when it happens. if ( $.rails !== undefined ) { $.error('jquery-ujs has already been loaded!'); } @@ -552,4 +555,11 @@ }); } -})( jQuery ); + }; + + if (window.jQuery) { + jqueryUjsInit(jQuery); + } else if (typeof exports === 'object' && typeof module === 'object') { + module.exports = jqueryUjsInit; + } +})(); From 3bff9e049afcb38191c6eb470062b9e51f46e535 Mon Sep 17 00:00:00 2001 From: aki Date: Fri, 14 Apr 2017 15:19:59 +0900 Subject: [PATCH 47/52] Use jQuery.fn.on instead of jQuery.fn.bind --- src/rails.js | 4 +- test/public/test/call-remote-callbacks.js | 114 +++++++++++----------- test/public/test/call-remote.js | 18 ++-- test/public/test/data-confirm.js | 40 ++++---- test/public/test/data-disable-with.js | 44 ++++----- test/public/test/data-disable.js | 38 ++++---- test/public/test/data-method.js | 4 +- test/public/test/data-remote.js | 64 ++++++------ test/public/test/settings.js | 2 +- test/views/layout.erb | 2 +- 10 files changed, 165 insertions(+), 165 deletions(-) diff --git a/src/rails.js b/src/rails.js index 1370a65c..7eee3906 100644 --- a/src/rails.js +++ b/src/rails.js @@ -372,7 +372,7 @@ element.html(replacement); } - element.bind('click.railsDisable', function(e) { // prevent further clicking + element.on('click.railsDisable', function(e) { // prevent further clicking return rails.stopEverything(e); }); element.data('ujs:disabled', true); @@ -384,7 +384,7 @@ element.html(element.data('ujs:enable-with')); // set to old enabled state element.removeData('ujs:enable-with'); // clean up cache } - element.unbind('click.railsDisable'); // enable element + element.off('click.railsDisable'); // enable element element.removeData('ujs:disabled'); } }; diff --git a/test/public/test/call-remote-callbacks.js b/test/public/test/call-remote-callbacks.js index e7ee0dab..35926202 100644 --- a/test/public/test/call-remote-callbacks.js +++ b/test/public/test/call-remote-callbacks.js @@ -12,13 +12,13 @@ module('call-remote-callbacks', { $(document).undelegate('form[data-remote]', 'ajax:send'); $(document).undelegate('form[data-remote]', 'ajax:complete'); $(document).undelegate('form[data-remote]', 'ajax:success'); - $(document).unbind('ajaxStop'); - $(document).unbind('iframe:loading'); + $(document).off('ajaxStop'); + $(document).off('iframe:loading'); } }); function start_after_submit(form) { - form.bind('ajax:complete', function() { + form.on('ajax:complete', function() { ok(true, 'ajax:complete'); start(); }); @@ -43,7 +43,7 @@ asyncTest('modifying form fields with "ajax:before" sends modified data in reque $('form[data-remote]') .append($('')) .append($('')) - .bind('ajax:before', function() { + .on('ajax:before', function() { var form = $(this); form .append($('',{name: 'other_user_name',value: 'jonathan'})) @@ -53,7 +53,7 @@ asyncTest('modifying form fields with "ajax:before" sends modified data in reque }); submit(function(form) { - form.bind('ajax:success', function(e, data, status, xhr) { + form.on('ajax:success', function(e, data, status, xhr) { equal(data.params.user_name, 'steve', 'modified field value should have been submitted'); equal(data.params.other_user_name, 'jonathan', 'added field value should have been submitted'); equal(data.params.removed_user_name, undefined, 'removed field value should be undefined'); @@ -63,13 +63,13 @@ asyncTest('modifying form fields with "ajax:before" sends modified data in reque asyncTest('modifying data("type") with "ajax:before" requests new dataType in request', 2, function(){ $('form[data-remote]').data('type','html') - .bind('ajax:before', function() { + .on('ajax:before', function() { var form = $(this); form.data('type','xml'); }); submit(function(form) { - form.bind('ajax:beforeSend', function(e, xhr, settings) { + form.on('ajax:beforeSend', function(e, xhr, settings) { equal(settings.dataType, 'xml', 'modified dataType should have been requested'); }); }); @@ -77,13 +77,13 @@ asyncTest('modifying data("type") with "ajax:before" requests new dataType in re asyncTest('setting data("with-credentials",true) with "ajax:before" uses new setting in request', 2, function(){ $('form[data-remote]').data('with-credentials',false) - .bind('ajax:before', function() { + .on('ajax:before', function() { var form = $(this); form.data('with-credentials',true); }); submit(function(form) { - form.bind('ajax:beforeSend', function(e, xhr, settings) { + form.on('ajax:beforeSend', function(e, xhr, settings) { equal(settings.xhrFields && settings.xhrFields.withCredentials, true, 'setting modified in ajax:before should have forced withCredentials request'); }); }); @@ -91,37 +91,37 @@ asyncTest('setting data("with-credentials",true) with "ajax:before" uses new set asyncTest('stopping the "ajax:beforeSend" event aborts the request', 1, function() { submit(function(form) { - form.bind('ajax:beforeSend', function() { + form.on('ajax:beforeSend', function() { ok(true, 'aborting request in ajax:beforeSend'); return false; }); - form.unbind('ajax:send').bind('ajax:send', function() { + form.off('ajax:send').on('ajax:send', function() { ok(false, 'ajax:send should not run'); }); - form.unbind('ajax:complete').bind('ajax:complete', function() { + form.off('ajax:complete').on('ajax:complete', function() { ok(false, 'ajax:complete should not run'); }); - form.bind('ajax:error', function(e, xhr, status, error) { + form.on('ajax:error', function(e, xhr, status, error) { ok(false, 'ajax:error should not run'); }); - $(document).bind('ajaxStop', function() { + $(document).on('ajaxStop', function() { start(); }); }); }); asyncTest('blank required form input field should abort request and trigger "ajax:aborted:required" event', 5, function() { - $(document).bind('iframe:loading', function() { + $(document).on('iframe:loading', function() { ok(false, 'form should not get submitted'); }); var form = $('form[data-remote]') .append($('')) .append($('')) - .bind('ajax:beforeSend', function() { + .on('ajax:beforeSend', function() { ok(false, 'ajax:beforeSend should not run'); }) - .bind('ajax:aborted:required', function(e,data){ + .on('ajax:aborted:required', function(e,data){ ok(data.length == 2, 'ajax:aborted:required event is passed all blank required inputs (jQuery objects)'); ok(data.first().is('input[name="user_name"]') , 'ajax:aborted:required adds blank required input to data'); ok(data.last().is('textarea[name="user_bio"]'), 'ajax:aborted:required adds blank required textarea to data'); @@ -131,7 +131,7 @@ asyncTest('blank required form input field should abort request and trigger "aja setTimeout(function() { form.find('input[required],textarea[required]').val('Tyler'); - form.unbind('ajax:beforeSend'); + form.off('ajax:beforeSend'); submit(); }, 13); }); @@ -140,7 +140,7 @@ asyncTest('blank required form input for non-remote form should abort normal sub var form = $('form[data-remote]') .append($('')) .removeAttr('data-remote') - .bind('ujs:everythingStopped', function() { + .on('ujs:everythingStopped', function() { ok(true, 'ujs:everythingStopped should run'); }) .trigger('submit'); @@ -153,10 +153,10 @@ asyncTest('blank required form input for non-remote form should abort normal sub asyncTest('form should be submitted with blank required fields if handler is bound to "ajax:aborted:required" event that returns false', 1, function(){ var form = $('form[data-remote]') .append($('')) - .bind('ajax:beforeSend', function() { + .on('ajax:beforeSend', function() { ok(true, 'ajax:beforeSend should run'); }) - .bind('ajax:aborted:required', function() { + .on('ajax:aborted:required', function() { return false; }) .trigger('submit'); @@ -170,10 +170,10 @@ asyncTest('disabled fields should not be included in blank required check', 2, f var form = $('form[data-remote]') .append($('')) .append($('')) - .bind('ajax:beforeSend', function() { + .on('ajax:beforeSend', function() { ok(true, 'ajax:beforeSend should run'); }) - .bind('ajax:aborted:required', function() { + .on('ajax:aborted:required', function() { ok(false, 'ajax:aborted:required should not run'); }); @@ -184,10 +184,10 @@ asyncTest('form should be submitted with blank required fields if it has the "no var form = $('form[data-remote]') .append($('')) .attr("novalidate", "novalidate") - .bind('ajax:beforeSend', function() { + .on('ajax:beforeSend', function() { ok(true, 'ajax:beforeSend should run'); }) - .bind('ajax:aborted:required', function() { + .on('ajax:aborted:required', function() { ok(false, 'ajax:aborted:required should not run'); }); @@ -199,10 +199,10 @@ asyncTest('form should be submitted with blank required fields if the button has var form = $('form[data-remote]') .append($('')) .append(submit_button) - .bind('ajax:beforeSend', function() { + .on('ajax:beforeSend', function() { ok(true, 'ajax:beforeSend should run'); }) - .bind('ajax:aborted:required', function() { + .on('ajax:aborted:required', function() { ok(false, 'ajax:aborted:required should not run'); }); @@ -210,7 +210,7 @@ asyncTest('form should be submitted with blank required fields if the button has }); asyncTest('blank required form input for non-remote form with "novalidate" attribute should not abort normal submission', 1, function() { - $(document).bind('iframe:loading', function() { + $(document).on('iframe:loading', function() { ok(true, 'form should get submitted'); }); @@ -229,7 +229,7 @@ asyncTest('unchecked required checkbox should abort form submission', 1, functio var form = $('form[data-remote]') .append($('')) .removeAttr('data-remote') - .bind('ujs:everythingStopped', function() { + .on('ujs:everythingStopped', function() { ok(true, 'ujs:everythingStopped should run'); }) .trigger('submit'); @@ -244,7 +244,7 @@ asyncTest('unchecked required radio should abort form submission', 1, function() .append($('')) .append($('')) .removeAttr('data-remote') - .bind('ujs:everythingStopped', function() { + .on('ujs:everythingStopped', function() { ok(true, 'ujs:everythingStopped should run'); }) .trigger('submit'); @@ -255,7 +255,7 @@ asyncTest('unchecked required radio should abort form submission', 1, function() }); asyncTest('required radio should only require one to be checked', 1, function() { - $(document).bind('iframe:loading', function() { + $(document).on('iframe:loading', function() { ok(true, 'form should get submitted'); }); @@ -263,7 +263,7 @@ asyncTest('required radio should only require one to be checked', 1, function() .append($('')) .append($('')) .removeAttr('data-remote') - .bind('ujs:everythingStopped', function() { + .on('ujs:everythingStopped', function() { ok(false, 'ujs:everythingStopped should not run'); }) .find('#checkme').prop('checked', true) @@ -276,7 +276,7 @@ asyncTest('required radio should only require one to be checked', 1, function() }); asyncTest('required radio should only require one to be checked if not all radios are required', 1, function() { - $(document).bind('iframe:loading', function() { + $(document).on('iframe:loading', function() { ok(true, 'form should get submitted'); }); @@ -288,7 +288,7 @@ asyncTest('required radio should only require one to be checked if not all radio // Only one needs to be required .append($('')) .removeAttr('data-remote') - .bind('ujs:everythingStopped', function() { + .on('ujs:everythingStopped', function() { ok(false, 'ujs:everythingStopped should not run'); }) .find('#checkme').prop('checked', true) @@ -307,13 +307,13 @@ function skipIt() { asyncTest('non-blank file form input field should abort remote request, but submit normally', 5, function() { var form = $('form[data-remote]') .append($('')) - .bind('ajax:beforeSend', function() { + .on('ajax:beforeSend', function() { ok(false, 'ajax:beforeSend should not run'); }) - .bind('iframe:loading', function() { + .on('iframe:loading', function() { ok(true, 'form should get submitted'); }) - .bind('ajax:aborted:file', function(e,data) { + .on('ajax:aborted:file', function(e,data) { ok(data.length == 1, 'ajax:aborted:file event is passed all non-blank file inputs (jQuery objects)'); ok(data.first().is('input[name="attachment"]') , 'ajax:aborted:file adds non-blank file input to data'); ok(true, 'ajax:aborted:file event should run'); @@ -322,7 +322,7 @@ function skipIt() { setTimeout(function() { form.find('input[type="file"]').val(''); - form.unbind('ajax:beforeSend'); + form.off('ajax:beforeSend'); submit(); }, 13); }); @@ -330,20 +330,20 @@ function skipIt() { asyncTest('file form input field should not abort remote request if file form input does not have a name attribute', 5, function() { var form = $('form[data-remote]') .append($('')) - .bind('ajax:beforeSend', function() { + .on('ajax:beforeSend', function() { ok(true, 'ajax:beforeSend should run'); }) - .bind('iframe:loading', function() { + .on('iframe:loading', function() { ok(true, 'form should get submitted'); }) - .bind('ajax:aborted:file', function(e,data) { + .on('ajax:aborted:file', function(e,data) { ok(false, 'ajax:aborted:file should not run'); }) .trigger('submit'); setTimeout(function() { form.find('input[type="file"]').val(''); - form.unbind('ajax:beforeSend'); + form.off('ajax:beforeSend'); submit(); }, 13); }); @@ -351,20 +351,20 @@ function skipIt() { asyncTest('blank file input field should abort request entirely if handler bound to "ajax:aborted:file" event that returns false', 1, function() { var form = $('form[data-remote]') .append($('')) - .bind('ajax:beforeSend', function() { + .on('ajax:beforeSend', function() { ok(false, 'ajax:beforeSend should not run'); }) - .bind('iframe:loading', function() { + .on('iframe:loading', function() { ok(false, 'form should not get submitted'); }) - .bind('ajax:aborted:file', function() { + .on('ajax:aborted:file', function() { return false; }) .trigger('submit'); setTimeout(function() { form.find('input[type="file"]').val(''); - form.unbind('ajax:beforeSend'); + form.off('ajax:beforeSend'); submit(); }, 13); }); @@ -377,13 +377,13 @@ asyncTest('"ajax:beforeSend" can be observed and stopped with event delegation', }); submit(function(form) { - form.unbind('ajax:send').bind('ajax:send', function() { + form.off('ajax:send').on('ajax:send', function() { ok(false, 'ajax:send should not run'); }); - form.unbind('ajax:complete').bind('ajax:complete', function() { + form.off('ajax:complete').on('ajax:complete', function() { ok(false, 'ajax:complete should not run'); }); - $(document).bind('ajaxStop', function() { + $(document).on('ajaxStop', function() { start(); }); }); @@ -391,19 +391,19 @@ asyncTest('"ajax:beforeSend" can be observed and stopped with event delegation', 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) { + form.on('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) { + form.on('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) { + form.on('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'); ok(xhr.getResponseHeader, 'third argument to "ajax:success" should be an XHR object'); }); - form.bind('ajax:complete', function(e, xhr, status) { + form.on('ajax:complete', function(e, xhr, status) { ok(xhr.getResponseHeader, 'first argument to "ajax:complete" should be an XHR object'); equal(status, 'success', 'second argument to ajax:complete should be a status string'); }); @@ -414,9 +414,9 @@ if(window.phantom !== undefined) { 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) { + form.on('ajax:beforeSend', function(arg) { ok(true, 'ajax:beforeSend') }); + form.on('ajax:send', function(arg) { ok(true, 'ajax:send') }); + form.on('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'); // Firefox 8 returns "Forbidden " with trailing space @@ -452,7 +452,7 @@ asyncTest('binding to ajax callbacks via .delegate() triggers handlers properly' asyncTest('binding to ajax:send event to call jquery methods on ajax object', 2, function() { $('form[data-remote]') - .bind('ajax:send', function(e, xhr) { + .on('ajax:send', function(e, xhr) { ok(true, 'event should fire'); equal(typeof(xhr.abort), 'function', 'event should pass jqXHR object'); xhr.abort(); diff --git a/test/public/test/call-remote.js b/test/public/test/call-remote.js index 2a6e3a61..0d9214a0 100644 --- a/test/public/test/call-remote.js +++ b/test/public/test/call-remote.js @@ -11,8 +11,8 @@ module('call-remote'); function submit(fn) { $('form') - .bind('ajax:success', fn) - .bind('ajax:complete', function() { start() }) + .on('ajax:success', fn) + .on('ajax:complete', function() { start() }) .trigger('submit'); } @@ -37,10 +37,10 @@ asyncTest('form method is read from submit button "formmethod" if submit is trig buildForm({ method: 'post' }); $('#qunit-fixture').find('form').append(submitButton) - .bind('ajax:success', function(e, data, status, xhr) { + .on('ajax:success', function(e, data, status, xhr) { App.assertGetRequest(data); }) - .bind('ajax:complete', function() { start() }); + .on('ajax:complete', function() { start() }); submitButton.trigger('click'); }); @@ -74,10 +74,10 @@ asyncTest('form url is read from submit button "formaction" if submit is trigger buildForm({ method: 'post', href: '/echo2' }); $('#qunit-fixture').find('form').append(submitButton) - .bind('ajax:success', function(e, data, status, xhr) { + .on('ajax:success', function(e, data, status, xhr) { App.assertRequestPath(data, '/echo'); }) - .bind('ajax:complete', function() { start() }); + .on('ajax:complete', function() { start() }); submitButton.trigger('click'); }); @@ -113,7 +113,7 @@ asyncTest('allow empty form "action"', 1, function() { buildForm({ action: '' }); $('#qunit-fixture').find('form') - .bind('ajax:beforeSend', function(e, xhr, settings) { + .on('ajax:beforeSend', function(e, xhr, settings) { // Get current location (the same way jQuery does) try { currentLocation = location.href; @@ -155,7 +155,7 @@ asyncTest('intelligently guesses crossDomain behavior when target URL has a diff $('#qunit-fixture').append(''); $('#qunit-fixture').find('form') - .bind('ajax:beforeSend', function(e, xhr, settings) { + .on('ajax:beforeSend', function(e, xhr, settings) { equal(settings.crossDomain, true, 'crossDomain should be set to true'); @@ -174,7 +174,7 @@ asyncTest('intelligently guesses crossDomain behavior when target URL consists o $('#qunit-fixture').append(''); $('#qunit-fixture').find('form') - .bind('ajax:beforeSend', function(e, xhr, settings) { + .on('ajax:beforeSend', function(e, xhr, settings) { equal(settings.crossDomain, false, 'crossDomain should be set to false'); diff --git a/test/public/test/data-confirm.js b/test/public/test/data-confirm.js index 8add713c..2e905d42 100644 --- a/test/public/test/data-confirm.js +++ b/test/public/test/data-confirm.js @@ -39,11 +39,11 @@ asyncTest('clicking on a link with data-confirm attribute. Confirm yes.', 6, fun window.confirm = function(msg) { message = msg; return true }; $('a[data-confirm]') - .bind('confirm:complete', function(e, data) { + .on('confirm:complete', function(e, data) { App.assertCallbackInvoked('confirm:complete'); ok(data == true, 'confirm:complete passes in confirm answer (true)'); }) - .bind('ajax:success', function(e, data, status, xhr) { + .on('ajax:success', function(e, data, status, xhr) { App.assertCallbackInvoked('ajax:success'); App.assertRequestPath(data, '/echo'); App.assertGetRequest(data); @@ -60,11 +60,11 @@ asyncTest('clicking on a button with data-confirm attribute. Confirm yes.', 6, f window.confirm = function(msg) { message = msg; return true }; $('button[data-confirm]') - .bind('confirm:complete', function(e, data) { + .on('confirm:complete', function(e, data) { App.assertCallbackInvoked('confirm:complete'); ok(data == true, 'confirm:complete passes in confirm answer (true)'); }) - .bind('ajax:success', function(e, data, status, xhr) { + .on('ajax:success', function(e, data, status, xhr) { App.assertCallbackInvoked('ajax:success'); App.assertRequestPath(data, '/echo'); App.assertGetRequest(data); @@ -81,11 +81,11 @@ asyncTest('clicking on a link with data-confirm attribute. Confirm No.', 3, func window.confirm = function(msg) { message = msg; return false }; $('a[data-confirm]') - .bind('confirm:complete', function(e, data) { + .on('confirm:complete', function(e, data) { App.assertCallbackInvoked('confirm:complete'); ok(data == false, 'confirm:complete passes in confirm answer (false)'); }) - .bind('ajax:beforeSend', function(e, data, status, xhr) { + .on('ajax:beforeSend', function(e, data, status, xhr) { App.assertCallbackNotInvoked('ajax:beforeSend'); }) .trigger('click'); @@ -102,11 +102,11 @@ asyncTest('clicking on a button with data-confirm attribute. Confirm No.', 3, fu window.confirm = function(msg) { message = msg; return false }; $('button[data-confirm]') - .bind('confirm:complete', function(e, data) { + .on('confirm:complete', function(e, data) { App.assertCallbackInvoked('confirm:complete'); ok(data == false, 'confirm:complete passes in confirm answer (false)'); }) - .bind('ajax:beforeSend', function(e, data, status, xhr) { + .on('ajax:beforeSend', function(e, data, status, xhr) { App.assertCallbackNotInvoked('ajax:beforeSend'); }) .trigger('click'); @@ -123,11 +123,11 @@ asyncTest('clicking on a button with data-confirm attribute. Confirm error.', 3, window.confirm = function(msg) { message = msg; throw "some random error"; }; $('button[data-confirm]') - .bind('confirm:complete', function(e, data) { + .on('confirm:complete', function(e, data) { App.assertCallbackInvoked('confirm:complete'); ok(data == false, 'confirm:complete passes in confirm answer (false)'); }) - .bind('ajax:beforeSend', function(e, data, status, xhr) { + .on('ajax:beforeSend', function(e, data, status, xhr) { App.assertCallbackNotInvoked('ajax:beforeSend'); }) .trigger('click'); @@ -144,11 +144,11 @@ asyncTest('clicking on a submit button with form and data-confirm attributes. Co window.confirm = function(msg) { message = msg; return false }; $('input[type=submit][form]') - .bind('confirm:complete', function(e, data) { + .on('confirm:complete', function(e, data) { App.assertCallbackInvoked('confirm:complete'); ok(data == false, 'confirm:complete passes in confirm answer (false)'); }) - .bind('ajax:beforeSend', function(e, data, status, xhr) { + .on('ajax:beforeSend', function(e, data, status, xhr) { App.assertCallbackNotInvoked('ajax:beforeSend'); }) .trigger('click'); @@ -166,11 +166,11 @@ asyncTest('binding to confirm event of a link and returning false', 1, function( }; $('a[data-confirm]') - .bind('confirm', function() { + .on('confirm', function() { App.assertCallbackInvoked('confirm'); return false; }) - .bind('confirm:complete', function() { + .on('confirm:complete', function() { App.assertCallbackNotInvoked('confirm:complete'); }) .trigger('click'); @@ -187,11 +187,11 @@ asyncTest('binding to confirm event of a button and returning false', 1, functio }; $('button[data-confirm]') - .bind('confirm', function() { + .on('confirm', function() { App.assertCallbackInvoked('confirm'); return false; }) - .bind('confirm:complete', function() { + .on('confirm:complete', function() { App.assertCallbackNotInvoked('confirm:complete'); }) .trigger('click'); @@ -209,11 +209,11 @@ asyncTest('binding to confirm:complete event of a link and returning false', 2, }; $('a[data-confirm]') - .bind('confirm:complete', function() { + .on('confirm:complete', function() { App.assertCallbackInvoked('confirm:complete'); return false; }) - .bind('ajax:beforeSend', function() { + .on('ajax:beforeSend', function() { App.assertCallbackNotInvoked('ajax:beforeSend'); }) .trigger('click'); @@ -231,11 +231,11 @@ asyncTest('binding to confirm:complete event of a button and returning false', 2 }; $('button[data-confirm]') - .bind('confirm:complete', function() { + .on('confirm:complete', function() { App.assertCallbackInvoked('confirm:complete'); return false; }) - .bind('ajax:beforeSend', function() { + .on('ajax:beforeSend', function() { App.assertCallbackNotInvoked('ajax:beforeSend'); }) .trigger('click'); diff --git a/test/public/test/data-disable-with.js b/test/public/test/data-disable-with.js index 0366f81e..1bb67426 100644 --- a/test/public/test/data-disable-with.js +++ b/test/public/test/data-disable-with.js @@ -40,7 +40,7 @@ module('data-disable-with', { })); }, teardown: function() { - $(document).unbind('iframe:loaded'); + $(document).off('iframe:loaded'); } }); @@ -49,7 +49,7 @@ asyncTest('form input field with "data-disable-with" attribute', 7, function() { App.checkEnabledState(input, 'john'); - form.bind('ajax:success', function(e, data) { + form.on('ajax:success', function(e, data) { setTimeout(function() { App.checkEnabledState(input, 'john'); equal(data.params.user_name, 'john'); @@ -67,7 +67,7 @@ asyncTest('blank form input field with "data-disable-with" attribute', 7, functi input.val(''); App.checkEnabledState(input, ''); - form.bind('ajax:success', function(e, data) { + form.on('ajax:success', function(e, data) { setTimeout(function() { App.checkEnabledState(input, ''); equal(data.params.user_name, ''); @@ -85,7 +85,7 @@ asyncTest('form button with "data-disable-with" attribute', 6, function() { App.checkEnabledState(button, 'Submit'); - form.bind('ajax:success', function(e, data) { + form.on('ajax:success', function(e, data) { setTimeout(function() { App.checkEnabledState(button, 'Submit'); start(); @@ -102,9 +102,9 @@ asyncTest('form input[type=submit][data-disable-with] disables', 6, function(){ App.checkEnabledState(input, 'Submit'); // WEEIRDD: attaching this handler makes the test work in IE7 - $(document).bind('iframe:loading', function(e, form) {}); + $(document).on('iframe:loading', function(e, form) {}); - $(document).bind('iframe:loaded', function(e, data) { + $(document).on('iframe:loaded', function(e, data) { setTimeout(function() { App.checkDisabledState(input, 'submitting ...'); start(); @@ -138,7 +138,7 @@ test('form input[type=submit][data-disable-with] re-enables when `pageshow` even 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.on('ajax:success', function(){ form.html(origFormContents); setTimeout(function(){ @@ -153,7 +153,7 @@ asyncTest('form[data-remote] input[data-disable-with] is replaced with disabled 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(){ + form.on('ajax:success', function(){ input.replaceWith(newDisabledInput); setTimeout(function(){ @@ -168,9 +168,9 @@ asyncTest('form input[type=submit][data-disable-with] using "form" attribute dis App.checkEnabledState(input, 'Form Attr Submit'); // WEEIRDD: attaching this handler makes the test work in IE7 - $(document).bind('iframe:loading', function(e, form) {}); + $(document).on('iframe:loading', function(e, form) {}); - $(document).bind('iframe:loaded', function(e, data) { + $(document).on('iframe:loaded', function(e, data) { setTimeout(function() { App.checkDisabledState(input, 'form attr submitting'); start(); @@ -188,7 +188,7 @@ 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) { + form.on('ajax:success', function(e, data) { setTimeout(function() { equal(data.params.user_bio, 'born, lived, died.'); start(); @@ -227,10 +227,10 @@ asyncTest('a[data-remote][data-disable-with] disables and re-enables', 6, functi App.checkEnabledState(link, 'Click me'); link - .bind('ajax:beforeSend', function() { + .on('ajax:beforeSend', function() { App.checkDisabledState(link, 'clicking...'); }) - .bind('ajax:complete', function() { + .on('ajax:complete', function() { setTimeout( function() { App.checkEnabledState(link, 'Click me'); start(); @@ -245,7 +245,7 @@ asyncTest('a[data-remote][data-disable-with] re-enables when `ajax:before` event App.checkEnabledState(link, 'Click me'); link - .bind('ajax:before', function() { + .on('ajax:before', function() { App.checkDisabledState(link, 'clicking...'); return false; }) @@ -263,7 +263,7 @@ asyncTest('a[data-remote][data-disable-with] re-enables when `ajax:beforeSend` e App.checkEnabledState(link, 'Click me'); link - .bind('ajax:beforeSend', function() { + .on('ajax:beforeSend', function() { App.checkDisabledState(link, 'clicking...'); return false; }) @@ -281,7 +281,7 @@ asyncTest('a[data-remote][data-disable-with] re-enables when `ajax:error` event App.checkEnabledState(link, 'Click me'); link - .bind('ajax:beforeSend', function() { + .on('ajax:beforeSend', function() { App.checkDisabledState(link, 'clicking...'); }) .trigger('click'); @@ -300,7 +300,7 @@ asyncTest('form[data-remote] input|button|textarea[data-disable-with] does not d submit = $('').appendTo(form); form - .bind('ajax:beforeSend', function() { + .on('ajax:beforeSend', function() { return false; }) .trigger('submit'); @@ -337,10 +337,10 @@ asyncTest('button[data-remote][data-disable-with] disables and re-enables', 6, f App.checkEnabledState(button, 'Click me'); button - .bind('ajax:send', function() { + .on('ajax:send', function() { App.checkDisabledState(button, 'clicking...'); }) - .bind('ajax:complete', function() { + .on('ajax:complete', function() { setTimeout( function() { App.checkEnabledState(button, 'Click me'); start(); @@ -355,7 +355,7 @@ asyncTest('button[data-remote][data-disable-with] re-enables when `ajax:before` App.checkEnabledState(button, 'Click me'); button - .bind('ajax:before', function() { + .on('ajax:before', function() { App.checkDisabledState(button, 'clicking...'); return false; }) @@ -373,7 +373,7 @@ asyncTest('button[data-remote][data-disable-with] re-enables when `ajax:beforeSe App.checkEnabledState(button, 'Click me'); button - .bind('ajax:beforeSend', function() { + .on('ajax:beforeSend', function() { App.checkDisabledState(button, 'clicking...'); return false; }) @@ -391,7 +391,7 @@ asyncTest('button[data-remote][data-disable-with] re-enables when `ajax:error` e App.checkEnabledState(button, 'Click me'); button - .bind('ajax:send', function() { + .on('ajax:send', function() { App.checkDisabledState(button, 'clicking...'); }) .trigger('click'); diff --git a/test/public/test/data-disable.js b/test/public/test/data-disable.js index 3df1bb9c..ed30b0ec 100644 --- a/test/public/test/data-disable.js +++ b/test/public/test/data-disable.js @@ -30,7 +30,7 @@ module('data-disable', { })); }, teardown: function() { - $(document).unbind('iframe:loaded'); + $(document).off('iframe:loaded'); } }); @@ -39,7 +39,7 @@ asyncTest('form input field with "data-disable" attribute', 7, function() { App.checkEnabledState(input, 'john'); - form.bind('ajax:success', function(e, data) { + form.on('ajax:success', function(e, data) { setTimeout(function() { App.checkEnabledState(input, 'john'); equal(data.params.user_name, 'john'); @@ -57,7 +57,7 @@ asyncTest('form button with "data-disable" attribute', 7, function() { App.checkEnabledState(button, 'Submit'); - form.bind('ajax:success', function(e, data) { + form.on('ajax:success', function(e, data) { setTimeout(function() { App.checkEnabledState(button, 'Submit'); start(); @@ -75,9 +75,9 @@ asyncTest('form input[type=submit][data-disable] disables', 6, function(){ App.checkEnabledState(input, 'Submit'); // WEEIRDD: attaching this handler makes the test work in IE7 - $(document).bind('iframe:loading', function(e, form) {}); + $(document).on('iframe:loading', function(e, form) {}); - $(document).bind('iframe:loaded', function(e, data) { + $(document).on('iframe:loaded', function(e, data) { setTimeout(function() { App.checkDisabledState(input, 'Submit'); start(); @@ -93,7 +93,7 @@ asyncTest('form input[type=submit][data-disable] disables', 6, 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(){ + form.on('ajax:success', function(){ form.html(origFormContents); setTimeout(function(){ @@ -108,7 +108,7 @@ asyncTest('form[data-remote] input[data-disable] is replaced with disabled field 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(){ + form.on('ajax:success', function(){ input.replaceWith(newDisabledInput); setTimeout(function(){ @@ -122,7 +122,7 @@ asyncTest('form[data-remote] textarea[data-disable] attribute', 3, function() { var form = $('form[data-remote]'), textarea = $('').appendTo(form); - form.bind('ajax:success', function(e, data) { + form.on('ajax:success', function(e, data) { setTimeout(function() { equal(data.params.user_bio, 'born, lived, died.'); start(); @@ -150,10 +150,10 @@ asyncTest('a[data-remote][data-disable] disables and re-enables', 6, function() App.checkEnabledState(link, 'Click me'); link - .bind('ajax:send', function() { + .on('ajax:send', function() { App.checkDisabledState(link, 'Click me'); }) - .bind('ajax:complete', function() { + .on('ajax:complete', function() { setTimeout( function() { App.checkEnabledState(link, 'Click me'); start(); @@ -168,7 +168,7 @@ asyncTest('a[data-remote][data-disable] re-enables when `ajax:before` event is c App.checkEnabledState(link, 'Click me'); link - .bind('ajax:before', function() { + .on('ajax:before', function() { App.checkDisabledState(link, 'Click me'); return false; }) @@ -186,7 +186,7 @@ asyncTest('a[data-remote][data-disable] re-enables when `ajax:beforeSend` event App.checkEnabledState(link, 'Click me'); link - .bind('ajax:beforeSend', function() { + .on('ajax:beforeSend', function() { App.checkDisabledState(link, 'Click me'); return false; }) @@ -204,7 +204,7 @@ asyncTest('a[data-remote][data-disable] re-enables when `ajax:error` event is tr App.checkEnabledState(link, 'Click me'); link - .bind('ajax:send', function() { + .on('ajax:send', function() { App.checkDisabledState(link, 'Click me'); }) .trigger('click'); @@ -223,7 +223,7 @@ asyncTest('form[data-remote] input|button|textarea[data-disable] does not disabl submit = $('').appendTo(form); form - .bind('ajax:beforeSend', function() { + .on('ajax:beforeSend', function() { return false; }) .trigger('submit'); @@ -260,10 +260,10 @@ asyncTest('button[data-remote][data-disable] disables and re-enables', 6, functi App.checkEnabledState(button, 'Click me'); button - .bind('ajax:send', function() { + .on('ajax:send', function() { App.checkDisabledState(button, 'Click me'); }) - .bind('ajax:complete', function() { + .on('ajax:complete', function() { setTimeout( function() { App.checkEnabledState(button, 'Click me'); start(); @@ -278,7 +278,7 @@ asyncTest('button[data-remote][data-disable] re-enables when `ajax:before` event App.checkEnabledState(button, 'Click me'); button - .bind('ajax:before', function() { + .on('ajax:before', function() { App.checkDisabledState(button, 'Click me'); return false; }) @@ -296,7 +296,7 @@ asyncTest('button[data-remote][data-disable] re-enables when `ajax:beforeSend` e App.checkEnabledState(button, 'Click me'); button - .bind('ajax:beforeSend', function() { + .on('ajax:beforeSend', function() { App.checkDisabledState(button, 'Click me'); return false; }) @@ -314,7 +314,7 @@ asyncTest('button[data-remote][data-disable] re-enables when `ajax:error` event App.checkEnabledState(button, 'Click me'); button - .bind('ajax:send', function() { + .on('ajax:send', function() { App.checkDisabledState(button, 'Click me'); }) .trigger('click'); diff --git a/test/public/test/data-method.js b/test/public/test/data-method.js index 57528377..32b90d70 100644 --- a/test/public/test/data-method.js +++ b/test/public/test/data-method.js @@ -7,12 +7,12 @@ module('data-method', { })); }, teardown: function() { - $(document).unbind('iframe:loaded'); + $(document).off('iframe:loaded'); } }); function submit(fn, options) { - $(document).bind('iframe:loaded', function(e, data) { + $(document).on('iframe:loaded', function(e, data) { fn(data); start(); }); diff --git a/test/public/test/data-remote.js b/test/public/test/data-remote.js index e751c8e0..215d8217 100644 --- a/test/public/test/data-remote.js +++ b/test/public/test/data-remote.js @@ -39,7 +39,7 @@ asyncTest('ctrl-clicking on a link does not fire ajaxyness', 0, function() { // follow links using `trigger('click')`, it only fires bindings. link .removeAttr('data-params') - .bind('ajax:beforeSend', function() { + .on('ajax:beforeSend', function() { ok(false, 'ajax should not be triggered'); }); @@ -62,7 +62,7 @@ asyncTest('ctrl-clicking on a link still fires ajax for non-GET links and for li link .removeAttr('data-params') .attr('data-method', 'POST') - .bind('ajax:beforeSend', function() { + .on('ajax:beforeSend', function() { ok(true, 'ajax should be triggered'); }) .trigger(e); @@ -80,23 +80,23 @@ asyncTest('ctrl-clicking on a link still fires ajax for non-GET links and for li asyncTest('clicking on a link with data-remote attribute', 5, function() { $('a[data-remote]') - .bind('ajax:success', function(e, data, status, xhr) { + .on('ajax:success', function(e, data, status, xhr) { App.assertCallbackInvoked('ajax:success'); App.assertRequestPath(data, '/echo'); equal(data.params.data1, 'value1', 'ajax arguments should have key data1 with right value'); equal(data.params.data2, 'value2', 'ajax arguments should have key data2 with right value'); App.assertGetRequest(data); }) - .bind('ajax:complete', function() { start() }) + .on('ajax:complete', function() { start() }) .trigger('click'); }); asyncTest('clicking on a link with disabled attribute', 0, function() { $('a[disabled]') - .bind("ajax:before", function(e, data, status, xhr) { + .on("ajax:before", function(e, data, status, xhr) { App.assertCallbackNotInvoked('ajax:success') }) - .bind('ajax:complete', function() { start() }) + .on('ajax:complete', function() { start() }) .trigger('click') setTimeout(function() { @@ -106,14 +106,14 @@ asyncTest('clicking on a link with disabled attribute', 0, function() { asyncTest('clicking on a button with data-remote attribute', 5, function() { $('button[data-remote]') - .bind('ajax:success', function(e, data, status, xhr) { + .on('ajax:success', function(e, data, status, xhr) { App.assertCallbackInvoked('ajax:success'); App.assertRequestPath(data, '/echo'); equal(data.params.data1, 'value1', 'ajax arguments should have key data1 with right value'); equal(data.params.data2, 'value2', 'ajax arguments should have key data2 with right value'); App.assertGetRequest(data); }) - .bind('ajax:complete', function() { start() }) + .on('ajax:complete', function() { start() }) .trigger('click'); }); @@ -131,39 +131,39 @@ asyncTest('changing a select option with data-remote attribute', 5, function() { ); $('select[data-remote]') - .bind('ajax:success', function(e, data, status, xhr) { + .on('ajax:success', function(e, data, status, xhr) { App.assertCallbackInvoked('ajax:success'); App.assertRequestPath(data, '/echo'); equal(data.params.user_data, 'optionValue2', 'ajax arguments should have key term with right value'); equal(data.params.data1, 'value1', 'ajax arguments should have key data1 with right value'); App.assertGetRequest(data); }) - .bind('ajax:complete', function() { start() }) + .on('ajax:complete', function() { start() }) .val('optionValue2') .trigger('change'); }); asyncTest('submitting form with data-remote attribute', 4, function() { $('form[data-remote]') - .bind('ajax:success', function(e, data, status, xhr) { + .on('ajax:success', function(e, data, status, xhr) { App.assertCallbackInvoked('ajax:success'); App.assertRequestPath(data, '/echo'); equal(data.params.user_name, 'john', 'ajax arguments should have key user_name with right value'); App.assertPostRequest(data); }) - .bind('ajax:complete', function() { start() }) + .on('ajax:complete', function() { start() }) .trigger('submit'); }); asyncTest('submitting form with data-remote attribute should include inputs in a fieldset only once', 3, function() { $('form[data-remote]') .append('
') - .bind('ajax:success', function(e, data, status, xhr) { + .on('ajax:success', function(e, data, status, xhr) { App.assertCallbackInvoked('ajax:success'); equal(data.params.items.length, 1, 'ajax arguments should only have the item once') App.assertPostRequest(data); }) - .bind('ajax:complete', function() { + .on('ajax:complete', function() { $('form[data-remote], fieldset').remove() start() }) @@ -175,27 +175,27 @@ asyncTest('submitting form with data-remote attribute submits input with matchin .append($('')); $('form[data-remote]') - .bind('ajax:success', function(e, data, status, xhr) { + .on('ajax:success', function(e, data, status, xhr) { App.assertCallbackInvoked('ajax:success'); App.assertRequestPath(data, '/echo'); equal(data.params.user_name, 'john', 'ajax arguments should have key user_name with right value'); equal(data.params.user_data, 'value1', 'ajax arguments should have key user_data with right value'); App.assertPostRequest(data); }) - .bind('ajax:complete', function() { start() }) + .on('ajax:complete', function() { start() }) .trigger('submit'); }); asyncTest('submitting form with data-remote attribute by clicking button with matching [form] attribute', 5, function() { $('form[data-remote]') - .bind('ajax:success', function(e, data, status, xhr) { + .on('ajax:success', function(e, data, status, xhr) { App.assertCallbackInvoked('ajax:success'); App.assertRequestPath(data, '/echo'); equal(data.params.user_name, 'john', 'ajax arguments should have key user_name with right value'); equal(data.params.user_data, 'value2', 'ajax arguments should have key user_data with right value'); App.assertPostRequest(data); }) - .bind('ajax:complete', function() { start() }); + .on('ajax:complete', function() { start() }); $('